# API keys (/docs/api/concepts/api-keys)



An API key authenticates every request you make and decides what that request is allowed to do. One key is scoped to one [organization](/docs/api/concepts/organizations). It carries the rate-limit tier that caps throughput and the allowlist of `returnUrl` values the OAuth flows will accept. A future scope list will gate individual routes.

## Key format [#key-format]

```text
lp_<env>_<key_id>_<secret>
```

* `env` is either `live` or `test`. Test keys talk to the same API but their traffic is routed to sandboxed back-ends — no production publishing, no real billing.
* `key_id` is a 16-character base32 handle. It's public — safe to log, used for rate-limit attribution, and how we look up the row.
* `secret` is a 43-character base64url remainder. Everything after the last underscore.

We store `bcrypt(cost=12, secret)`. The plaintext exists only in your client. If you lose it, you rotate.

<Callout type="warn">
  The secret is shown exactly once — at the moment of creation. We can't recover it, display it again, or confirm you have the right one. Paste it into your secrets manager before you close the tab.
</Callout>

## Sending the key [#sending-the-key]

Primary form: `X-Api-Key: <key>`. `Authorization: Bearer <key>` is accepted as a fallback for clients that can't set custom headers. If both are sent, the server prefers `X-Api-Key`.

```http
GET /v1/whoami HTTP/1.1
Host: api.layers.com
X-Api-Key: lp_live_01HX9Y6K7EJ4T2AB_4QZpN...remainder
```

The first call any client should make is [`GET /v1/whoami`](/docs/api/reference/organizations/whoami) — it resolves your key to the org it's bound to and lets you fail fast if anything is wrong. Response shape:

```json
{
  "organizationId": "2481fa5c-a404-44ed-a561-565392499abc",
  "workspaceId": "2481fa5c-a404-44ed-a561-565392499abc",
  "organizationName": "Acme Growth",
  "scopes": [],
  "rateLimitTier": "standard",
  "killSwitch": false,
  "apiAccessRevoked": false,
  "apiKeyId": "c2037bb9-354d-4662-96b7-97a28ad6b6e1"
}
```

## Scopes (planned) [#scopes-planned]

<Callout>
  Scopes are not yet enforced. Partner keys currently carry org-level access across the whole surface and `/v1/whoami` returns `scopes: []`. The table below documents the planned vocabulary so your integration can plan for granular access.
</Callout>

| Scope                                                | Gates                                                |
| ---------------------------------------------------- | ---------------------------------------------------- |
| `projects:read` / `projects:write`                   | Project CRUD.                                        |
| `ingest:write`                                       | GitHub / website / App Store ingestion.              |
| `content:read` / `content:write` / `content:approve` | Content read, generation, and approval actions.      |
| `social:read` / `social:write`                       | Social account listing and OAuth flows.              |
| `publish:write`                                      | Schedule and publish posts.                          |
| `events:read` / `events:read+pii`                    | SDK event stream. `+pii` includes unredacted fields. |
| `metrics:read`                                       | Organic and ads metrics.                             |
| `ads:read` / `ads:write`                             | Ads reads today; ads CRUD is planned.                |
| `influencers:write`                                  | Influencer create / clone / patch.                   |
| `leased:write`                                       | Submit and release leased-account requests.          |
| `engagement:write`                                   | Patch engagement layer config.                       |
| `github:admin`                                       | Register and read GitHub App installations.          |
| `jobs:read` / `jobs:cancel`                          | Read and cancel [jobs](/docs/api/concepts/jobs).     |

## Rate-limit tiers [#rate-limit-tiers]

Tier is a property of the key, not the endpoint. Buckets are keyed per `(key_id, endpoint_class)` so a runaway generation loop can't starve your reads. Endpoint classes: `read-light`, `write-light`, `long-running`.

| Tier       | Typical provisioning                                                             |
| ---------- | -------------------------------------------------------------------------------- |
| `standard` | Default for every partner key.                                                   |
| `pilot`    | Higher throughput for early-integration partners — granted by Layers on request. |
| `partner`  | Enterprise tier for GA partners with SLAs.                                       |

Every `429` response carries:

* `Retry-After` (seconds)
* `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
* `X-RateLimit-Endpoint-Class`, `X-RateLimit-Tier`
* Body with `error.details.retryAfterMs` and `error.details.endpointClass`

See [common patterns](/docs/api/getting-started/common-patterns#rate-limit-signals) for a back-off snippet and [rate limits](/docs/api/operational/rate-limits) for the full bucket policy.

## Rotation [#rotation]

You can rotate a key without downtime:

<Steps>
  <Step>
    Ask Layers to create a second key with the same access.
  </Step>

  <Step>
    Deploy the new secret to your callers.
  </Step>

  <Step>
    Watch 

    `last_used_at`

     on the old key drop to zero.
  </Step>

  <Step>
    Revoke the old key.
  </Step>
</Steps>

Both keys are active in parallel during the cutover, so there's no flap.

## Revocation [#revocation]

Three levers, each stronger than the last:

* **Revoke the key.** `revoked_at` gets stamped. Every subsequent request fails with `401 UNAUTHENTICATED`. This is the normal path.
* **Kill switch on the key.** Sets `kill_switch = true`. Requests fail with `503 KILL_SWITCH` — a different signal than revoked, so you can tell an incident from a rotation. Clearable without reissuing.
* **Org-wide kill switch.** Flips `organizations.api_access_revoked = true`. Every key on the org fails. Reserved for incident response.

<Callout>
  Revocation and kill-switch flips take effect on the next request — propagation is effectively immediate.
</Callout>
