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

# Authentication & Capability Scopes

> How agent access tokens authenticate and how the default-deny scope model gates every call.

## The credential

An **agent access token** is a scoped API key, distinct from the singular full-access **personal access token** your dashboard integrations use:

|                 | Personal access token     | Agent access token          |
| --------------- | ------------------------- | --------------------------- |
| Count           | one per user              | many per account            |
| Scopes          | full access               | explicit capability scopes  |
| Expiry / revoke | no                        | yes / yes                   |
| Intended for    | dashboard & quick scripts | coding agents / MCP clients |

Mint and manage agent keys in **Settings → API & Agents** (see the in-app Help Center for the UI walkthrough). Each key is owned by the admin who created it and **acts as that user** — role permissions still apply on top of scopes.

## How a call authenticates

* **Token** — sent as `Authorization: Bearer <key>` (the MCP server relays it to the API as the `api_access_token` header). Direct REST callers send `api_access_token: <key>` themselves.
* **Account** — fixed by the `X-Sapot-Account-Id` header (MCP) or the `/api/v1/accounts/:id/…` path (REST). A key is **bound to the one account it was minted in**, even if its owner belongs to several — a mismatched account id is refused.

## Capability scopes

| Scope                 | Grants                                                                   |
| --------------------- | ------------------------------------------------------------------------ |
| `conversations:read`  | list/search/filter/read conversations & messages                         |
| `conversations:write` | reply, assign, status, priority, labels                                  |
| `contacts:manage`     | find/create/update/label contacts; list labels                           |
| `reports:read`        | summaries, time series, live counts                                      |
| `knowledge:manage`    | curate AI Knowledge entries + profile                                    |
| `templates:manage`    | WhatsApp template CRUD + sync                                            |
| `flows:manage`        | operate flow agents (intent-level)                                       |
| `flows:publish`       | publish a flow agent                                                     |
| `campaigns:draft`     | prepare campaign drafts + preview audience                               |
| `automation:manage`   | **reserved** — in the catalog but has no live tools yet (default-denied) |
| `*`                   | all of the above; still bounded by the owner's role                      |

## Default-deny model

Authorization is an **allowlist**: each endpoint + action is paired with the scope that unlocks it. The rule is default-deny:

* An endpoint **not** in the allowlist is refused for agent tokens — *even with `*`*. (`*` grants every mapped scope, not a bypass.)
* An endpoint in the allowlist but whose scope you weren't granted is refused with `missing the required scope: <scope>`.

<Tip>
  Both refusals are **expected behavior, not bugs**. Name the missing scope and stop — don't blind-retry. If the capability should be available, add the scope to a new key (you can't widen a key in place).
</Tip>

## Account binding + role checks

Two layers apply, in order:

1. **Scope allowlist** — is this endpoint reachable with this key's scopes, in this account?
2. **Role** — the owning user's role still gates the action. A key can't do what its owner couldn't do in the dashboard (e.g. admin-only actions need an admin owner).

So `*` is "everything this user may do, through the mapped surface" — never more than the human behind the key.

## Security guidance

* **Least privilege** — grant only the scopes an integration needs; one key per integration.
* **Short expiries** — set an expiry; rotate by revoking and minting a new key.
* **Revoke on suspicion** — instant, and affects only that key, never your login or other keys.
* The plaintext token is shown **once** on creation — store it like a password.
