> ## Documentation Index
> Fetch the complete documentation index at: https://docs.decimal.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> API key authentication, scopes, permissions, rotation, and revocation.

All requests to the DecimalAI API authenticate with an API key. Pass it as a standard `Authorization: Bearer <key>` header.

```bash theme={null}
curl https://api.decimal.ai/api/v1/traces \
  -H "Authorization: Bearer dai_sk_..."
```

<Note>
  Every key starts with the prefix `dai_sk_`. The dashboard's session cookie auth (`/api/v1/auth/me`, `/api/v1/auth/verify`) is for the web UI only — programmatic access always uses API keys.
</Note>

## Key scopes

Scope controls **what data** a key can see. It's set at creation time and is immutable.

| Scope       | What it sees                             | When to use                                                                                             |
| ----------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `global`    | Everything in the org                    | SDK init for an app that spans multiple workspaces; the only scope that can create or revoke other keys |
| `workspace` | One workspace (specified at create time) | CI key for one team's repo                                                                              |
| `project`   | One project under a workspace            | Per-environment keys (dev / staging / prod)                                                             |

A key cannot escalate to a wider scope. To widen access, create a new key.

## Key permissions

Permissions control **what actions** a key can take, orthogonal to scope. Default is `admin` for backwards compatibility.

| Permission | Allowed methods                                             |
| ---------- | ----------------------------------------------------------- |
| `read`     | `GET` only                                                  |
| `write`    | `GET` + `POST` / `PUT` / `PATCH` (ingest, register, attach) |
| `admin`    | All of the above + `DELETE` / destructive ops               |

A CI key that only ingests traces should be `write`. An SRE read-only on-call key should be `read`. Use `admin` only for keys that need to delete data.

## Expiration

Keys can optionally expire. Pass an ISO-8601 `expires_at` when creating; omit for a non-expiring key (the historical default).

Once expired, requests return `401` with the response body `{"detail": "API key has expired"}` — a distinct detail string from the revoked-key case (also `401`, `{"detail": "API key has been revoked"}`), so callers can prompt regeneration without a generic "auth failed" branch. Both are authentication failures, so they share the `401` status with a missing or malformed key; the `detail` string is what distinguishes them.

## Creating a key

### From the dashboard

1. Sign in to the [DecimalAI Dashboard](https://app.decimal.ai)
2. Go to **Settings → API Keys**
3. Click **Create API Key**, choose scope, permissions, and optional expiry
4. Copy the key — it's shown **only once** and stored as a SHA-256 hash on the server

### Programmatically

Requires an existing `global`-scoped key with `admin` role (only global keys can mint other keys):

```bash theme={null}
curl -X POST https://api.decimal.ai/api/v1/api-keys \
  -H "Authorization: Bearer dai_sk_<your_global_admin_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "ci-staging",
    "scope": "workspace",
    "workspace_id": "ws_abc123",
    "permissions": "write",
    "expires_at": "2027-01-01T00:00:00Z"
  }'
```

Response (the raw key is returned **once**; the server keeps only the hash):

```json theme={null}
{
  "id": "key_abc123",
  "raw_key": "dai_sk_...",
  "key_prefix": "dai_sk_xxxxxxxx...",
  "label": "ci-staging",
  "scope": "workspace",
  "permissions": "write",
  "expires_at": "2027-01-01T00:00:00Z",
  "created_at": "2026-05-28T18:00:00Z"
}
```

<Note>
  A security-alert email goes to the org's billing contact when a new key is created. If you didn't expect it, revoke immediately.
</Note>

## Listing keys

```bash theme={null}
curl https://api.decimal.ai/api/v1/api-keys \
  -H "Authorization: Bearer dai_sk_..."
```

Returns prefix, label, scope, permissions, `created_at`, and `last_used_at` for each key. Raw keys are never returned. Only global-scoped keys can list.

`last_used_at` is updated as a fire-and-forget UPDATE on every successful auth — use it to surface stale-but-not-revoked keys for cleanup.

## Rotating a key

There's no in-place rotation. To rotate:

<Steps>
  <Step title="Create a new key">
    Mint a new key with the same scope and permissions as the one you're replacing.
  </Step>

  <Step title="Roll it out">
    Deploy the new key to your callers (env vars, secret manager) and let them pick it up.
  </Step>

  <Step title="Revoke the old key">
    Revoke the old key once `last_used_at` on it stops advancing.
  </Step>
</Steps>

The last step is the only way to know rotation is complete — if `last_used_at` is still climbing, something still has the old key.

## Revoking a key

```bash theme={null}
curl -X DELETE https://api.decimal.ai/api/v1/api-keys/{key_id} \
  -H "Authorization: Bearer dai_sk_<your_global_admin_key>"
```

Revocation is immediate — in-flight requests authenticated with the revoked key will fail on the next handler invocation. Only global-scoped keys can revoke. A revoked key is soft-deleted (the row stays for audit); re-creation requires a new `POST`.

## Security checklist

* **Never commit keys to source control.** Use environment variables or a secret manager.
* **Use the narrowest scope and permissions that work.** A CI key that only sends traces should be `workspace`/`write`, not `global`/`admin`.
* **Set `expires_at` on keys handed to humans.** Long-lived keys without expiry should belong to automated systems with rotation procedures.
* **Audit `last_used_at` quarterly.** Revoke anything that hasn't been used in 90 days.
* **Lost a key?** Revoke first, regenerate second. The new key won't have the old key's `id` — update any infra that pins to it.
