Skip to main content
API keys are the permission boundary for every integration and agent, so scope them tightly and rotate them on a schedule.

Prerequisites

  • An API key with admin organization access, or the dashboard — see Create an API key
  • A website id, if you are creating website-scoped keys — see the Quickstart

Scoping strategy

A key’s access takes one of two shapes: The owner role is never grantable to a key. Apply least privilege, especially for agents:
  • A content agent for one client site: websites scope with content_editor.
  • A site-building agent: websites scope with full_editor on that website.
  • Fleet automation (creating websites, managing members and keys): organization scope with admin.
One key per agent per concern. Blast radius on a leak stays one website, one role.
1

Create a scoped key

POST /v1/apiKeys requires expiresAt — pass an ISO datetime, or null for no expiry.
Create a website-scoped key
Response
plaintextKey appears exactly once, here and on rotate. Cactal stores only a SHA-256 hash. Store it in a secret manager immediately; if you lose it, rotate.
Expired and revoked keys fail authentication with 401.
2

Update name and expiry

PATCH /v1/apiKeys/{apiKeyId} changes metadata without touching the secret:
Shorten the expiry
You can shorten or extend expiry on an active key. Once a key is expired or revoked it is inactive for good — setting a future expiresAt returns 409.
3

Change a key's access

Sending access to PATCH /v1/apiKeys/{apiKeyId} replaces the key’s grants atomically — there is no partial merge. Widen a client key to a second website, or tighten an organization key down to specific websites:
Rescope to two websites
Every websiteId must belong to the key’s organization, or the call fails with 400. The secret is unchanged, so running integrations keep working under the new scope.
4

Rotate the secret

POST /v1/apiKeys/{apiKeyId}/rotate issues a new secret for the same key id and access. The old secret stops authenticating the moment the call returns.
Rotate
The response repeats the apiKey metadata with a fresh one-time plaintextKey. Inactive keys cannot rotate (409).
5

Revoke a key

POST /v1/apiKeys/{apiKeyId}/revoke disables the key permanently and idempotently:
Revoke
The response is the key’s metadata with revokedAt set. Revocation is the correct end state for offboarded agents — revoked keys stay listed for audit history.
6

Audit keys per website

GET /v1/apiKeys?organizationId=... lists every key in the organization with its access. To answer “which keys can touch this client’s website”, pass websiteId instead:
List keys scoped to a website
You should see { "items": [ { "id": "...", "name": "acme-content-agent", "access": { "kind": "websites", "websiteIds": ["V1StGXR8_Z5jdHi6B-myT"], "role": "content_editor" }, ... } ], "nextCursor": null }. This lists keys with a direct grant on the website; organization-wide keys appear only in the organization listing. Pass exactly one of organizationId or websiteId.
Key creation, updates, access changes, rotation, and revocation are all recorded in the organization’s audit log.

Key rotation runbook

For a suspected leak, rotate in place — the leaked secret dies instantly:
  1. POST /v1/apiKeys/{apiKeyId}/rotate and capture the new plaintextKey.
  2. Update the secret in your secret manager and redeploy consumers.
  3. Expect 401s from consumers between steps 1 and 2; they confirm the old secret is dead.
For routine, zero-downtime rotation, use create-then-revoke:
  1. Create a new key with identical access and a dated name.
  2. Roll consumers to the new secret and verify traffic with the old key has stopped.
  3. Revoke the old key.

Troubleshooting

Next steps