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

# Authentication

> Bearer API key with scopes. Create it in the console, send it from your server, rotate it with a grace period.

Every call sends the key in the `Authorization` header:

```http theme={null}
Authorization: Bearer sk_a1b2c3d4_<64 characters>
```

A key is `sk_`, 8 characters, an underscore and 64 characters. Sahl stores only a SHA-256 hash of it and a prefix (`sk_a1b2c3d4`) for the console list. The secret is shown once when the key is created or rotated, and nobody at Sahl can read it back.

## Create a key

1. Sign in to the console at [app.sahlfinancial.com](https://app.sahlfinancial.com) as a tenant admin or API manager, with a verified email.
2. **Settings**, then **API Keys**, then **New key**.
3. Give it a name, such as `production-backend` or `sandbox-test`.
4. Tick the scopes. Only the scopes your workspace may hold are offered.
5. Optionally bind it to a Google service account (below).
6. **Create API Key**. Copy the secret from the banner. It is shown once.

If the form shows "This workspace is not enabled for the partner KYC API, so its keys carry no scopes", [request sandbox access](https://sahlfinancial.com/contact?type=demo) to have the partner API enabled on your workspace (it is switched on per workspace by Sahl). A request to create a key with a `kyc:` scope in a workspace that is not enabled returns 403 `kyc_scope_not_allowed`.

There is no separate sandbox key and production key. One key works in both environments. The `environment` field on each call decides. If you want different keys for different systems, name and scope them that way.

## Scopes

| Scope | Allows |
| - | - |
| `kyc:extract` | `POST /v1/kyc/extract` |
| `kyc:verify` | `POST /v1/kyc/verify`, `POST /v1/kyc/assess` |
| `kyc:eid` | `POST /v1/kyc/eid`, `GET /v1/kyc/eid/{key}`, `GET /v1/kyc/eid/{key}/report` |

* These three are the only scopes. A scope outside the list is refused when you create the key.
* A key only carries the scopes it was issued with, so a key leaked for one purpose cannot be used for another.
* Every `kyc:` call also needs the workspace to be enabled for the partner API. A key that holds the scope on a workspace that is not enabled gets 403 `kyc_scope_not_allowed`.
* Give each system the least it needs. A server that only verifies profiles needs `kyc:verify` and not `kyc:extract`, which is the scope that spends reads.

## Rotate a key

Rotation issues a new key and keeps the old one working for a grace period, so you can deploy without downtime.

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant You as You in the console
    participant Old as Old key
    participant New as New key
    participant App as Your servers
    You->>Old: Rotate, choose a grace period
    Old-->>You: New secret (shown once)
    Note over Old,New: Both keys work during the grace period
    You->>App: Deploy the new secret
    Note over Old: Grace period ends, old key returns 401 API key expired
    App->>New: Calls continue
```

1. **Settings, API Keys**, find the key, click **Rotate**.
2. Choose how long the old key keeps working: none, 1 hour, 24 hours, 3 days or 7 days. The API accepts 0 to 168 hours, default 24.
3. Copy the new secret and deploy it.
4. When the grace period ends the old key returns 401 `API key expired`.

What the code guarantees:

* The new key has the same name, scopes and bound service account as the old one. Rotation cannot widen or narrow access.
* Rotating a key that is still in its grace period returns 409 `key_already_rotated`, so a retry cannot leave you with several live keys. Use the successor, or create a new key.
* A rotation is a fresh grant of the scopes. A workspace that has left the partner allowlist cannot mint new `kyc:` keys by rotating.
* The key list shows "Grace" with the stop time for a key in its grace period.

## Revoke a key

**Revoke** in the key list. A revoked key returns 401 `Invalid or revoked API key` from the next call. Revoke a key that may have leaked, then create a new one. Rotation with a grace period of none does the same in one step and gives you a replacement.

## Bind a key to a service account

If your servers run on Google Cloud, you can bind a key to the Google service account they run as. Every call must then carry a Google-signed identity token for that account in `X-Partner-Identity`, issued for the audience shown in the key form. A leaked key alone is useless.

| Rule | Value |
| - | - |
| Format | `name@project.iam.gserviceaccount.com`. A person's Google account is refused. |
| Several accounts | Separate by commas, for example staging and production. Up to 255 characters in all. |
| Missing or bad token | 401 `Partner identity could not be verified` |

The API playground cannot send this header, so do not bind a key you want to try there.

## Errors

| Status | `detail` | Cause |
| - | - | - |
| 401 | `Missing API key` | No `Authorization` header, an empty value, or a scheme other than `Bearer`. |
| 401 | `Invalid or revoked API key` | A wrong key, a malformed key, or a revoked key. |
| 401 | `API key expired` | A rotated key whose grace period ended. |
| 401 | `Partner identity could not be verified` | The key is bound and the identity token is missing, expired, for another audience, or for another account. |
| 403 | `API key lacks the kyc:verify scope` (string) | The key does not hold the scope for this endpoint. |
| 403 | `{"code": "kyc_scope_not_allowed", ...}` | The workspace is not enabled for the partner KYC API. |
| 403 | `{"code": "direct_access_refused", ...}` | You called Sahl's internal address. Call `https://app.sahlfinancial.com/api`. |

Fix a missing scope by creating a key that has it. The scope check runs before the workspace check, so a key without the scope gets the string form even if the workspace is not enabled.

## Keep keys safe

* Call the API from your server only. Never put a key in a browser, a mobile app or a repository.
* Keep it in a secret manager or an environment variable such as `SAHL_API_KEY`.
* One key per system, so you can revoke one without stopping the others.
* Watch **Developers, Call log** in the console: each key's calls, status and latency are listed, and the key list shows when each key last called the API.
* The docs playground sends the request from your own browser straight to the Sahl API. This site does not proxy it and does not store your key. Even so, paste a key made for testing, and revoke it when you are done.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.