InstaView uses scope-based permissions to provide granular access control. Each API key has a set of scopes that determine which operations it can perform on which resources.
| Scope | Permissions | Operations | |-------|-------------|------------| |
read:companies | View company | GET /companies/{id} | | write:companies
| Update company | PATCH /companies/{id} | | - | No delete | Companies
cannot be deleted via API | Note: Regular API keys can only access their
own company. ATS keys can access multiple companies.
Scope
Permissions
Operations
read:billing
View billing info
GET /billing/usage
-
No write
Billing changes must be made via dashboard
-
No delete
Billing data cannot be deleted
Read-only resource for monitoring usage and subscription status.
Scope
Permissions
Operations
read:webhooks
View webhooks
GET /webhooks, GET /webhooks/{id}
write:webhooks
Create/update webhooks
POST /webhooks, PATCH /webhooks/{id}, DELETE /webhooks/{id}, POST /webhooks/{id}/test, POST /webhooks/{id}/reset-circuit
-
No delete scope
Deletion uses write:webhooks scope
Common Combinations:
Copy
["read:webhooks"] // View webhook configurations["read:webhooks", "write:webhooks"] // Full webhook management
Reduces Attack Surface: Compromised keys have limited damage potential
Prevents Accidents: Applications can’t accidentally delete data they shouldn’t
Audit Trail: Scope requirements make it clear what each integration does
Compliance: Many regulations require least-privilege access
Example: Webhook Handler
A webhook handler that processes interview completion events: ❌ Too many
scopes: json ["read:interviews", "write:interviews", "read:candidates", "write:candidates"] ✅ Minimal scopes: json ["read:interviews", "read:candidates"] The webhook only needs to read interview data, not
create or modify it.
Example: Reporting Tool
A reporting tool that generates analytics:❌ Includes write scopes:
Inline resources (candidate, job, agent) are created automatically as part of
the parent operation and follow the same scope requirements. The XOR behavior
ensures you use either existing resource IDs or inline resource definitions,
not both.
// Key belongs to Company AAuthorization: Bearer sk_abc123def456ghi789// ✅ Can access Company A's jobsGET /jobs// ❌ Cannot access Company B's jobs (even if you know the ID)GET /jobs/company-b-job-uuid// Returns: 403 Forbidden
// ATS key with access to multiple companiesAuthorization: Bearer sk_ats123xyz456abc789// ✅ Must specify companyId for Company AGET /jobs?companyId=company-a-uuid// ✅ Can access Company B with different companyIdGET /jobs?companyId=company-b-uuid// ❌ Cannot access Company C (not managed by this ATS key)GET /jobs?companyId=company-c-uuid// Returns: 403 Forbidden - "Access denied to this company"
Instead of one key with all scopes, create multiple keys for different purposes:
Copy
// Separate keys for different integrationsconst analyticsKey = 'sk_live_analytics'; // read-onlyconst syncKey = 'sk_live_sync'; // write candidatesconst automationKey = 'sk_live_automation'; // write interviews
Document Required Scopes
Document which scopes each part of your application needs: ```javascript /** *
Candidate Sync Service * * Required Scopes: * - read:jobs (validate job
ownership) * - read:candidates (check for duplicates) * - write:candidates
(create/update candidates) */ class CandidateSyncService
Copy
</Accordion><Accordion title="Handle Permission Errors" icon="triangle-exclamation"> Gracefully handle insufficient permission errors: ```javascript async function createJob(jobData) { try { return await api.post('/jobs', jobData); } catch (error) { if (error.response?.status === 403) { console.error( `Permission denied: ${error.response.data.message}` ); // Notify admin or fallback to alternative workflow } throw error; } }
// Verify your API key by making a simple GET request// to any read endpoint you have access to, such as:GET /jobsAuthorization: Bearer sk_abc123def456ghi789// If successful, your API key is valid and has the required scope