Skip to content

API keys ​

Routes are relative to <PUBLIC_BASE_URL>; request and response bodies are JSON unless stated otherwise. Authentication, scopes, pagination and the error format are described in the REST API guides.

GET /api/v1/api-keys ​

List my API keys

The keys of the signed-in member, live ones first. Never a secret: only the eight-character prefix.

Access — Member session only: API keys are refused (api_key.session_required).

Responses

200 — The keys.

FieldTypeRequired
keysobject[]yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "keys": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "prefix": { "type": "string" },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "workflows:read",
                "workflows:write",
                "workflows:*",
                "executions:read",
                "executions:write",
                "executions:*",
                "mailboxes:read",
                "mailboxes:write",
                "mailboxes:*",
                "messages:read",
                "messages:write",
                "messages:*",
                "tables:read",
                "tables:write",
                "tables:*",
                "contacts:read",
                "contacts:write",
                "contacts:*",
                "templates:read",
                "templates:write",
                "templates:*",
                "connections:read",
                "connections:write",
                "connections:*",
                "approvals:read",
                "approvals:write",
                "approvals:*",
                "notifications:read",
                "notifications:write",
                "notifications:*",
                "review:read",
                "review:write",
                "review:*",
                "analyzer:read",
                "analyzer:write",
                "analyzer:*",
                "assistant:read",
                "assistant:write",
                "assistant:*",
                "dashboard:read",
                "dashboard:write",
                "dashboard:*",
                "profile:read",
                "profile:write",
                "profile:*",
                "admin:read",
                "admin:write",
                "admin:*",
                "signals:write"
              ]
            }
          },
          "memberId": { "type": "string" },
          "memberEmail": { "type": "string" },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
          },
          "expiresAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
              },
              { "type": "null" }
            ]
          },
          "lastUsedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
              },
              { "type": "null" }
            ]
          },
          "revokedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
              },
              { "type": "null" }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "scopes",
          "memberId",
          "memberEmail",
          "createdAt",
          "expiresAt",
          "lastUsedAt",
          "revokedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "keys" ],
  "additionalProperties": false
}

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

POST /api/v1/api-keys ​

Create an API key

Creates a key acting on behalf of the signed-in member, limited to the given scopes. The secret is returned once, here, and never again. Scopes reserved to administrators are refused to other members.

Access — Member session only: API keys are refused (api_key.session_required).

Request body (application/json)

FieldTypeRequired
namestringyes
scopesstring (enum)[]yes
expiresInDaysintegerno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "name": { "type": "string", "minLength": 1, "maxLength": 120 },
    "scopes": {
      "minItems": 1,
      "maxItems": 20,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "workflows:read",
          "workflows:write",
          "workflows:*",
          "executions:read",
          "executions:write",
          "executions:*",
          "mailboxes:read",
          "mailboxes:write",
          "mailboxes:*",
          "messages:read",
          "messages:write",
          "messages:*",
          "tables:read",
          "tables:write",
          "tables:*",
          "contacts:read",
          "contacts:write",
          "contacts:*",
          "templates:read",
          "templates:write",
          "templates:*",
          "connections:read",
          "connections:write",
          "connections:*",
          "approvals:read",
          "approvals:write",
          "approvals:*",
          "notifications:read",
          "notifications:write",
          "notifications:*",
          "review:read",
          "review:write",
          "review:*",
          "analyzer:read",
          "analyzer:write",
          "analyzer:*",
          "assistant:read",
          "assistant:write",
          "assistant:*",
          "dashboard:read",
          "dashboard:write",
          "dashboard:*",
          "profile:read",
          "profile:write",
          "profile:*",
          "admin:read",
          "admin:write",
          "admin:*",
          "signals:write"
        ]
      }
    },
    "expiresInDays": { "type": "integer", "minimum": 1, "maximum": 730 }
  },
  "required": [ "name", "scopes" ]
}

Responses

201 — The key, with its secret.

FieldTypeRequired
keyobjectyes
tokenstringyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "key": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "prefix": { "type": "string" },
        "scopes": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "workflows:read",
              "workflows:write",
              "workflows:*",
              "executions:read",
              "executions:write",
              "executions:*",
              "mailboxes:read",
              "mailboxes:write",
              "mailboxes:*",
              "messages:read",
              "messages:write",
              "messages:*",
              "tables:read",
              "tables:write",
              "tables:*",
              "contacts:read",
              "contacts:write",
              "contacts:*",
              "templates:read",
              "templates:write",
              "templates:*",
              "connections:read",
              "connections:write",
              "connections:*",
              "approvals:read",
              "approvals:write",
              "approvals:*",
              "notifications:read",
              "notifications:write",
              "notifications:*",
              "review:read",
              "review:write",
              "review:*",
              "analyzer:read",
              "analyzer:write",
              "analyzer:*",
              "assistant:read",
              "assistant:write",
              "assistant:*",
              "dashboard:read",
              "dashboard:write",
              "dashboard:*",
              "profile:read",
              "profile:write",
              "profile:*",
              "admin:read",
              "admin:write",
              "admin:*",
              "signals:write"
            ]
          }
        },
        "memberId": { "type": "string" },
        "memberEmail": { "type": "string" },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
        },
        "expiresAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
            },
            { "type": "null" }
          ]
        },
        "lastUsedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
            },
            { "type": "null" }
          ]
        },
        "revokedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
            },
            { "type": "null" }
          ]
        }
      },
      "required": [
        "id",
        "name",
        "prefix",
        "scopes",
        "memberId",
        "memberEmail",
        "createdAt",
        "expiresAt",
        "lastUsedAt",
        "revokedAt"
      ],
      "additionalProperties": false
    },
    "token": { "type": "string" }
  },
  "required": [ "key", "token" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — api_key.bad_request, api_key.scope_forbidden. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

Example request

json
{
  "name": "CRM sync",
  "scopes": [ "workflows:read", "executions:write" ],
  "expiresInDays": 365
}

Example response (201)

json
{
  "key": {
    "id": "0192f1c2-3b4d-7e8f-9a0b-1c2d3e4f5a6b",
    "name": "CRM sync",
    "prefix": "Qm9uam91",
    "scopes": [ "executions:write", "workflows:read" ],
    "memberId": "0192f1c2-0000-7000-8000-000000000001",
    "memberEmail": "alice@example.test",
    "createdAt": "2026-10-04T09:00:00.000Z",
    "expiresAt": "2027-10-04T09:00:00.000Z",
    "lastUsedAt": null,
    "revokedAt": null
  },
  "token": "mk_Qm9uam91cl9jZXN0X3VuX2V4ZW1wbGVfZGVfY2xl"
}

POST /api/v1/api-keys/{id}/revoke ​

Revoke one of my API keys

Immediate and final. The row stays, for the audit log. Idempotent: revoking twice keeps the first date.

Access — Member session only: API keys are refused (api_key.session_required).

Parameters

NameInTypeRequired
idpathstringyes

Responses

200 — The key, revoked.

FieldTypeRequired
keyobjectyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "key": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "prefix": { "type": "string" },
        "scopes": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "workflows:read",
              "workflows:write",
              "workflows:*",
              "executions:read",
              "executions:write",
              "executions:*",
              "mailboxes:read",
              "mailboxes:write",
              "mailboxes:*",
              "messages:read",
              "messages:write",
              "messages:*",
              "tables:read",
              "tables:write",
              "tables:*",
              "contacts:read",
              "contacts:write",
              "contacts:*",
              "templates:read",
              "templates:write",
              "templates:*",
              "connections:read",
              "connections:write",
              "connections:*",
              "approvals:read",
              "approvals:write",
              "approvals:*",
              "notifications:read",
              "notifications:write",
              "notifications:*",
              "review:read",
              "review:write",
              "review:*",
              "analyzer:read",
              "analyzer:write",
              "analyzer:*",
              "assistant:read",
              "assistant:write",
              "assistant:*",
              "dashboard:read",
              "dashboard:write",
              "dashboard:*",
              "profile:read",
              "profile:write",
              "profile:*",
              "admin:read",
              "admin:write",
              "admin:*",
              "signals:write"
            ]
          }
        },
        "memberId": { "type": "string" },
        "memberEmail": { "type": "string" },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
        },
        "expiresAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
            },
            { "type": "null" }
          ]
        },
        "lastUsedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
            },
            { "type": "null" }
          ]
        },
        "revokedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
            },
            { "type": "null" }
          ]
        }
      },
      "required": [
        "id",
        "name",
        "prefix",
        "scopes",
        "memberId",
        "memberEmail",
        "createdAt",
        "expiresAt",
        "lastUsedAt",
        "revokedAt"
      ],
      "additionalProperties": false
    }
  },
  "required": [ "key" ],
  "additionalProperties": false
}

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — api_key.not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/admin/api-keys ​

List every API key of the instance

Access — Member session or API key with scope admin:read (administrator role required).

Responses

200 — The keys.

FieldTypeRequired
keysobject[]yes

Same schema as GET /api/v1/api-keys.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

POST /api/v1/admin/api-keys/{id}/revoke ​

Revoke any API key

Access — Member session or API key with scope admin:write (administrator role required).

Parameters

NameInTypeRequired
idpathstringyes

Responses

200 — The key, revoked.

FieldTypeRequired
keyobjectyes

Same schema as POST /api/v1/api-keys/{id}/revoke.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — api_key.not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.