Authentication
Every request carries a bearer access token, obtained by exchanging a client
credential at POST /oauth/token, or by an AI assistant signing you in with OAuth.
Two kinds of credential
Section titled “Two kinds of credential”A credential is either a company credential or a user credential. Both use the same OAuth flow, the same scopes and the same endpoints; they differ in whose data the token reads.
| Company credential | User credential | |
|---|---|---|
| Belongs to | The company | One user of the company |
| Reads | Everything the company owns | What that user can see in Salesbud, nothing more |
| Created by | Someone who manages API credentials | Any active member who does not approve credentials, then approved by an administrator. An assistant signing you in with OAuth creates one too |
| Validity | Chosen at creation, may never expire | 30, 90, 180 or 365 days, counted from the approval. A connection made with OAuth has no fixed validity |
| Typical use | Server-to-server integrations, the shared MCP connector | A personal script, a local MCP client, Claude or ChatGPT signed in with OAuth |
Neither kind is tied to a login session, a selected workspace or a parameter: what a token reads is decided on the server from the credential, and no input widens it.
GET /v1/context says which kind a token carries in client.kind, and for a user credential who
owns it in client.owner. See Get the authenticated integration context.
Requesting a user credential
Section titled “Requesting a user credential”- A member of a company with API access requests a credential on the API credentials screen, choosing a name, the scopes and the validity. Only scopes the member can use in Salesbud are offered; email and WhatsApp scopes need the matching access there.
- The client ID and secret are shown once, at that moment. The credential is inert until
approved:
POST /oauth/tokenanswersinvalid_client. - An administrator approves or denies the request as it is. Scopes cannot be edited afterwards; a different set of scopes is a new request.
- Once approved, the same secret starts issuing tokens. Its validity of 30, 90, 180 or 365 days is counted from the approval, not from the request. A denied request is revoked.
Approvers do not get user credentials. When an administrator, or anyone allowed to manage API credentials, makes the same request, the credentials screen creates an active company credential instead, with no expiry unless they choose 30, 90, 180 or 365 days.
An AI assistant can also create a user credential for you by signing you in with OAuth instead of a copied secret. That connection follows the same approval and the same limits, except that an approver who connects gets an active user credential, not a company one. See Connect Claude or ChatGPT.
Salesbud‘s own apps, such as the AI collaborators, connect the same way with three
differences: the company needs the AI collaborators feature instead of API access, and without it
authorizing and renewing answer 403 AI_COLLABORATORS_DISABLED; the connection is active at once,
with no approval; and it is left out of the per-user limits below, which it does not count toward
either. It is listed on the API credentials screen like any other connection, and an administrator
can disable or revoke it.
Each user can hold 3 active and 1 pending user credential in each company; a disabled credential counts as active. User credentials do not count toward the 20 active credentials of the company.
When the owner leaves
Section titled “When the owner leaves”A user credential works only while its owner is an active member of the company. If the owner is
deactivated or removed, or no longer has a Salesbud login, POST /oauth/token answers invalid_client with
"error_description": "The credential has no active owner.", and a token already in hand stops
working within 60 seconds: reads answer 401 INVALID_ACCESS_TOKEN. The credential stays listed until an administrator revokes it.
A connection made with OAuth has no secret to present, so it fails differently: renewing it with the
refresh token answers invalid_grant, not invalid_client.
Getting a token
Section titled “Getting a token”curl -X POST https://api.salesbud.com.br/oauth/token \ -d 'grant_type=client_credentials' \ -d "client_id=$SALESBUD_CLIENT_ID" \ -d "client_secret=$SALESBUD_CLIENT_SECRET"The endpoint accepts only application/x-www-form-urlencoded. Credentials may
also be sent with HTTP Basic instead of in the body.
{ "access_token": "eyJhbGciOiJSUzI1NiIs...", "token_type": "Bearer", "expires_in": 3600, "scope": "meetings.read calls.read transcriptions.read"}Tokens are signed RS256 and carry the company, the granted scopes and the credential version.
Scopes
Section titled “Scopes”| Scope | Grants |
|---|---|
meetings.read | List and retrieve meetings, their template answers and their overall evaluations. |
calls.read | List and retrieve calls, their template answers and their overall evaluations. |
transcriptions.read | Read transcripts, alongside the scope of the collection the transcript belongs to. Requested separately because a transcript is the most sensitive payload the API serves. |
A transcript therefore needs two scopes: meetings.read and
transcriptions.read for a meeting transcript, calls.read and
transcriptions.read for a call transcript. transcriptions.read on its own
reads nothing.
A token receives the scopes attached to its credential. Requesting a route
outside them returns 403 INSUFFICIENT_SCOPE — that is the gate working, not a
missing route.
A user credential’s token carries the requested scopes minus the ones its owner cannot use in
Salesbud at issuance time. Asking for a scope the credential does not hold is still
refused with 400 invalid_scope. If the owner loses email access, the next token comes without the
emails.* scopes and the scope field of the token response says so. If the owner has lost the
permissions for every scope of the credential, the token still comes back, with an empty scope,
and every read answers 403 INSUFFICIENT_SCOPE until the permission returns. A connection made with
OAuth is stricter: when none of its scopes is available, the token request is refused with
400 invalid_scope instead of returning an empty scope.
Call GET /v1/context to see the scopes a token actually carries.
Lifetime and renewal
Section titled “Lifetime and renewal”At issuance, manual clients and third-party OAuth apps require API_ACCESS; first-party OAuth apps require AI_COLLABORATORS. For partner companies, the partner must also be active and the published offer must contain the feature required by that client. For these partner companies, the same feature is rechecked on every authenticated request: removing the required access rejects existing partner tokens with 403 API_ACCESS_DISABLED or 403 AI_COLLABORATORS_DISABLED, according to the client. Legacy token lifetimes are unchanged. A workspace header cannot switch the company bound to a credential.
Tokens expire after expires_in seconds (3600 by default).
Client credentials have no refresh token, by design. It is a
machine-to-machine grant with no user to re-consent, so
RFC 6749 §4.4.3 says not
to issue one — a refresh token would be a second long-lived secret with the same
power as the first. Renewal is calling /oauth/token again. Connections made by
signing in with OAuth are different: they get a refresh token, valid once and for
90 days, because there is a person behind them and no secret to call with again.
Cache the token slightly under expires_in, and also re-issue when a request
returns 401 INVALID_ACCESS_TOKEN. A credential can be revoked before the clock
runs out; a timer alone will not notice.
A connection made by signing in ends with POST /oauth/revoke
(RFC 7009): send its refresh token, or one of its access
tokens, with the app’s client_id. The whole connection is revoked, as on the API credentials
screen, and the answer is 200 with an empty body even for a token the server does not know. A
connection an administrator disabled stays disabled when its owner or the app revokes it: its
refresh tokens end, but only an administrator’s revocation moves it to revoked. See
/oauth/revoke.
Rotating a secret
Section titled “Rotating a secret”Rotating a company credential issues a new secret while the previous one keeps working for seven days, so you can deploy the new value without a window of failed requests.
Only the owner rotates a user credential. An administrator can disable or revoke it, but not rotate it. The seven days above apply to company credentials only: when the owner rotates a user credential, the previous secret stops issuing tokens at once, there is no overlap window, and the new secret gets a validity of 30, 90, 180 or 365 days counted from the rotation. Tokens already issued keep working until they expire.
A connection made with OAuth has no secret, so it cannot be rotated: asking for it is refused with
409 OAUTH_CLIENT_NOT_ROTATABLE. To cut it off, revoke it.
For a company credential, at most two secrets are valid at once. Revoke the expiring one before starting
another rotation, otherwise the second rotation is refused with
409 ROTATION_IN_PROGRESS.
Revoking or disabling the whole client invalidates issued tokens immediately — that is the lever to pull on a suspected leak, not rotation.
IP allowlists
Section titled “IP allowlists”A credential can carry an optional list of IPv4/IPv6 CIDRs. When set, it is enforced both when issuing a token and on every request that uses it, so a leaked token is useless from outside your network.