Skip to content

Security

Every read is filtered by the company on the access token. The company comes from the token claim, never from a request parameter, so there is no input that widens the scope of a credential.

Partner WhatsApp reads also require the session’s saved company to match the credential company. A shared seller or a session without company ownership cannot cross that boundary. Confirmed legacy companies keep the existing seller selection.

Resources are addressed by opaque, prefixed identifiers (mtg_, call_, usr_, tpl_, acc_, team_, tag_) that are allocated per company. Internal sequential ids are never exposed, so record volume cannot be inferred and ids cannot be enumerated. The only way to enumerate them is through the catalogs (/v1/accounts, /v1/teams, /v1/tags), which return a company’s own accounts, teams and tags and nothing else. An id from another company used as a filter yields an empty page, never an error that would confirm it exists.

An identifier your company does not own returns 404, never 403 — the API does not confirm that a resource exists elsewhere.

User credentials see what their owner sees

Section titled “User credentials see what their owner sees”

A user credential narrows company isolation; it never widens it. On every request the API works out, from the current state in Salesbud, what the owner can see, and serves only that.

ResourceCompany credentialUser credential
Meetings and calls, with transcripts, answers and evaluationsEvery record of the companyRecords of the owners the user can see through their teams, plus records shared with the user
Email conversationsEvery conversation of the companyOnly with email access in Salesbud; conversations of mailboxes the user can see, or all of them for a platform administrator
WhatsApp conversationsEvery conversation, both sourcesOnly the sources the user can use; conversations of sellers the user can see
/v1/teamsEvery teamTeams the user belongs to
/v1/tagsEvery tagAll for a platform administrator; otherwise tags with no team or group, tags of the user’s teams and tags of groups the user can reach
/v1/accountsEvery accountEvery account, as in Salesbud; with the ACCOUNTS feature off for the company, /v1/accounts is empty, accounts comes back as [] on meetings, calls, email and WhatsApp conversations, and filtering by account_id returns an empty page
team_id filterAny team of the companyA team the user is not in behaves as unknown: an empty page

A record outside that view answers 404, exactly like a record of another company, so a user credential cannot confirm that something exists. Losing email or WhatsApp access in Salesbud inside a token’s lifetime answers 403 INSUFFICIENT_SCOPE on those routes; a record that leaves the owner’s view simply stops appearing and answers 404.

The rules mirror the product, divergences included

Section titled “The rules mirror the product, divergences included”

“What a user can see” is not one rule in Salesbud, and the API copies each rule as it is instead of unifying them:

Meetings, calls, transcriptsEmail and WhatsAppTags
Owner’s roleEffective: a master in their default team counts as a memberAs assignedNot applicable: team and group membership only
Platform administratorNo shortcutSees everythingSees everything

The visible consequence: a master in their default team appears to that team’s admins in meetings, but not in email or WhatsApp, where no team member sees them, only a platform admin.

Administrators cannot request user credentials, so the administrator rows apply only when the owner becomes an administrator after the credential was approved.

  • It does not cross companies. A user who reaches several companies in Salesbud needs one credential per company, each approved by that company’s administrators.
  • Changes take up to 60 seconds. The view is cached for at most 60 seconds, so a team change, a new share or a removed permission reaches the API within that window. Revoking or disabling the credential is immediate.

Meetings and calls follow the owner’s default company at read time for direct, native partner and converted companies alike. Historical event metadata does not override ownership. Changing the default company moves the owner’s history to that company. Every read still requires a credential for the company and the existing visibility permissions.

A meeting keeps its template reference and historical answers when its owner’s default company changes. The template name is Template when that template’s current name is outside the credential’s company scope. The real name remains available for the meeting owner’s own templates, templates whose author has that company as their default, and templates explicitly shared with a team in that company. A secondary company membership alone does not expose the author’s template names.

Product access uses the company’s persisted features and the owner’s permissions for every account. The partner’s commercial offer does not revoke a feature already granted to the company. New tokens require the feature of the requesting app; issued tokens keep their normal lifetime, and credential revocation remains immediate. Owner visibility and permission changes follow the existing cache window of up to 60 seconds.

WhatsApp sessions with saved company ownership must match the credential company for every account. Baileys sessions without company ownership use the owner’s existing enabled legacy workspace, restricted to authorized sellers; legacy RD Conversas and Umbler sessions use their company-specific prefix. A shared seller does not grant access to another company’s explicit session.

transcriptions.read is a distinct scope from meetings.read and calls.read, and a credential can hold the collection scopes without it. A transcript is the most sensitive payload the API serves — verbatim customer speech — so access to the metadata of a meeting does not imply access to what was said in it.

The gate composes rather than replacing the collection scope: a transcript request is checked against both, so a credential limited to calls cannot reach a meeting transcript by holding transcriptions.read.

Transcript text is sanitised before it leaves the service.

Every business read is recorded: which credential, which company, which route, which resource ids, and the calling identity.

The audit write is fail closed. If the trail cannot be written, the read is not returned — you get 503 AUDIT_UNAVAILABLE instead. A successful response therefore means the access was recorded.

  • Secrets are stored only as HMAC-SHA256 hashes with a service-side pepper. They cannot be read back, only rotated.
  • Rotating a company credential keeps the previous secret valid for seven days so you can deploy without downtime; revoke it once the new one is live. Rotating a user credential stops the previous secret at once.
  • On a suspected leak, revoke the client — that invalidates issued tokens immediately. Rotation does not invalidate tokens already issued, and for a company credential the old secret also stays valid.
  • Set an IP allowlist when your integration runs from fixed addresses. It is enforced at token issuance and on every request, so a leaked token is useless from elsewhere.
  • Prefer a user credential for anything personal, such as a script on your laptop: it reads only what you can see and dies with your membership. Keep company credentials for integrations that must read the whole company.

Stack traces, source paths, internal ids, tokens, secrets and request bodies are never returned in a response, and never written to logs.