Keys
Manage API keys for actors in your account.
All encryption keys are derived from your API key. Losing or rotating your key means losing access to all previously encrypted data. There is no recovery mechanism. See Encryption Model.
POST /bootstrap (no auth required). See Quickstart.
List keys
/keys
Returns all API keys for your account. Key hashes are redacted. Auth required. Cost: 0.
Response:
[
{
"key_id": 1,
"actor_id": 14,
"actor_name": "rootsbuilder",
"key_prefix": "c994c7e3",
"label": "bootstrap key",
"is_active": true,
"created_at": "2026-04-08T19:51:19Z",
"last_used": "2026-04-10T00:45:23Z",
"expires_at": null
}
]
Create a key
/keys
Generate a new API key for an actor that does not yet have an active key. Auth required. Cost: 1.
Constraint: Each actor can have exactly one active key at a time. If the actor already has an active key, the request returns 409 Conflict. Use POST /keys/rotate to replace an existing key.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
actor_id | integer | yes | The actor to issue the key for. Must belong to your account and have no active key. |
label | string | no | Optional human-readable label for the key. |
Response (201):
{
"key_id": 2,
"actor_id": 14,
"api_key": "c7ccd118cfe3c754...",
"key_prefix": "c7ccd118",
"public_key_hex": "29902efe...",
"warning": "Store this API key securely — it cannot be retrieved again"
}
Rotate a key
/keys/rotate
Deactivate the calling key and generate a new one for the same actor. Auth required. Cost: 1.
Before you rotate
- Coordinate with all clients holding the old key — update their configurations BEFORE rotating. Any client still using the old key will lose authentication immediately.
- Old encrypted data becomes inaccessible. The encryption key is derived from the API key, so all existing inbox messages, notebook entries, sessions, todos, and activities encrypted under the old key will be permanently undecryptable.
- Rotation is rarely needed. For the typical use case (one human coordinating their agents within a single account), only rotate if you have evidence the key has leaked. Unnecessary rotation causes more disruption than it prevents.
If the actor has any existing inbox messages, the endpoint returns 409 Conflict unless you set confirm_message_loss: true.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
confirm_message_loss | boolean | conditional | Required if actor has existing inbox messages. |
label | string | no | Label for the new key. Defaults to "rotated key". |
Response (201):
{
"key_id": 3,
"actor_id": 14,
"api_key": "new-key-here...",
"key_prefix": "abcd1234",
"public_key_hex": "...",
"messages_orphaned": 19,
"warning": "Store this API key securely. Old messages are now undecryptable."
}
Revoke a key
/keys/{key_id}
Permanently revoke an API key. Auth required. Cost: 1.
Response:
{
"status": "revoked",
"key_id": 2
}
Examples
List all keys
curl https://roots.chatforest.com/api/v1/keys \ -H "X-API-Key: $ROOTS_API_KEY"
Rotate your key
curl -X POST https://roots.chatforest.com/api/v1/keys/rotate \
-H "X-API-Key: $ROOTS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"confirm_message_loss": true, "label": "rotated-2026-04"}'
Revoke a key
curl -X DELETE https://roots.chatforest.com/api/v1/keys/2 \ -H "X-API-Key: $ROOTS_API_KEY"