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

# Authentication

> How ReconLayer authenticates API requests: API keys, dashboard session tokens, organization resolution, and the dev bypass.

<Warning>
  **Early preview.** The ReconLayer API is under active development. Endpoints, request/response shapes, and defaults may change. Breaking changes will be announced in advance — pin to a known-good integration and watch the changelog before upgrading.
</Warning>

Every request to a `/v1/*` route must carry a **Bearer credential**. ReconLayer accepts two kinds of credential on the same `Authorization` header, and resolves your organization from whichever one you present.

```http theme={null}
Authorization: Bearer <credential>
```

<Note>
  `/health`, `/openapi.json`, `/openapi.yaml`, the `/docs` API explorer, and everything under `/webhooks/*` are public and do not require a credential. Every other route — including all `/v1/*` routes — runs through the authentication hook below before it is handled.
</Note>

## Two credential types

ReconLayer inspects the bearer token and routes it to one of two verification paths:

<Tabs>
  <Tab title="API key (headless clients)">
    Use an **API key** for servers, scripts, and backend integrations. API keys are created via [`POST /v1/api-keys`](/api-reference/integrations/create-an-api-key) and are the recommended credential for any code that calls ReconLayer outside the dashboard.

    ```bash theme={null}
    curl https://api.reconlayer.com/v1/payment-intents \
      --header "Authorization: Bearer rl_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789..."
    ```

    The organization, scopes, and key status are all resolved server-side from the key itself — you do not need to send `x-organization-id` alongside a valid API key.
  </Tab>

  <Tab title="Clerk session token (dashboard users)">
    Dashboard requests authenticate with a **Clerk session token**. The API verifies the session with Clerk and resolves the caller's organization from the active Clerk organization, falling back to the `x-organization-id` header when the session doesn't carry one.

    ```bash theme={null}
    curl https://api.reconlayer.com/v1/payment-intents \
      --header "Authorization: Bearer <clerk-session-token>" \
      --header "x-organization-id: org_acme"
    ```

    Some routes — for example managing API keys themselves — require a **signed-in user** and will reject a valid API key with `403 forbidden`. See [Signed-in-user-only routes](#signed-in-user-only-routes) below.
  </Tab>
</Tabs>

### How ReconLayer tells the two apart

The API key check is a cheap prefix match before any database lookup: any bearer token that **starts with `rl_live_`** is treated as an API key and verified against the hashed key store. Anything else is treated as a Clerk session token and verified with Clerk.

```
rl_live_<43 url-safe base64 characters>   →  ~32 bytes of entropy
```

Only the SHA-256 hash of the full key is stored. The **plaintext key is returned exactly once**, in the response body of `POST /v1/api-keys` — copy it immediately, because it cannot be retrieved again. The response also includes a non-secret `prefix` (the key's first 16 characters, e.g. `rl_live_AbCd1234`) that is safe to display in the dashboard for identification.

## API keys: scopes, status, and expiry

Each API key carries:

| Field        | Description                                                                                     |
| ------------ | ----------------------------------------------------------------------------------------------- |
| `prefix`     | Non-secret display prefix (e.g. `rl_live_AbCd1234`), safe to show in UI.                        |
| `scopes`     | An array of free-form scope strings assigned at creation, used by your own authorization logic. |
| `status`     | One of `active`, `expired`, or `revoked`, derived from `revokedAt` / `expiresAt`.               |
| `expiresAt`  | Optional ISO 8601 expiry. After this time the key is rejected.                                  |
| `revokedAt`  | Set when a key is revoked; revoked keys are rejected immediately.                               |
| `lastUsedAt` | Updated on every successful verification.                                                       |

A request authenticated with an API key whose `status` is not `active` (revoked, or past `expiresAt`) is rejected with:

```json 401 Invalid, revoked, or expired API key theme={null}
{
  "error": "unauthorized",
  "message": "Invalid, revoked, or expired API key."
}
```

Manage keys via [`POST /v1/api-keys`](/api-reference/integrations/create-an-api-key), [`GET /v1/api-keys`](/api-reference/integrations/list-api-keys), [`PATCH /v1/api-keys/{keyId}`](/api-reference/integrations/update-an-api-key), and [`DELETE` (revoke) `/v1/api-keys/{keyId}`](/api-reference/integrations/revoke-an-api-key).

<Warning>
  API key management routes require a signed-in dashboard session (a Clerk session token), not another API key. Calling them with an API key returns `403 forbidden`.
</Warning>

## Organization resolution

ReconLayer is multi-tenant: every record belongs to an organization, and every authenticated request resolves to exactly one `organizationId`.

* **API key requests** — the organization is whatever the key was issued for. You do not need to send `x-organization-id`.
* **Clerk session requests** — the organization comes from the active organization on the Clerk session. If the session does not carry one, send `x-organization-id` and ReconLayer will use it as a fallback.

```http theme={null}
Authorization: Bearer <clerk-session-token>
x-organization-id: org_acme
```

If neither the credential nor the header resolves to an organization, the request fails before it reaches any handler.

## Missing or invalid credentials

| Situation                                                                       | Status | Body                                                                                                        |
| ------------------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------- |
| No `Authorization` header, or it doesn't start with `Bearer `                   | `401`  | `{ "error": "unauthorized", "message": "Missing bearer credential. Provide an API key or session token." }` |
| Token looks like an API key (`rl_live_...`) but is unknown, revoked, or expired | `401`  | `{ "error": "unauthorized", "message": "Invalid, revoked, or expired API key." }`                           |
| Token is not a valid Clerk session, or resolves to no organization              | `401`  | `{ "error": "unauthorized", "message": "Invalid session token or no active organization." }`                |

## Signed-in-user-only routes

A small set of routes require a real dashboard user — for example, creating, updating, or revoking API keys, and other account-administration actions. If you call one of these with an API key, ReconLayer responds:

```json 403 Signed-in session required theme={null}
{
  "error": "forbidden",
  "message": "This action requires a signed-in dashboard session."
}
```

The same shape (`error: "forbidden"`) is also returned when a signed-in user lacks the workspace permission required for an action — for example, a user without the `cases_action` permission calling a write endpoint.

## Local development bypass

When running the API locally with `AUTH_DEV_BYPASS=1` (and `NODE_ENV` is **not** `production`), requests may omit the `Authorization` header entirely and instead identify their organization with `x-organization-id` alone:

```bash theme={null}
curl http://localhost:3001/v1/payment-intents \
  --header "x-organization-id: org_dev"
```

This bypass is intended for local development and the test suite only. It refuses to activate when `NODE_ENV=production`, so it cannot accidentally ship enabled. In any real deployment, always present a Bearer credential as described above.

## Next steps

* [API design principles](/api-reference/design-principles) — idempotency, business-level uniqueness, and how writes are deduplicated.
* [Idempotency](/api-reference/idempotency) — the `Idempotency-Key` header in depth.
* [Errors](/api-reference/errors) — the error envelope and status codes you'll see across the API.
* [Quickstart](/getting-started/quickstart) — get an API key and make your first authenticated call.
