SalesOS

Authentication

API keys, OAuth 2.0 access tokens, scopes, organization binding, expiry, rotation and IP allow-lists.

SalesOS accepts three credentials:

CredentialHeaderUse it for
API keyX-API-Key: sk_... or Authorization: ApiKey sk_...Integrations, scripts, back-office jobs
OAuth 2.0 access tokenAuthorization: Bearer ...Apps that act on behalf of other people or organizations. See OAuth 2.0
Session token (JWT)Authorization: Bearer ...The SalesOS web app. Not intended for integrations

A request that presents an API key is authenticated by the key alone. A bad key is rejected and never falls back to a browser session.

What a key can do

A key acts as the user who created it, in the organization it was created in:

  • It never has more access than that user: same role, same record visibility (own / team / all), same Profile and field-level security, same plan entitlements.
  • If the owner is deactivated or leaves the organization, the key stops working (401 API_KEY_OWNER_INACTIVE / API_KEY_OWNER_NOT_IN_ORG).
  • If its organization is suspended, the key stops working (401 ORGANIZATION_SUSPENDED).
  • Keys can never call /auth, /api-keys, /admin, /scim, /oauth, /oauth-apps, /connected-apps, /developer-orgs, /apps, /marketplace, /page-builder or /named-credentials (403 API_KEY_SCOPE_BLOCKED), so a leaked key can't mint sessions, more keys or OAuth clients, install apps, or repoint an outbound credential. Use a signed-in session for those.

Scopes

Scopes are granted per resource. READ_<RESOURCE> covers GET; WRITE_<RESOURCE> covers every method and implies read.

ResourcePathsScopes
Leads/v1/leadsREAD_LEADS, WRITE_LEADS
Contacts/v1/contactsREAD_CONTACTS, WRITE_CONTACTS
Accounts/v1/accounts, /v1/companiesREAD_ACCOUNTS, WRITE_ACCOUNTS
Opportunities/v1/opportunities, /v1/dealsREAD_OPPORTUNITIES, WRITE_OPPORTUNITIES
Products/v1/productsREAD_PRODUCTS, WRITE_PRODUCTS
Quotes/v1/quotesREAD_QUOTES, WRITE_QUOTES
Orders/v1/ordersREAD_ORDERS, WRITE_ORDERS
Tasks/v1/tasksREAD_TASKS, WRITE_TASKS
Activities/v1/activitiesREAD_ACTIVITIES, WRITE_ACTIVITIES
Cases/v1/casesREAD_CASES, WRITE_CASES
Custom objects/v1/custom-objectsREAD_CUSTOM_OBJECTS, WRITE_CUSTOM_OBJECTS
Custom fields/v1/custom-fieldsREAD_CUSTOM_FIELDS, WRITE_CUSTOM_FIELDS
Webhooks/v1/webhooksREAD_WEBHOOKS, WRITE_WEBHOOKS (or the older WEBHOOKS)
GraphQL/v1/graphqlthe READ_ / WRITE_ scope of each entity an operation touches
Bulk API/v1/bulkthe scope of the job's object (WRITE_ to load, READ_ to export)
Running flows/v1/flows, /v1/flows/interviews…the scope of each record step
Anything else*

Action scopes open one capability each, rather than a resource:

ScopeOpens
INVOKE_FUNCTIONSPOST /v1/functions/{id}/invoke
MANAGE_METADATAevery /v1/metadata route (retrieve, deploy, tests)
PUBLISH_EVENTSPOST /v1/events/custom/{apiName}; implies READ_EVENTS
READ_EVENTSGET /v1/events/org-catalog and the GraphQL customEvents subscription

Action scopes never raise the owner's role: the routes behind them still need the owner to be an org owner or admin where they say so.

Each operation in the reference lists the scope it needs (x-api-key-scopes in the OpenAPI document). A missing scope is 403 API_KEY_SCOPE_INSUFFICIENT. OAuth access tokens use the same scopes and rules (without * or the legacy scopes); a miss there is 403 OAUTH_SCOPE_INSUFFICIENT.

Keys created before per-resource scopes existed may carry the legacy read / write scopes. These cover only the original resources (leads through activities and webhooks). Orders, cases, custom objects and custom fields always need an explicit scope.

Expiry

Keys expire on the date you set at creation (default 90 days; "never" is allowed but discouraged). An expired key returns 401 API_KEY_EXPIRED. Settings → API Keys flags keys expiring within 7 days.

Rotation

Rotate a key without downtime:

  1. In Settings → API Keys, choose Rotate on the key, or call POST /v1/api-keys/{id}/rotate with {"overlapHours": 24}.
  2. SalesOS issues a new secret with the same settings (scopes, rate limit, IP allow-list, expiry). The response contains it once.
  3. The old secret keeps working for the overlap (0–168 hours, default 24), then expires on its own. During the overlap both secrets work.
  4. Deploy the new secret. To stop the old one early, revoke it.

After the overlap the old secret returns 401 API_KEY_EXPIRED, with a message saying it was rotated. POST /v1/api-keys/{id}/regenerate (swap with no overlap) is deprecated in favour of rotation.

Every create, rotate, revoke and delete is written to the audit log with the actor, IP address and user agent.

IP allow-list

Restrict a key to the addresses your integration runs from. Each entry is an IPv4 or IPv6 address or a CIDR block:

203.0.113.0/24
198.51.100.7
2001:db8::/32

An empty list allows any address. A request from elsewhere gets 403 API_KEY_IP_NOT_ALLOWED. The address checked is the connecting client as seen by SalesOS's edge, not a client-supplied X-Forwarded-For.

Usage

Settings → API Keys shows, per key, the request count, when it was last used and from which IP, its rate limit, and any calls to deprecated endpoints.

On this page