Skip to navigation

API Keys

API keys authenticate server-to-server requests. Create one on the API keys page, or with POST /v1/api_keys.

The secret is returned once, when the key is created or rotated. Pass it as a bearer token:

Authorization: Bearer <PHONIC_API_KEY>

Never put an API key in browser code. For a browser or mobile client, mint a session token from your backend instead.

Scope

Every key has a scope. It decides which projects the key can use and whether it can manage the workspace.

Projects

project_access is all or selected.

  • all can use every project in the workspace, including projects created later.
  • selected can use only the projects listed in project_ids. An empty project_ids array can use no projects.

GET /v1/projects returns only the projects in the key’s list. A request that names a project outside that list, including a list of that project’s agents or conversations, fails with 403:

{
"error": {
"message": "API key does not have access to project \"customer-support\""
}
}

Workspace management

workspace_access: true lets the key:

  • read and update the workspace (GET and PATCH /v1/workspace)
  • create projects (POST /v1/projects)
  • create, update, rotate, and delete API keys

A key with workspace_access: false can still read and update the projects in its list, including their agents, tools, and conversations. Updating or deleting an existing project follows project access, not workspace access.

A key without workspace access receives 403 and API key does not have workspace access on the routes above.

Keys created in the dashboard start with access to every project and to workspace management. You can narrow that before saving the key.

Create a narrower key

POST /v1/api_keys accepts an optional scope. An API key can grant another key only a scope within its own. It cannot:

  • turn on workspace access it does not have
  • grant project_access: "all" when its own access is selected
  • include a project outside its own list

Those requests fail with 403 and API key cannot grant access beyond its own scope.

Omit scope when the caller is an API key and the new key copies that key’s scope. Omit it when the caller is a signed-in user and the new key can use every project and manage the workspace.

A project ID that is not in the workspace fails with 400. The response sets param_errors["scope.project_ids"] to Unknown project.

{
"name": "billing-key",
"scope": {
"workspace_access": false,
"project_access": "selected",
"project_ids": ["proj_ad0334f1-2487-4155-9df3-abd8129b29ad"]
}
}

The response includes the secret and the scope that was stored:

{
"id": "api_key_12cf6e88-c254-4d3e-a149-a7f1bdd22783",
"name": "billing-key",
"api_key": "ph_abc123def456...",
"scope": {
"workspace_access": false,
"project_access": "selected",
"project_ids": ["proj_ad0334f1-2487-4155-9df3-abd8129b29ad"]
}
}

Send project_ids only when project_access is selected. Duplicate IDs are removed.

Update, rotate, and delete

PATCH /v1/api_keys/{id} changes the name, the scope, or both. Send at least one of them. A new scope replaces the stored scope and must stay within the caller’s scope.

POST /v1/api_keys/{id}/rotate returns a new secret and invalidates the old one. The new key keeps the previous name and scope. The rotate response does not include scope.

DELETE /v1/api_keys/{id} deletes the key.

The caller can update, rotate, or delete a key only when that key’s current scope is within the caller’s scope. A limited key cannot rotate a wider key and receive its secret. That request fails with 403 and API key cannot manage an API key with access beyond its own scope.

If the key’s scope changes while the request is in progress, the call fails with 409 and API key scope changed. Retry the request.