# Scoped API Keys

Every partner API key carries scopes granted at creation, and PII (personally identifiable information) endpoints require an ephemeral user token on top of the key. This page explains why, what each scope unlocks, and the minimal set you actually need per integration.

## Why scoped keys exist

Sail holds PII (personally identifiable information) and ACH deposit credentials, so a single leaked key shouldn't expose your entire user base. Issue a narrow key per workload instead of one key with every scope, your expense-sync worker's key doesn't need to be able to read account numbers. Here's what each scope unlocks:

| Scope | Unlocks |
| :--- | :--- |
| `expenses` | Reading expenses, creating the connections that produce them |
| `benefit_account` | Creating HSA/FSA connections, reading account and balance data |
| `account_numbers` | Reading masked and revealing full deposit numbers |
| `identity` | Reading originated and administrator-reported identity (PII) |
| `ingest` | Pushing your own transaction data to an external connection |
| `token_admin` | Minting and revoking ephemeral user tokens |

## `token_admin` is exclusive

A live key holding `token_admin` can hold no other scope. That's not an accident, the minting key never touches data itself, and the keys that do use tokens can never mint their own. Reaching a PII endpoint always needs two separate credentials working together, a data-scoped key plus a token minted by a different, `token_admin`-only key.

## Ephemeral user tokens

PII endpoints ([originated identity](/reference/identity/get-users-user-id-identity), [administrator-reported identity](/reference/identity/get-users-user-id-accounts-account-id-identity), and [account numbers](/reference/accounts/get-users-user-id-accounts-account-id-numbers)) need an `x-sail-user-token` header alongside your partner key. Mint one just-in-time through [Mint Ephemeral User Token](/reference/users/post-users-user-id-tokens). It expires after `ttl_seconds` (900 by default, 3,600 at most) and is never stored. Mint it, use it, discard it. Every mint is audit-logged and rate-limited per user.

You can narrow a token below your key's own scopes at mint time (`identity`, `numbers`, `numbers:reveal`), so a service that only displays masked numbers never holds a token that could reveal a full one. If you need to cut off every outstanding token for a user immediately, for example during an incident, [Revoke All User Tokens](/reference/users/delete-users-user-id-tokens) does that in one call.

## Three reasons authorization can fail

A request can fail authorization for three different reasons, and Sail's error codes tell you which one applies. `403 insufficient_key_scope` means your key doesn't carry the scope this call needs, `403 insufficient_token_scope` means your user token doesn't, and `403 product_not_enabled` means the scope and token are fine, but this data source's connection doesn't have that capability turned on. See [Authentication](/docs/api-reference/authentication) for the full endpoint-by-endpoint matrix of what each call requires.

## Minimal scopes per integration

Requesting every scope is easier upfront but works against the point of scoping keys at all. Here's the minimal set each integration path actually needs.

| Integration | Scopes |
| :--- | :--- |
| Expenses (card/store connections) | `expenses` |
| Classification (pushing your own transactions) | `expenses`, `ingest` |
| Account connection (HSA/FSA) | `benefit_account`, `account_numbers`, `identity` |

## Next steps

* To request your first key, see [Get an API Key](/docs/get-started/get-started-overview/api-key).
* To look up the full endpoint-by-endpoint authorization matrix, see [Authentication](/docs/api-reference/authentication).