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

> ## Agent Instructions
> Unkey is two separate products. Compute builds, deploys, and runs apps behind a gateway. API Management issues API keys, enforces rate limits, manages identities and permissions, and reports usage. Say which product a page belongs to; a reader can use either without the other.
> Every Unkey API endpoint is an HTTP POST to https://api.unkey.com/v2/{service}.{procedure} with a root key in the Authorization: Bearer header. Root keys are workspace scoped.
> Error codes have the form err:{system}:{category}:{specific} and each has a page at /errors/{system}/{category}/{specific}.
> The word environment means production or preview in Compute. Rate limiting has four meanings on this site; the glossary lists them.

# Recoverable keys

> Store an encrypted copy of a key so you can show it again later.

A recoverable key is one you can show again after it's created. By default Unkey keeps only a hash of each key, so once `keys.createKey` returns it, nobody (including Unkey) can read it again. That's the safest setup and right for most apps: a user who loses a key gets a new one.

Use recoverable keys when showing a key again is worth the extra risk, such as an API playground that needs a working key, or a settings page where users expect to reveal their key. We store an encrypted copy and decrypt it only when you ask. [Key storage](/docs/platform/security/key-storage) explains how that copy is protected.

## Opt the keyspace in

Recoverable keys need the keyspace's **Store encrypted keys** setting. It's off for new keyspaces and there's no dashboard toggle, so email support with the keyspace's API ID and we'll turn it on. Keys created before that can't be recovered.

<Note>
  You need a root key with the permissions listed on this page. Create one in the dashboard under **Settings > Root Keys**. See [Permission reference](/docs/platform/root-keys/permissions-legacy) for every permission.
</Note>

Creating a recoverable key needs `api.*.encrypt_key` or `api.<api_id>.encrypt_key` in addition to the create permission. Reading one back needs `api.*.decrypt_key` or `api.<api_id>.decrypt_key` in addition to the read permission. Grant these only to root keys that need them. See [Root key permissions](/docs/platform/root-keys/permissions).

A missing encrypt permission doesn't look like a permission problem. `keys.createKey` returns HTTP 404 [`err:unkey:data:api_not_found`](/docs/errors/unkey/data/api_not_found), "The specified API was not found." If you see this for an API ID you know is correct, check the root key's `encrypt_key` permission first.

## Create a recoverable key

<ParamField body="recoverable" type="boolean" default="false">
  On `keys.createKey`. When `true`, an encrypted copy of the key is stored. Fails with HTTP 412 [`err:unkey:application:precondition_failed`](/docs/errors/unkey/application/precondition_failed) when the keyspace doesn't store encrypted keys ("This API does not support key encryption."), and with HTTP 404 `err:unkey:data:api_not_found` when the root key lacks `encrypt_key`.
</ParamField>

```bash theme={"system"}
curl -X POST https://api.unkey.com/v2/keys.createKey \
  -H "Authorization: Bearer $UNKEY_ROOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "apiId": "api_...", "recoverable": true }'
```

Rerolling a recoverable key produces a recoverable key, so the reroll needs the encrypt permission too.

## Read the plaintext back

Both `keys.getKey` and `apis.listKeys` accept `decrypt`.

<ParamField body="decrypt" type="boolean" default="false">
  When `true`, every returned key that's recoverable has a `plaintext` field. Other keys come back without it, and the request doesn't fail.
</ParamField>

```bash theme={"system"}
curl -X POST https://api.unkey.com/v2/keys.getKey \
  -H "Authorization: Bearer $UNKEY_ROOT_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "keyId": "key_...", "decrypt": true }'
```

```json theme={"system"}
{
  "meta": { "requestId": "req_..." },
  "data": {
    "keyId": "key_...",
    "start": "sk_live_abc1",
    "enabled": true,
    "createdAt": 1704067200000,
    "plaintext": "sk_live_..."
  }
}
```

`decrypt: true` fails with HTTP 412 when the key's keyspace doesn't store encrypted keys, and with a permission error when the root key lacks `decrypt_key`. Treat any response with `plaintext` like the original create response: send it over a secure channel, and never log or cache it.

## Recoverable keys and rotation

If a key may have leaked, reroll it. Being able to read it again doesn't make it safe. See [Rerolling keys](/docs/api-management/keys/rerolling-keys).
