Authentication
Every request carries a bearer access token, obtained by exchanging a client
credential at POST /oauth/token.
A credential is a company, not a user
Section titled “A credential is a company, not a user”The credential identifies your company. It is not tied to a person, a login, a team or a selected workspace, and it does not inherit anybody’s permissions. Everything the token can read is scoped to the company it was issued for, and no parameter widens that.
This is why there is no user login flow: there is no user.
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.
Call GET /v1/context to see the scopes a token actually carries.
Lifetime and renewal
Section titled “Lifetime and renewal”Tokens expire after expires_in seconds (3600 by default).
There is no refresh token, by design. Client credentials 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.
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.
Rotating a secret
Section titled “Rotating a secret”Rotation 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.
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.