Docs/Reference/API Keys

API Keys

Machine credentials scoped to a realm. Used for server-to-server authentication instead of a user access token. All endpoints are under /v1/realms/{slug}/keys.

Every endpoint requires ROLE_REGISTERED — only registered users can manage keys, and a user can only see their own keys.

Create

POST /v1/realms/{slug}/keys

Create a new API key in the given realm. The plaintext secret is returned once — the server never exposes it again. Store it immediately.

  • Auth: ROLE_REGISTERED
  • Rate limit: 10 / 60s

Path params: slug — realm slug.

Request

JSON
{
   "name": "Production backend",
   "scopes": [ "stumper:read", "stumper:write" ],
   "origins": [ "mywebsite.com", "*.mywebsite.com" ],
   "expires_at": "2027-01-01T00:00:00+00:00"
}
FieldTypeConstraints
namestringrequired, not blank, max 128 chars.
scopesarray<string>default [].
originsarray<string>default []. Allow-list of HTTP origins (host, optionally with *. wildcard) the key may be presented from. Empty means unrestricted — required for browser-embedded keys like DDB tooltips.
expires_atstring|nullISO 8601, must be in the future.

Response201 Created

JSON
{
   "key": {
      "id": 17,
      "name": "Production backend",
      "prefix": "kat_live_abc123",
      "scopes": [ "stumper:read", "stumper:write" ],
      "origins": [ "mywebsite.com", "*.mywebsite.com" ],
      "is_active": true,
      "last_used_at": null,
      "expires_at": "2027-01-01T00:00:00+00:00",
      "created_at": "2026-04-18T12:00:00+00:00",
      "revoked_at": null
   },
   "plaintext": "kat_live_abc123_<long-random-string>"
}

Errors

CodeMeaning
400expires_at is in the past.
404Realm not found.

List

GET /v1/realms/{slug}/keys

List the caller's API keys for the given realm.

  • Auth: ROLE_REGISTERED

Response200 OK

JSON
{
   "keys": [
      {
         "id": 17,
         "name": "Production backend",
         "prefix": "kat_live_abc123",
         "scopes": [ "stumper:read" ],
         "is_active": true,
         "last_used_at": "2026-04-18T11:55:00+00:00",
         "expires_at": null,
         "created_at": "2026-04-01T00:00:00+00:00",
         "revoked_at": null
      }
   ]
}

Show

GET /v1/realms/{slug}/keys/{id}

Get a single key by ID. Only returns keys owned by the caller.

  • Auth: ROLE_REGISTERED

Response200 OK

JSON
{
   "key": { "…": "as above" }
}

Errors

CodeMeaning
404Realm or key not found.

Update

PATCH /v1/realms/{slug}/keys/{id}

Update a key's name, scopes, or active flag. Cannot change the secret; use /roll for that.

  • Auth: ROLE_REGISTERED

Request

JSON
{
   "name": "Production backend (EU)",
   "scopes": [ "stumper:read" ],
   "origins": [ "mywebsite.com" ],
   "is_active": true
}

All fields optional. name is constrained to max 128 chars. Setting origins replaces the existing allow-list (pass [] to remove all restrictions).

Response200 OK

JSON
{
   "key": { "…": "updated key" }
}

Errors

CodeMeaning
404Realm or key not found.

Revoke

DELETE /v1/realms/{slug}/keys/{id}

Revoke an API key. Soft-deletes; the key can no longer authenticate.

  • Auth: ROLE_REGISTERED

Response200 OK

JSON
{
   "revoked": true
}

Errors

CodeMeaning
404Realm or key not found.

Roll secret

POST /v1/realms/{slug}/keys/{id}/roll

Regenerate a key's secret. The previous plaintext stops working immediately. The new plaintext is returned once.

  • Auth: ROLE_REGISTERED
  • Rate limit: 5 / 60s

Response200 OK

JSON
{
   "key":       { "…": "updated key" },
   "plaintext": "kat_live_abc123_<new-long-random>"
}

Errors

CodeMeaning
404Realm or key not found.