> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.phonic.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.phonic.co/_mcp/server.

# API Keys

> Create API keys and limit each one to the projects and workspace actions it should reach.

API keys authenticate server-to-server requests. Create one on the
[API keys page](https://phonic.co/api-keys), or with `POST /v1/api_keys`.

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

```http
Authorization: Bearer <PHONIC_API_KEY>
```

Never put an API key in browser code. For a browser or mobile client, mint a
[session token](/docs/platform/authentication/session-tokens) 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`:

```json
{
  "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`.

```json
{
  "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:

```json
{
  "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.`