# API keys

Project-scoped credentials for the CLI, CI and the REST API: scopes, creation, usage, rotation, and how a key inherits its creator's permissions.

Canonical: https://comvi.io/docs/platform/api-keys

An API key authenticates automation: `comvi push` and `comvi pull`, CI pipelines, type
generation, and any server-to-server call against the [REST API](/docs/api). Loading
deployed translations into an app needs no key, because the CDN is unauthenticated.

`Terminal`

```bash
curl -H "Authorization: Bearer comvi_…" https://api.comvi.io/v1/project
```

Pass the key as a Bearer token in the `Authorization` header. `GET /v1/project` returns the
project the key belongs to and is the first call for any integration. The base path is
`/v1`. Bearer requests need no CSRF token.

## How a key is checked

On every request Comvi checks that the key exists and is active, has not expired, that its
creator's account is still active with access to the project, that the key's scopes still
fall inside the creator's **current** permissions, and that what remains covers the
permission the endpoint requires. A key never outgrows its creator: demote the creator and
the key's access shrinks on the next request; delete, block or remove them from the project
and the key stops working; if nothing survives the intersection, the request fails with
`401 API_KEY_PERMISSIONS_REVOKED`. History entries attribute actions to the key's name.

Keys are project-scoped and cannot reach organization-scoped routes. A route that accepts
either auth method refuses a key with `403 FORBIDDEN` and "API keys cannot access
organization-scoped routes"; a session-only route never reads the header and answers
`401 AUTHENTICATION_REQUIRED`. Managing members, organization settings or billing needs a
signed-in person. [Authentication](/docs/api/authentication#which-endpoints-accept-which)
lists the split per route.

## Scopes

| Scope | Grants |
|---|---|
| `project:read` | Read the project, its locales and its namespaces |
| `translations:read` | Load translations, pull and export data, read keys, comments, history, glossary and TM |
| `translations:write` | Create, update, import and edit keys, values, namespaces, locales, glossary entries and comments; delete a namespace; edit the project's name, description and AI context |
| `translations:review` | Mark translations reviewed |
| `translations:delete` | Soft-delete translation keys |
| `schema:read` | Read the type-generation schema |

`project:read` is added automatically to every key and does not appear in the dashboard's
scope list; the SDK, the CLI and the in-context editor all start from `GET /v1/project`.
At least one scope is required, and you can only grant what you hold: a request for a scope
your own permissions do not cover fails with `API_KEY_FORBIDDEN_PERMISSIONS`.

The create dialog offers presets:

| Preset | Scopes |
|---|---|
| Runtime SDK | `translations:read` |
| CLI pull | `translations:read` |
| CLI push/import | `translations:read`, `translations:write` |
| In-context editor | `translations:read`, `translations:write` |
| Type generation | `schema:read` |
| MCP automation | `translations:read`, `translations:write`, `translations:review` |

## Create a key

1. Open **Settings → API Keys** in the project sidebar.

2. Click **Create API Key** and name it after the consumer, such as
   `github-actions-release`. The name shows up in logs and in **Last Used**.

3. Pick a preset or tick the scopes you need.

4. Copy the key. It is shown once, and cannot be viewed again after the dialog closes. Keys
   start with `comvi_` so secret scanners can spot them.

The dashboard offers the six scopes as checkboxes and has no expiry field. To change what a key can do, create a
new one and delete the old one. The REST API accepts `expiresAt` on create and update, and an
expired key fails with `401 API_KEY_EXPIRED`.

## Use a key
**CLI**

`Terminal`

```bash
export COMVI_API_KEY=comvi_…
comvi pull
```

**GitHub Actions**

`.github/workflows/translations.yml`

```yaml
- run: npx --package @comvi/cli comvi pull
    env:
      COMVI_API_KEY: ${{ secrets.COMVI_API_KEY }}
```

**GitLab CI**

`.gitlab-ci.yml`

```yaml
sync-translations:
    script:
      - npx --package @comvi/cli comvi pull
    variables:
      COMVI_API_KEY: $COMVI_API_KEY
```

The CLI reads `COMVI_API_KEY` from the environment. The config file can also hold `apiKey`,
but the file then has to stay out of git, so prefer the environment variable and keep
`.comvirc.json` committable. See [CI/CD integration](/docs/platform/recipes/ci-cd-integration)
for complete pipelines.

## Rotate a key

1. Create a new key with the same scopes.

2. Update every consumer: CI secrets, deployed services, local `.env` files.

3. Trigger one job or request to confirm the new key works.

4. Delete the old key in the dashboard. It stops working immediately.

Rotate on a schedule, after someone leaves the team, or on a suspected leak. **Last Used**
on each key tells you whether anything still depends on it before you delete it.

> **A leaked key**
>
> Delete it immediately and create a replacement. Check **Last Used** and the project
> history for unexpected activity, and rotate any downstream credentials the leak may have
> exposed.

## Practices

- Keep keys in environment variables or a secrets manager; never commit them.
- One key per integration, with the narrowest preset that works. `translations:read` is
enough for pulls and the runtime SDK.
- Replace a departing colleague's keys. They keep working only as long as that account and
its permissions do.

## Troubleshooting

- **`401 Unauthorized`.** Check that the header is `Bearer ` followed by the whole token and
that the key is still listed under **Settings → API Keys**. Code
`API_KEY_PERMISSIONS_REVOKED` means the key is fine but its creator no longer holds any of
the permissions behind its scopes.
- **`403 Forbidden`.** The key lacks the scope the endpoint requires. Create a replacement
key with the missing scope. "API keys cannot access organization-scoped routes" means no
scope will help; that route needs a signed-in person.
- **The key is not listed.** Keys are project-scoped. Switch to the project it was created
in.

## Next steps

- [Roles & permissions](/docs/platform/roles-permissions): The permission catalog scopes map onto.
- [REST API authentication](/docs/api/authentication): Header format and error codes per endpoint.
- [CLI](/docs/cli): Configure the CLI with an API key.
- [CI/CD integration](/docs/platform/recipes/ci-cd-integration): Full pipeline examples.
