API keys API
API key endpoints require the admin scope. All responses redact the plaintext key (it is only returned at creation time).
Key endpoints
Section titled “Key endpoints”| Method | Path | Description |
|---|---|---|
POST |
/api/v1/api-keys |
Create API key |
GET |
/api/v1/api-keys |
List API keys |
DELETE |
/api/v1/api-keys/:id |
Revoke API key |
POST |
/api/v1/api-keys/:id/rotate |
Rotate API key |
GET |
/api/v1/api-keys/dormant |
List dormant keys |
Role endpoints (RBAC)
Section titled “Role endpoints (RBAC)”| Method | Path | Description |
|---|---|---|
POST |
/api/v1/roles |
Create role |
GET |
/api/v1/roles |
List roles |
GET |
/api/v1/roles/:id |
Get role |
PATCH |
/api/v1/roles/:id |
Update role |
DELETE |
/api/v1/roles/:id |
Delete role |
POST |
/api/v1/roles/assign/:keyId |
Assign role to API key |
DELETE |
/api/v1/roles/assign/:keyId |
Remove role from API key |
Create API key
Section titled “Create API key”POST /api/v1/api-keysBody:
{ "tenant_id": "uuid", "name": "my-service", "rate_limit_max": 100, "rate_limit_window": "1 minute"}| Field | Type | Required | Description |
|---|---|---|---|
tenant_id |
UUID | Yes | Tenant to scope the key to |
name |
string | No | Descriptive name |
rate_limit_max |
integer | No | Max requests per window (1-100,000) |
rate_limit_window |
string | No | Time window (e.g., "30 seconds", "1 minute", "1 hour") |
Response: 201 Created
{ "id": "key-uuid", "tenant_id": "tenant-uuid", "key_prefix": "sk_live_abc", "name": "my-service", "created_at": "2024-01-01T00:00:00.000Z", "plaintext_key": "sk_live_abc123..."}The plaintext_key is only returned in this response and cannot be retrieved again.
List API keys
Section titled “List API keys”GET /api/v1/api-keys?tenant_id=<uuid>Query Parameters:
| Param | Type | Description |
|---|---|---|
tenant_id |
UUID | Filter by tenant (optional; scoped keys auto-filter) |
Response: 200 OK with an array of API key records (without plaintext keys).
Revoke API key
Section titled “Revoke API key”DELETE /api/v1/api-keys/:idResponse: 204 No Content
Returns 404 if the key does not exist or is already revoked.
Rotate API key
Section titled “Rotate API key”POST /api/v1/api-keys/:id/rotateRevokes the existing key and creates a new one. Optionally update the name.
Body (optional):
{ "name": "my-service-v2"}Response: 201 Created. Returns the new key with plaintext_key.
List dormant keys
Section titled “List dormant keys”GET /api/v1/api-keys/dormant?days=90Returns keys that have not been used in the specified number of days.
| Param | Type | Default | Description |
|---|---|---|---|
days |
integer | 90 | Minimum days since last use (1-365) |
Response: 200 OK with an array of API key records.
Create role
Section titled “Create role”POST /api/v1/rolesBody:
{ "name": "Editor", "description": "Can read and write but not manage keys", "scopes": ["read", "write"], "tenant_id": "uuid (optional, null for global)"}| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Role name (1-100 chars) |
description |
string | No | Description (max 500 chars) |
scopes |
string[] | Yes | Array of "read", "write", "admin" |
tenant_id |
UUID | No | Scope role to a tenant (null for global) |
Response: 201 Created. Returns the role object.
List roles
Section titled “List roles”GET /api/v1/roles?tenant_id=<uuid>Response: 200 OK with an array of role objects.
Update role
Section titled “Update role”PATCH /api/v1/roles/:idBody (all optional):
{ "name": "Senior Editor", "scopes": ["read", "write", "admin"]}Response: 200 OK. Returns the updated role.
Assign role to API key
Section titled “Assign role to API key”POST /api/v1/roles/assign/:keyIdBody:
{ "role_id": "uuid"}When a role is assigned, the role’s scopes override the key’s default scopes.
Response: 200 OK with {"success": true}
Remove role from API key
Section titled “Remove role from API key”DELETE /api/v1/roles/assign/:keyIdRemoves the role, reverting the key to its own default scopes.
Response: 200 OK with {"success": true}
Error responses
Section titled “Error responses”| Code | Status | Description |
|---|---|---|
UNAUTHORIZED |
401 | Missing or invalid API key / JWT |
FORBIDDEN |
403 | Insufficient scope |
NOT_FOUND |
404 | Key or role not found |
VALIDATION_ERROR |
400 | Invalid request body |
A VALIDATION_ERROR about the request body lists each failed field in error.details.issues. Each item has a path, a message, and a zod issue code.
The key and role routes also send the same issues in a top-level details field, and in error.issues. Both copies are deprecated and go away in the next major release, so read error.details.issues.