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

# Authentication

> API keys, scopes, and how to keep them safe.

The Instant Compliance API authenticates every request with a **bearer
API key**.

## Header format

```http theme={null}
Authorization: Bearer ic_live_<32+ characters>
```

The key prefix (`ic_live_`) is fixed so secret scanners (GitHub
Secret Scanning, GitGuardian, etc.) can recognise leaked keys
automatically.

## How keys are stored

We **never** store the plaintext key. At creation we keep only:

* The first 12 characters (the prefix, for the UI label).
* The last 4 characters (so admins can identify which key is which).
* A **SHA-256 hash** of the full plaintext.

When a request arrives we hash the incoming bearer token and look it up
by hash, with a constant-time comparison. The plaintext is shown exactly
**once** at creation and never retrievable.

<Warning>
  If you lose the plaintext key after closing the creation dialog, it is
  gone — there is no "show key" button. Revoke the key and issue a new
  one.
</Warning>

## Issuing a key

Open **Settings → Developers** in Instant Compliance and click **Create
key**. You must hold the `org.api-keys` permission (Administrators have
it by default).

You will be asked for:

* **Label** — a name only used in the admin dashboard. Pick something
  you will recognise six months later (e.g.
  `Zapier — HubSpot production`).
* **Scopes** — see below.
* **Expiry** *(optional)* — useful for short-lived integrations or
  one-off scripts.

## Scopes

Scopes are the public, integrator-facing surface granted to a key. Each
scope maps to a backing internal permission, so a key can never exceed
what the role model allows for human users.

| Scope             | Grants                                           |
| ----------------- | ------------------------------------------------ |
| `customers:write` | `POST /v1/customers`, `PATCH /v1/customers/{id}` |
| `customers:read`  | `GET /v1/customers`, `GET /v1/customers/{id}`    |
| `aml:read`        | `GET /v1/customers/{id}/aml`                     |

Grant the **minimum** scopes the integration needs. A reporting-only
integration that syncs status into your CRM should ask for
`customers:read` and `aml:read` only; it physically cannot write or
trigger billing.

## Rotation

Best practice: rotate keys every 6–12 months, and immediately on:

* Departure of an engineer who had access to the plaintext.
* Any suspected leak (committed to git, posted in a support ticket,
  etc.).
* Decommissioning the integration.

Rotation is "create new key → cut over the integration → revoke old
key". The revoke is **immediate** — the next request authenticated with
the revoked key returns `401 unauthorized`.

## IP allowlist (optional)

Keys may be locked to a list of source IPs. When set, a request from
any other source is rejected at the auth layer before the scope check
runs — so a stolen key from an off-allowlist source cannot even probe
scopes. Useful for integrations that always call from a fixed VPC
egress.

## What to do if a key leaks

1. **Revoke immediately** in Settings → Developers. Effect is
   instantaneous.
2. Issue a fresh key for the integration.
3. Audit the leak source and remove the secret from where it was
   exposed (`git filter-repo`, ticket scrub, etc.).
4. Review the request log for that key to assess what was accessed.

## Common errors

| HTTP | `error.code`      | Cause                                                              |
| ---- | ----------------- | ------------------------------------------------------------------ |
| 401  | `unauthorized`    | Header missing / malformed / wrong format / key revoked or expired |
| 403  | `forbidden_scope` | Key authenticated but lacks the scope this route requires          |

See [Errors](/concepts/errors) for the full envelope shape.
