# API Tokens
URL: https://docs.autociso.io/developers/api-tokens/
Description: Create, store, rotate and revoke AutoCISO API tokens for CI pipelines, scripts and AI clients, and choose a safe expiry for each scope.

An API token is a long-lived bearer credential for a machine: a CI/CD pipeline, a nightly script or an AI client
connected over MCP. Tokens belong to an organisation, carry one or more scopes, and reach only the endpoints listed in
the [Access model](https://docs.autociso.io/developers/access-model/) — every other endpoint answers `403 FORBIDDEN`.

## Before you start

- You need the **Owner** or **Manager** [role](https://docs.autociso.io/getting-started/roles-and-permissions/). Only an **Owner** can grant
  `pii:read`, `audit_log:read`, `audit_log:verify`, `access_review:read` and `employee:read`; every other scope needs an Owner or Manager of
  the organisation itself. Other roles can view the token list but not create or revoke tokens. Tokens cannot be
  created or revoked from a support session.
- Decide which scopes the integration needs. The full list and what each grants is in
  [The eight scopes](https://docs.autociso.io/developers/access-model/#the-eight-scopes) and [Read scopes for platform data](https://docs.autociso.io/developers/access-model/#read-scopes-for-platform-data).
- Tokens are issued per organisation, and the organisation is bound into the token on the server. A token created in
  one organisation can never read another's data.

## Create a token

1. Open the user menu (your name at the bottom of the sidebar) and choose **API Tokens**
   ([screen reference](https://docs.autociso.io/console/settings/api-tokens/)).
2. Click **Create Token**.
3. Enter a name that identifies the integration, for example `ci-pipeline-sbom-upload`.
4. Select the scopes, or pick a preset (**Read everything** or **BI read-only**) and adjust. Use the search box to find a
   scope by name or id, tick a group's checkbox to select all its scopes, and read the selected count above the list.
   Scopes are grouped as **Supply chain**, **Supplier templates**, **MCP**, **Employee lifecycle**, **Assets and access**,
   **Risk**, **Suppliers**, **ISO and compliance**, **People**, **Audit trail** and **Sensitive**. A scope you may not
   grant is disabled with *Only an organisation Owner can grant this scope.* A token can hold at most 40 scopes.
5. Choose **Expires**: **Never**, **30 days**, **90 days**, **180 days**, **1 year** or **Custom date**. Some scopes
   require an expiry and cap how far out it can be — see [Expiry rules](#expiry-rules).
6. Click **Create Token**. If you selected **Employee identity (pii:read)**, confirm **Grant employee identity access?**.
7. Copy the value from the **Token Created** dialog and click **I have copied the token**. It is shown only once.

A token looks like `aci_<64 hex chars>_<64 hex chars>` and always begins with `aci_`. AutoCISO stores only a SHA-256
hash of it; nobody, including support, can recover the value later.

## Use the token

Send it as a bearer token in the `Authorization` header of every request:

```http
Authorization: Bearer aci_your_token_here
```

REST endpoints live under `https://api.autociso.io/api/v1`; MCP is `https://api.autociso.io/mcp`. Start with the
[REST quickstart](https://docs.autociso.io/api/quickstart/) or the [MCP quickstart](https://docs.autociso.io/developers/mcp/quickstart/).

## Expiry rules

| Token carries | Expiry |
|---|---|
| Only `sbom:*`, `mcp:*` or `supplier_template:read` | Optional — without one the token works until revoked |
| `supplier_template:write` | Required |
| `jml:read`, or any of `asset:read`, `risk:read`, `supplier:read`, `iso:read`, `compliance:read`, `evidence:read`, `access_review:read` | Required, at most **365 days** |
| `employee:read`, `audit_log:read` or `audit_log:verify` | Required, at most **180 days** |
| `pii:read` (alone or with any other scope) | Required, at most **90 days** |

The lowest ceiling wins: a token holding `audit_log:read` and `pii:read` must expire within 90 days.

### Scope for audit trail integrity

| Scope | Who may grant it | Longest expiry | Plan | What it allows |
|-------|------------------|----------------|------|----------------|
| `audit_log:verify` | **Owner only** | 180 days | The Growth and above (Audit Trail) | Run [the audit chain integrity check](https://docs.autociso.io/api/reference/audit-trail/verify/). Part of the **Read everything** preset. |

It reads the integrity of the audit chain only, not the entries. It implies no other scope and no other scope implies it.

When a scope needs an expiry, the console disables **Never**, disables the options that are longer than the ceiling, and
preselects **90 days**. A missing or too-long expiry is rejected with `422 EXPIRY_REQUIRED` or `422 EXPIRY_TOO_LONG`.
Where your [plan](https://docs.autociso.io/getting-started/plans-and-features/) does not include Employee lifecycle, the dialog warns that `jml:read` calls will return `402` until you
upgrade.

**Warning — Employee identity (pii:read) is a deliberate grant**

Selecting **Employee identity (pii:read)** shows *This token can reveal employee names and emails.* and asks you to
confirm **Grant employee identity access?**. Only an Owner can grant it (`403 OWNER_REQUIRED` otherwise); where the
capability is not enabled for your deployment the option is disabled and the API answers `422 SCOPE_UNAVAILABLE`.
Tokens carrying it show a **PII** tag in the list. See [Employee identity](https://docs.autociso.io/developers/employee-identity/).

## Handle tokens safely

- **Header only.** Send the token only in the `Authorization: Bearer` header — never in a query string, a URL, a log
  line, a chat message or a ticket.
- **Server-side only.** Tokens are server-side credentials. Do not embed one in a browser page, a mobile app or a
  client-side script.
- **One token per integration.** A leaked token then costs you one integration, not all of them, and revocation is
  surgical.
- **One token per client tenant.** If you are an MSP or consultant serving several client organisations, each client
  creates its own token and you store one token per client, in separate secrets. There is no cross-tenant token.
- **Store it in a secrets manager** (GitHub Actions secrets, a cloud KMS, Vault). Never commit it to source control.
- **Prefer 90 days**, and rotate on that schedule — immediately when the person who set up the integration leaves.
- **Personal data has a retention.** Names and emails returned to a `pii:read` token, and the pseudonymised data from
  `jml:read`, are personal data. Keep only what the integration needs, under your own retention rules, and delete it
  when you no longer need it.

## Rotating a token

Rotation is create-then-revoke, in that order, so the integration never loses access:

1. Create the replacement with the same scopes (for `pii:read`, as an Owner, with a fresh expiry of at most 90 days).
2. Install the new value in the integration's secret store and confirm a call succeeds.
3. Revoke the old token.

The token list shows **Expires in N days** in the status column once a token is within 14 days of expiry. A
`pii:read` token needs rotating every quarter at the latest.

## Revoke a token

On **API Tokens**, click **Revoke** on the token's row and confirm. Revocation takes effect immediately. Revoke a
token when its integration is decommissioned, and at once if it may have leaked — then create a replacement.

## Why a token was refused

Creating a token can fail with a stable code. Match on the code, not the message.

| Code | Status | Meaning |
|---|---|---|
| `SCOPE_INVALID` | 400 | A scope is unknown, a wildcard (`asset:*`) or a preset name. The message names it. Send explicit scope names |
| `OWNER_REQUIRED` | 403 | The scope can be granted only by an Owner. The message names the scope |
| `INSUFFICIENT_ROLE` | 403 | Your role may not grant one of the scopes |
| `MEMBERSHIP_REQUIRED` | 403 | Your membership of the organisation is not recorded; ask an Owner to create the token |
| `SUPPORT_SESSION_NO_MINT` | 403 | Tokens cannot be created or revoked from a support session |
| `EXPIRY_REQUIRED`, `EXPIRY_TOO_LONG` | 422 | A scope needs an expiry, or the date is past its ceiling |
| `SCOPE_UNAVAILABLE` | 422 | `pii:read` is not enabled for the deployment |
| `FEATURE_GATED` | 402 | Your plan does not include the feature; `error.args.minTierName` names the lowest plan that does |

Once a token is in use, a token whose organisation is suspended is refused with `403 ORG_SUSPENDED` on every token-accessible surface
(supply chain, supplier templates, lifecycle, the MCP endpoint and the newer read endpoints once published), and a token whose creator lost the required role is suspended (the list shows
**suspendedAt** and **suspendedReason**) and then refused like a revoked one. Full list: [Errors & rate limits](https://docs.autociso.io/developers/errors-and-limits/#error-codes).

Token creation and revocation are recorded in the [audit trail](https://docs.autociso.io/console/settings/audit-trail/) (`api_token.created`,
`api_token.revoked`).

## Token management endpoints

These four endpoints are the one part of the public surface that an API token cannot reach. They require an
interactive user session (a JWT sent as `Authorization: Bearer <jwt>`) held by an Owner or Manager; an `aci_` token
receives `403 FORBIDDEN`. The base URL is `https://api.autociso.io/api/v1`.

| Operation | Endpoint | What it does |
|---|---|---|
| [List tokens](https://docs.autociso.io/api/reference/tokens/list/) | `GET /api/v1/tokens` | Lists every token issued for your organisation. Token values are never returned. |
| [Create a token](https://docs.autociso.io/api/reference/tokens/create/) | `POST /api/v1/tokens` | Mints a token. The body is `name`, `scopes` (1 to 40 explicit scope names) and `expiresAt` (required for the scopes in the table above). The plaintext `tokenValue` is returned once. An organisation can hold at most 20 tokens. |
| [List assignable scopes](https://docs.autociso.io/api/reference/token-scopes/list/) | `GET /api/v1/tokens/scopes` | Lists every scope you can put in a token, with its group, whether it needs an expiry (and the longest one), who may grant it, and whether its endpoints are live. The create-token dialog uses it. |
| [Revoke a token](https://docs.autociso.io/api/reference/tokens/revoke/) | `DELETE /api/v1/tokens/{tokenId}` | Revokes the token immediately. |

Creation and revocation are written to the audit trail as `api_token.created` and `api_token.revoked`. Everything else
in the [API reference](https://docs.autociso.io/api/reference/) is called with the token you create here.

## What tokens cannot do

- **Manage tokens.** `/api/v1/tokens` refuses API-token authentication; token management needs a signed-in user
  (see [Token management endpoints](#token-management-endpoints)).
- **Choose an organisation.** There is no `org_id` parameter and no `X-Org-ID` header for tokens.
- **Change their scopes.** Scopes are fixed at creation; rotate to a new token instead.
- **Approve or publish.** Human-only actions refuse every token — see
  [Human-only actions](https://docs.autociso.io/developers/access-model/#human-only-actions).

Each token's list entry also shows `lastUsedAt` and, once the token has been used, a daily `activity` summary (requests,
rows returned, distinct routes, last route).

Rate limits per token and per IP are listed in [Errors & rate limits](https://docs.autociso.io/developers/errors-and-limits/#rate-limits).
