How a request is checked
Each request passes four checks in order. The first one that fails answers, and nothing after it runs. The organization always comes from the token, so no organization or tenant header is ever sent. A record addressed in the path must also belong to the request’s project; a call or agent of another project answers404.
API tokens
A token is an 80-character secret sent as a bearer token:- CallVA stores only a hash of the token. The plain value is shown once, in the dialog that creates it; nobody can read it again, including CallVA.
- A token does not expire. The API Keys list shows when each key was last used, so a key nothing uses any more is easy to spot.
- Deleting a key in the API Keys list revokes it at once: its next request answers
401. - Creating and deleting keys is done by people in the dashboard, with the
api_tokens.createandapi_tokens.deletepermissions. The External API has no endpoint that creates or lists keys, so a token can never mint another token.
Organization-wide and project tokens
The project scope chosen at creation makes a token one of two kinds.Naming the project
An organization-wide token names the project of each request in one of these places, read in this order:- The
X-Project-IDheader. project_idin the JSON body - for writes (POST,PUT,PATCH).project_idin the query string - for reads and deletes.
An organization-wide token needs a project even for
GET /projects. To find a project’s id, open the project in the dashboard and choose Project Settings: the id sits under the page title.What a project token cannot do
A project token keeps only the project-level permissions of its role. Permissions that act on the whole organization - projects, members, roles, billing, organization settings, phone numbers and telephony providers - are dropped, even when the role is Admin. The one exception: every project token may read its own project’s phone numbers, which placing a call from a chosen number needs. So a project token with the Admin role still cannot create a project:403 Forbidden
Roles and permissions
A permission is a resource and an action, written<resource>.<action>: calls.read, agents.update, outbound_calls.create. A role is a named set of permissions. Every member of an organization and every API token gets its permissions from roles; a token carries exactly one role.
Built-in roles
Every organization starts with these roles. They are the organization’s own copies: they can be edited, renamed or deleted like any role you create.
There is no Owner role. The organization’s owner is a person, and holds every permission; a token is never an owner.
Every permission
All resources and their actions
All resources and their actions
Project permissions apply inside a project. Organization permissions apply to the whole organization and are dropped from project tokens.
Fields have no read permission of their own: reading field and field-group definitions is open to any role that holds
calls.read or any field or field-group permission. An agent’s presets and tools fall under agents: reading them is agents.read, changing them is agents.update, and cloning an agent is agents.create.Custom roles
When no built-in role fits, make your own under Settings, Roles, New role: name it, then tick the permissions it holds, or give it full access.- Creating a role needs
roles.create. You can put into a role, or hand out through a key, only the permissions you hold yourself - unless you holdroles.manage(Grant any permissions). Only someone with full access can create or hand out a full-access role. - Editing a role changes what every member and key holding it may do, from their next request.
- A role still held by a member or an API key cannot be deleted; move them to another role, or delete the keys, first.
How each endpoint states its permission
Every page of the API reference opens with the permission the endpoint requires, such as “Requires permission:calls.read”. The same requirement is in the OpenAPI specification as the operation’s x-permission field - a string for a single permission, or a rule for the few endpoints whose requirement is worked out per request:
GET /projects, GET /projects/{id}) needs no permission beyond reaching the project: the rule is project_reach, and a token answers only for the projects it reaches.
A software agent can read x-permission from the specification to work out, before calling, which role an integration needs.
Why a request is refused
401 - missing or invalid token
401 - missing or invalid token
No A token CallVA does not know - mistyped, or deleted from the API Keys list:These two answers have no
Authorization header, or one that does not start with Bearer :401 Unauthorized
401 Unauthorized
success field; read the HTTP status.400 PROJECT_REQUIRED - organization-wide token without a project
400 PROJECT_REQUIRED - organization-wide token without a project
400 Bad Request
404 PROJECT_INVALID - a project the token cannot reach
404 PROJECT_INVALID - a project the token cannot reach
The same answer for a project that does not exist, one of another organization, and an inactive one, so the response never reveals which:
404 Not Found
403 insufficient_permission - the role lacks the permission
403 insufficient_permission - the role lacks the permission
A token with the Analyst role may read calls, but not agents:The refusal is whole: an endpoint never answers part of its result to a token that lacks its permission. To reach it, use a token whose role holds the permission the endpoint’s page names.
403 Forbidden
Give each token only what it needs
A token is a credential that works from anywhere until it is deleted. Keep what each one can do as small as its job.1
Pick or make a role that matches the job
List the endpoints the integration calls, and read the permission on each one’s page. If a built-in role holds those and little else, use it - Analyst for reporting, Support for working calls. Otherwise create a role with exactly those permissions. A dashboard that only reads results needs
calls.read and transcripts.read, not Admin.2
Prefer a project token
If the integration works in one project, create the key for that project. It cannot reach another project’s data, and it cannot touch organization-level settings, members, billing or phone numbers whatever its role holds.
3
One token per integration
Give each system, script and person its own key, named for it. The API Keys list then tells you what each key is for and when it was last used, and deleting one stops one integration only.
4
Rotate and revoke
To rotate a key, create a new one with the same role and scope, switch the integration over, confirm the new key’s Last Used At moves, then delete the old key. Delete a key at once if it may have leaked, and delete keys that are no longer used.
MCP connections use OAuth
AI agents that connect over MCP do not use API keys. When an MCP client is pointed athttps://mcp.callva.io/mcp, it opens a CallVA sign-in in your browser; you choose the organization and the project, and approve. The connection then acts as you, in that one project, and never beyond what your own role holds there. CallVA issues the MCP client its own OAuth tokens, which the External API does not accept, so nothing usable as an API key leaves the flow. Connections are listed, and can be revoked, under Settings, MCP Connections. See MCP for setup.