API Keys
Non-interactive (machine-to-machine) bearer credentials, scoped like a human member to an organization (admin role) or one environment (user role) — never owner. Requires the caller to be an Owner or Admin, the same floor as adding a human member. CreateApiKey's response carries the complete bearer secret exactly once; it is never shown or retrievable again by any endpoint, including ListApiKeys, which returns only the key's public, non-secret prefix. Losing a secret means creating a new key and revoking the old one.
3 endpoints
Lists every API key for the organization, revoked and expired ones included.
get/v1/organizations/{organizationId}/api-keysRequires a bearer token
The caller sees each key's revokedAt/expiresAt rather than the row
disappearing from the list. No key's secret is ever present here; it
is returned once, only from the create-key response. Requires the
caller to be an Owner or Admin.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| organizationId* | path | string<uuid> | Organization id |
Responses
| Status | Body | Description |
|---|---|---|
| 200 | ListApiKeysResponse | Every api key for this organization |
| 401 | ErrorBody | Missing, invalid, or expired bearer token |
| 403 | ErrorBody | The caller is a member but not an Owner or Admin |
| 404 | ErrorBody | No organization with this id exists, or the caller is not a member of it |
| 429 | ErrorBody | The caller has exceeded its request budget. error is rateLimited. Wait Retry-After seconds before retrying — a prior successful response's own RateLimit-Remaining/RateLimit-Reset headers show this coming before it happens. |
| 500 | ErrorBody | An unexpected internal error. error is always internalError; message is deliberately the fixed string "an unexpected error occurred", never the real cause, which is logged server-side instead of being returned to the caller. |
Mints a new API key scoped to this organization.
post/v1/organizations/{organizationId}/api-keysRequires a bearer token
admin role for an org-wide credential, or user role bound to
exactly one environmentId. Requires the caller to be an Owner or
Admin — the same floor as adding a human member, since minting a
standing credential is at least as privileged as that.
The response's secret is the complete bearer credential and the only
time it is ever returned — losing it means creating a new key and
revoking this one.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| organizationId* | path | string<uuid> | Organization id |
Request bodyCreateApiKeyRequest
| Field | Type | Description |
|---|---|---|
| environmentId | string | null<uuid> | Required when role is "user"; must be unset when role is
"admin" — otherwise surfaced as 422 invalidScope. |
| expiresAt | string | null<date-time> | Optional hard expiry. Must be in the future, otherwise surfaced as
422 invalidScope. |
| name* | string | Operator-chosen label, shown back when listing this organization's keys and used nowhere else. |
| role* | ApiKeyRole | "admin" (org-wide) or "user" (must also set environmentId).
"owner" is rejected — an API key can never hold the Owner role. |
Responses
| Status | Body | Description |
|---|---|---|
| 201 | CreateApiKeyResponse | The newly minted key, including its one-time secret |
| 400 | ErrorBody | role is not "admin" or "user" |
| 401 | ErrorBody | Missing, invalid, or expired bearer token |
| 403 | ErrorBody | The caller is a member but not an Owner or Admin |
| 404 | ErrorBody | No organization with this id exists, or the caller is not a member of it |
| 422 | ErrorBody | invalidScope: a user-role key with no environmentId, an admin-role key with one, environmentId not in this organization, or expiresAt in the past |
| 429 | ErrorBody | The caller has exceeded its request budget. error is rateLimited. Wait Retry-After seconds before retrying — a prior successful response's own RateLimit-Remaining/RateLimit-Reset headers show this coming before it happens. |
| 500 | ErrorBody | An unexpected internal error. error is always internalError; message is deliberately the fixed string "an unexpected error occurred", never the real cause, which is logged server-side instead of being returned to the caller. |
Revokes an API key.
delete/v1/organizations/{organizationId}/api-keys/{id}Requires a bearer token
Effective immediately: the very next request made with it is refused, checked uncached on every call — there is no propagation delay to wait out. Idempotent: revoking an already-revoked key succeeds without error. Requires the caller to be an Owner or Admin.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| organizationId* | path | string<uuid> | Organization id |
| id* | path | string<uuid> | Api key id |
Responses
| Status | Body | Description |
|---|---|---|
| 204 | — | The key is revoked (or already was) |
| 401 | ErrorBody | Missing, invalid, or expired bearer token |
| 403 | ErrorBody | The caller is a member but not an Owner or Admin |
| 404 | ErrorBody | No organization with this id exists, the caller is not a member of it, or no api key with this id exists in this organization |
| 429 | ErrorBody | The caller has exceeded its request budget. error is rateLimited. Wait Retry-After seconds before retrying — a prior successful response's own RateLimit-Remaining/RateLimit-Reset headers show this coming before it happens. |
| 500 | ErrorBody | An unexpected internal error. error is always internalError; message is deliberately the fixed string "an unexpected error occurred", never the real cause, which is logged server-side instead of being returned to the caller. |