---
title: API tokens
description: Mint, scope, and rotate the izri_ tokens the CLI, Action, and MCP server authenticate with.
order: 40
---

# API tokens

Every non-browser client — the CLI, the GitHub Action, the MCP server — authenticates with a bearer token.

## The `izri_` prefix is load-bearing

All tokens start with `izri_`. This is not cosmetic. The API short-circuits to bearer-token validation **only** when it sees that prefix; anything else falls through to cookie-session handling and produces an opaque `401` with no explanation.

If you get an unexplained 401 with a token you believe is valid, check the prefix before anything else. A truncated copy-paste that lost the prefix looks exactly like an expired token.

## Minting a token

From organization settings, create a token and select its scopes. **Copy it immediately** — it is shown once and stored hashed.

A token can hold a subset of your role's scopes, never more. See [organizations and roles](/docs/configure/organizations-and-roles) for the full role-to-scope matrix.

## Scoping

Grant the minimum the client needs.

| Client | Typical scopes |
| --- | --- |
| GitHub Action | `analysis:run`, `tests:run`, `tests:read`, `projects:read` |
| CLI (local dev) | `analysis:run`, `tests:run`, `tests:read`, `projects:read` |
| MCP server | `projects:read`, `tests:read`, `analysis:read` — add `analysis:run` to let the agent run scope checks and `tests:run` to let it trigger runs |
| Read-only dashboards | `projects:read`, `tests:read`, `analysis:read` |

A CI token does not need `tokens:write` or `org:write`. If a leaked CI token can mint more tokens, the blast radius of a leak is unbounded. Nor does it need `analysis:approve`: keep baseline approval with people, so a leaked CI token cannot wave its own visual changes through.

### What each write needs

Every change a token makes to a project needs the matching scope. A token without it gets `403` with the scope named, whoever minted it.

| Write | Scope |
| --- | --- |
| Trigger a test run, upload CI evidence (`izri run`, `izri upload`, `/api/ci/*`) | `tests:run` |
| Scope check, derive a scope config, re-analyze a PR, analyze a repository | `analysis:run` |
| Approve or revoke a visual baseline | `analysis:approve` |
| Project settings, quality policy, scope config, cost-tracking reset | `projects:write` |
| Delete a project | `projects:delete` |
| Inbound webhook configuration; outbound webhook subscriptions (create, update, rotate secret, delete, test) — the latter also need an OWNER or ADMIN creator | `webhooks:write` |

A token needs at least one scope; the API refuses an empty list.

## Using a token

```bash
export IZRI_API_TOKEN=izri_xxxxxxxxxxxx
export IZRI_API_URL=https://api.izri.ai
```

```yaml
- uses: legendify-dev/izri/apps/github-action@v1
  with:
    api-token: ${{ secrets.IZRI_API_TOKEN }}
```

Directly against the API:

```bash
curl -H "Authorization: Bearer izri_xxxxxxxxxxxx" \
     https://api.izri.ai/health
```

## Rotation

1. Mint the new token.
2. Update every consumer — CI secrets, local environments, MCP configs.
3. Revoke the old one.

In that order. Revoking first means a window where CI fails on every PR.

Tokens do not expire on their own. Rotate on a schedule you choose, and immediately whenever someone with token-minting rights leaves.

## Tokens follow their creator's role

A token's scopes are fixed when it's created, but its *write* access also depends on its creator's current role. Demote the creator to `VIEWER`, or remove them from the organization, and every token they minted stops writing (reads keep working). Promoting someone does not widen tokens they already minted — reissue them.

Changing a member's role or removing them from the organization also revokes every token they minted there, so after a role change the person mints fresh tokens with the scopes their new role allows. The creator-role check above is the backstop for anything that bypasses that.

A token can only hand out what it holds: minting or rotating with a token refuses any scope the calling token itself lacks, so `tokens:write` on its own never becomes `analysis:approve` or `projects:write`.

## Never commit a token

Use repository secrets in CI and your environment locally. A token in git history is compromised even after the commit is removed — rotate it rather than rewriting history.

## Related

- [Organizations and roles](/docs/configure/organizations-and-roles) — what scopes you can grant.
- [CLI](/docs/install/cli) — where the token goes.
