import { Code } from '@astrojs/starlight/components';
import { apiHost, apiOrigin } from '../../../../lib/docs-env';

## Symptoms

The API returns `401 invalid_api_key` or `401 authentication_required`,
or a `403 insufficient_scope` after the key authenticated. The 401 message
is the same opaque string on both paths by design — the server never
discloses whether a key exists, only whether the request as-presented
is authorized.

## Region mismatch

API keys are bound to the region they were issued in. The key prefix
encodes the region:

- `ah_sk_us_…` only works against <code>{apiHost('us')}</code>.
- `ah_sk_eu_…` only works against <code>{apiHost('eu')}</code>.

A wrong-region request returns `401 invalid_api_key`. Pick the host
that matches the prefix:

<Code
  code={`# Correct\ncurl ${apiOrigin('us')}/v1/me \\\n  -H "Authorization: Bearer ah_sk_us_test_..."`}
  lang="bash"
/>

For the region rules, see
[Where is my data hosted?](/guides/faq/data-residency).

## Mode mismatch

Keys are also bound to a mode (`test` or `live`). The two modes are
isolated databases. A `test` key cannot read or write live-mode
resources, and vice versa.

If you mix modes by accident (storing a test key as your production
secret), you see "vanishing data": verifications created against one mode
return `404 not_found` to the other mode's key.

## Missing scope

Each key carries a list of scopes (`applicants:read`,
`sessions:write`, etc.) that gate which routes it can call. A request
to a route the key does not have the scope for returns
`403 insufficient_scope`, and the error body's `details.required_scope`
field names the missing one.

## Revoked or expired key

Revoked keys are rejected immediately on every request with no grace
window. Expired keys are rejected after their `expiresAt` timestamp
passes. Keys created without an expiry never expire.

## Fix

1. Read the key prefix — confirm region and mode match the endpoint.
2. Open **Developers -> API keys** in the matching console region and
   confirm the key's status is `active` with the scopes you need.
3. If revoked, generate a fresh one and rotate it through your
   secrets manager. If the scopes are wrong, generate a new key —
   scopes are not editable. See
   [Rotate an API key](/developers/api-keys-rotate) for the
   zero-downtime swap.

## Still stuck?

Confirm the header is `Authorization: Bearer <key>` with a single
space. Missing whitespace returns `401 authentication_required`. If
the key looks right and the rejection looks wrong, include the
`X-Request-Id` from the response in your support ticket.