inbox:read and inbox:write, and nothing else. An agency gives each client’s automation a key limited to that client’s brand, so a leaked key cannot reach another client.
An empty
scopes array grants every scope, and an empty brand_ids array grants every brand. A key is restricted when at least one of the two arrays holds a value.Create a scoped key
Passscopes, brand_ids, or both to POST /v1/keys:
raw_key is shown once. Creating keys needs full access (a full-access key, an OAuth access token, or the dashboard), so a scoped key can never create a key with more access than it has.
- An unknown scope returns
400 validation.scope_invalid, and the message names it. - A brand ID that does not exist, is not a UUID, or belongs to another SocialAPI account returns
400 validation.brand_invalid.
Scopes
Scopes have the formresource:action. A scope-restricted key can perform an operation only when it holds the operation’s scope.
GET /v1/keys/scopes returns this catalog with a group and a description per scope, so your app can render a scope picker from it:
Automations need their actions’ scopes
An Automation carries out its actions long after the request that configured it. A key can configure only actions it can perform itself: besidesautomations:write, it needs the scope of each action.
A key that holds only
automations:write and creates an Automation with a send_dm action gets 403 auth.insufficient_scope with meta.required_scope set to dms:send.
The check runs on every change that can make the Automation send more: creating it, resuming it, or changing its steps, post_ids, or once_per_person. Renaming, pausing, and deleting need only automations:write, so a key can always stop an Automation it can see.
Always available
Any valid key can call these endpoints, whatever its scopes and brands:GET /v1/users/meGET /v1/usageGET /v1/billing/plansGET /v1/media/storageGET /v1/posts/validateGET /v1/keys/scopesGET /v1/webhooks/eventsGET /v1/announcementsandPOST /v1/announcements/{id}/seen
Full-access operations
A restricted key can never perform these operations. They need full access: a full-access key, an OAuth access token, or the dashboard.- Managing API keys: list, create, edit, rotate, and revoke, including the key itself
- Editing or deleting your SocialAPI account (
PATCHandDELETE /v1/users/me) - Creating, editing, or deleting brands. Listing brands needs
accounts:read - Listing, creating, and revoking invites
Brand restrictions
A brand-restricted key behaves as if its brands are the only brands in your SocialAPI account. It never learns that other resources exist:- Reading an account, brand, or other resource outside the key’s brands returns the same
404as a resource that does not exist, for example404 account.not_foundor404 brand.not_found. - Lists (accounts, brands, posts, conversations, comments, mentions, Automations) return only resources inside the key’s brands.
GET /v1/brandsreturns only the key’s brands. An Automation belongs to the brand of its account. - Connecting an account needs a
brand_idfrom the key’s list. A connection withoutbrand_idcreates a new brand that the key cannot see. See Connect a social account.
Posts
A post belongs to the brands of its target accounts:- Read. A post is visible when at least one of its target accounts is in the key’s brands. A draft with no target is not visible to a brand-restricted key. A post the key cannot see returns
404 resource.not_found. - Change (edit, publish, retry, unpublish, delete). Every target account must be in the key’s brands. Otherwise the post returns
404 resource.not_found. - Create or retarget. Every account in the request must be in the key’s brands. Otherwise the request returns
404 account.not_found.
Media is shared
media:read and media:write work on the whole media library, whatever the key’s brands. A file in the library reaches a platform only when a post uses it, and creating or changing a post is limited to the key’s brands.
Webhooks and analytics need every brand
Webhook endpoints, their deliveries, the events log, and exports cover every brand of your SocialAPI account. A brand-restricted key is refusedwebhooks:manage and analytics:read operations even when it holds the scope, with 403 auth.insufficient_scope. A key restricted only by scopes can hold and use them.
Deleting a brand never widens a key
When you delete a brand that a key lists inbrand_ids, its ID stays in the array and matches nothing. The key does not become unrestricted, even when that brand was its only one.
Read a key’s restrictions
GET /v1/keys returns scopes and brand_ids on every key. Both arrays are always present, and two empty arrays mean full access:
Change the restrictions on a key
PATCH /v1/keys/{id} replaces the key’s name, scopes, and brand_ids together. The secret does not change, so services using the key keep working:
404 resource.not_found. To issue a new secret with the same restrictions, rotate the key: see Authentication.
Errors
A restricted key that is refused gets403 auth.insufficient_scope. meta.required_scope tells you what is missing:
- The key lacks the scope.
meta.required_scopenames the missing scope. - A
webhooks:manageoranalytics:readoperation with a brand-restricted key.meta.required_scopenames that scope. Brand-restricted keys are refused it even when they hold it. - A full-access operation.
meta.required_scopeis absent. - An operation no scope covers.
meta.required_scopeis absent.
404, never a 403. Full-access keys and OAuth 2.1 access tokens are never restricted.
Related
- Authentication for creating, rotating, and revoking keys
- Connect a social account for connecting accounts with a brand-restricted key
- Automations for the scopes each action needs
- Account permissions for limiting what a connected account can do, which differs from a key’s scopes
- The MCP server for giving an AI agent a scoped key
- Errors for every
auth.*code