# Keys

Source: https://capacms.com/docs/concepts/keys

The two key families, production and draft keys, and which key to give each site, script and browser.

Every read from Capa carries an API key in the `x-api-key` header. The key decides three things: which project you read, which content you see, and what else the key may do.

## Two families

| Family | Looks like                                     | Works on                                                                  | Carries scopes                    |
| ------ | ---------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------- |
| Scoped | `cap_live_…`, `cap_test_…`                     | `/api/` only                                                              | Yes                               |
| Legacy | `pk_…`, `sk_…`, or an older key with no prefix | `/v2/api`, `/v3/api`, `/v2/schema`, `/v2/seo`, `/v2/agent/*`, and `/api/` | No. One of four fixed permissions |

**Start new work with a `cap_` key.** It can be limited to exactly what one integration needs: reads only, one model, an expiry, a list of allowed origins.

**Your existing legacy key keeps working**, unchanged, everywhere it works today. It also reads `/api/`, so you can move a page to the new API without minting anything.

## Production and draft keys

Every key has an environment.

| Environment                     | Prefix             | Sees                         | Cached at the CDN |
| ------------------------------- | ------------------ | ---------------------------- | ----------------- |
| Production                      | `cap_live_`, `pk_` | Published entries only       | Yes               |
| Draft, or any other environment | `cap_test_`, `sk_` | Every entry, drafts included | Never             |

The admin labels the two choices **Production** and **Draft**. `GET /api/me` reports the key's `environment`, and any value other than `production` sees drafts.

A draft key is a read-visibility dial, not a sandbox. There is one database. A draft key that can publish publishes to your live site. If you want somewhere safe to break things, use a separate project.

See [Drafts and publishing](https://capacms.com/docs/concepts/drafts-and-publishing) for what each key sees.

## What a scoped key can do today

`/api/` reads and never writes. A `cap_` key that holds `instance:read` reads entries through `GET /api/entries/...` and `/api/graphql`. `GET /api/me` answers any key.

A write scope you grant today is stored and enforced from the day the route it unlocks exists, and does nothing before then. **Keep your legacy key for writes** and for anything that calls `/v2` or `/v3`.

## Why a `cap_` key is refused on `/v2` and `/v3`

The legacy routes do not read scopes. If they accepted a scoped key, they would serve it everything, and a restriction you set in the admin would be ignored on the surface most sites use.

So a `cap_` key sent to `/v2/api`, `/v3/api`, `/v2/schema`, `/v2/seo` or `/v2/agent/*` gets the same `401` an unknown key gets. A site that is half ported runs two keys: its legacy key for the old pages and a `cap_` key for the new ones.

## Create a key

1. Open **Developers > Keys** and choose **New key**.
2. Give it a **Name** after the system that will hold it, such as `Website` or `Shopify sync`.
3. Pick an **Environment**: **Production** for a live site, **Draft** for a preview or staging build.
4. Under **Grants**, pick a starting point. **Read only** "reads published content, models and media" and suits almost every site.
5. Optionally set an **Expiry**.
6. Choose **Create key**, then copy the secret.

**The secret is shown once.** Capa stores it hashed and cannot show it again. If you lose it, rotate the key.

Only Admins, Developers and the project owner can create keys. The full scope list and presets are in [Authentication](https://capacms.com/docs/api/authentication).

## Check a key

`GET /api/me` says what the key in your hand can do:

```bash
curl https://cdn.capacms.com/api/me -H "x-api-key: $CAPA_KEY"
```

It returns the key's `environment`, `scopes`, the `models` it can read, the `surfaces` it works on, its version pin and its expiry. Read it before you debug a `403`.

## Keys in a browser

Anything in a browser bundle is public. If a key must ship to the browser:

* Use a **production** `cap_live_` key with **Read only**.
* Bind it to your site's origins with **Allowed origins** in the key's row menu. A request from another origin gets [`403 origin_refused`](https://capacms.com/docs/errors/origin_refused).
* Never ship a draft key. Anyone who views your page source could read your unpublished drafts with it.

A server-to-server call sends no `Origin` header, so origin binding never blocks your own backend.

## Rotate, expire and deactivate

* **Rotate** mints a second key with the same grants and gives the old one a grace window: Now, 1 hour, 24 hours or 7 days. Deploy the new secret inside the window. A key can be rotated once.
* **Expiry** is optional. Within 14 days of it, Developers > Keys marks the key with a red dot. Put the date in your own calendar too: nothing emails you.
* **Deactivate** stops a key at once. You can reactivate it later.

An expired, deactivated or unknown key all get the same [`401 invalid_key`](https://capacms.com/docs/errors/invalid_key), so the answer never reveals which it was.

## Which key for which job

| Job                                          | Key                                                |
| -------------------------------------------- | -------------------------------------------------- |
| A public website                             | `cap_live_`, Read only                             |
| A preview or staging build that shows drafts | `cap_test_`, Read only, server-side only           |
| A browser app                                | `cap_live_`, Read only, bound to your origins      |
| A build script or static site generator      | `cap_live_`, Read only                             |
| An existing site on `/v2/api`                | Keep its legacy key                                |
| An AI assistant through the MCP server       | `cap_live_`, Read only. See [AI and MCP](https://capacms.com/docs/ai) |

One key per site or integration makes rotation painless and makes the **Last used** column meaningful.

## Related

* [Authentication](https://capacms.com/docs/api/authentication) for scopes, presets, rotation and allowed origins in detail.
* [The Developers section](https://capacms.com/docs/admin/developers) for the Keys screen.
* [Versions](https://capacms.com/docs/concepts/versions) for how a key's version pin works.
