Skip to main content
Every External API request carries an API token. The token says which organization the request belongs to, which project it may act in, and - through its role - which endpoints it may call. This page explains each of those checks, the answer you get when one fails, and how to issue tokens that hold no more than they need.

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 answers 404.

API tokens

A token is an 80-character secret sent as a bearer token:
Tokens are created in the dashboard, under the organization’s Settings, API Keys, Create API Key - the Quickstart walks through it. Three choices are made at creation and cannot be changed afterwards: the key’s name, its role, and its project scope.
  • 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.create and api_tokens.delete permissions. 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:
  1. The X-Project-ID header.
  2. project_id in the JSON body - for writes (POST, PUT, PATCH).
  3. project_id in the query string - for reads and deletes.
A project token ignores all three, so sending a header to a project token does no harm and changes nothing.
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

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 hold roles.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:
Reading projects (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

No Authorization header, or one that does not start with Bearer :
401 Unauthorized
A token CallVA does not know - mistyped, or deleted from the API Keys list:
401 Unauthorized
These two answers have no success field; read the HTTP status.
400 Bad Request
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
A token with the Analyst role may read calls, but not agents:
403 Forbidden
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.

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 at https://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.