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 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.Create a root key with the permissions listed on this page under Root Keys in the dashboard sidebar. CLI commands can instead use a session from unkey login with the required permissions. See Permission reference for every permission.
unkey:v1:<workspace_id>:projects/<project_id>/keyspaces/<keyspace_id>/keys/*#write, the same permission as creating a non-recoverable key. Use the keyspace’s project ID. 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.
A missing write permission may not look like a permission problem. If the root key can’t read the keyspace, keys.createKey returns HTTP 404 err:unkey:data:api_not_found. If you see this for an API ID you know is correct, check the root key’s write permission first. A key that can read the keyspace gets HTTP 403 err:unkey:authorization:insufficient_permissions naming the missing write permission instead.
Create a recoverable key
On
keys.createKey. When true, an encrypted copy of the key is stored. Fails with HTTP 412 err:unkey:application:precondition_failed when the keyspace doesn’t store encrypted keys (“This API does not support key encryption.”). Missing write permission returns 404, or 403 if the root key can read the keyspace.Read the plaintext back
Bothkeys.getKey and apis.listKeys accept decrypt.
When
true, every returned key that’s recoverable has a plaintext field. Other keys come back without it, and the request doesn’t fail.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.