Scopes and consent
The four checks every request passes: the connection scope, the action scope, the member's role, and whether the workspace has the module on.
A request passes four checks. A refusal tells you which one it failed, and they fail for different reasons, so it is worth knowing all four.
1. The connection scope
A token serves one resource, and each resource has one scope: the REST resources need api.access, MCP needs mcp.access. offline_access buys a refresh token.
Those three are the only *.access scopes that exist. There is no cpq.access or reports.access to ask for. A tenant module is not something a client requests — it is configured on the workspace and checked on the server.
2. The action scope
An endpoint’s scope is its action. GET /api/v1/objects/{objectId}/records needs records.read, because that is the action it performs. The reference names the scope for each endpoint.
Twenty-one actions exist:
objects.bootstrap objects.read objects.manage
records.read records.write
prices.read prices.manage
quotes.read quotes.write
reports.read reports.manage
documents.read documents.generate documents.manage
approvals.read approvals.submit approvals.decide approvals.manage
marketing.read marketing.manage
settings.manage
Ask only for the actions you use. A token carrying an action it never exercises is a larger grant than it needs.
marketing.read and marketing.manage are grantable but have no external endpoint — their only callers are session-only. Treat them as reserved.
3. The member’s role
The scope has to be in the token and the member’s role has to permit the action. A token holding records.write granted by a viewer still cannot write.
Only actions this API reaches are listed; marketing.read and marketing.manage belong to the role sets too but have no endpoint here.
| Role | Can |
|---|---|
owner, admin |
every action |
member |
read everything a viewer can, plus records.write, quotes.write, documents.generate, approvals.submit, approvals.decide |
viewer |
objects.read, records.read, prices.read, quotes.read, reports.read, documents.read, approvals.read |
4. The workspace’s modules
Many actions belong to an optional module, which the workspace enables or does not. The action fails if the module is off, whatever the token says.
| Module | Actions it owns |
|---|---|
custom_objects |
objects.manage |
cpq |
prices.read, prices.manage, quotes.read, quotes.write, settings.manage |
reports |
reports.read, reports.manage |
documents |
documents.read, documents.generate, documents.manage |
approvals |
approvals.read, approvals.submit, approvals.decide, approvals.manage |
| core | objects.read, objects.bootstrap, records.read, records.write |
The api module gates the REST resources themselves and mcp gates MCP. They are independent: a workspace can have one without the other.
Read GET /api/v1/capabilities to see what the caller may do and which modules are on, at the configuration revision both were read at. Poll it rather than inferring entitlements from a refusal.
When a module is turned off
Turning a module off refuses tokens that already exist, before they expire. The check runs again inside the transaction that does the work, so a request admitted a moment earlier may still finish while a later one is refused.
Four pairings worth knowing
The scope an endpoint needs is not always the one its name suggests.
GET /api/v1/capabilitiesreports which modules are on, but still needsobjects.read. You cannot discover your own entitlements without it.PUT /api/v1/settings/commercialneedssettings.manageand thecpqmodule.GET /api/v1/documents/{id},/retryand/downloadneeddocuments.generate, notdocuments.read. A read-only client cannot poll a generation it did not request.POST /api/v1/bootstrapand record reads and writes are core: no module, only the scope and the role.
Changing what a client may do
Update the client’s registered scopes and obtain fresh consent. A consent records the resources it covers, and a token names one of them, so a consent that no longer covers the resource stops issuing tokens for it. A token from before a scope change does not gain the new scope.