Create API Key
Generate a new API key with restricted permissions for programmatic access.
POST /api/api-keys
Create a new API key for the authenticated user in the active tenant. The plaintext key is returned only once in the response — store it securely.
Auth: Required — api-key:manage permission
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable label for the key (max 255 characters) |
permissions | string[] | Yes | List of permission names to grant (minimum 1). Must be a subset of your current permissions; api-key:manage cannot be assigned. Editable later with PUT /api/api-keys/:id/permissions |
expiresAt | string | No | ISO 8601 expiration date. Key is automatically rejected after this date |
scope | string | Yes | Scope of the key: project or tenant (see below) |
projectId | UUID | null | Conditional | Project the key belongs to. Required with scope: "project"; not allowed with scope: "tenant" (null is accepted) |
Key scope
The scope is chosen when the key is created and decides how the key picks the project it works in:
| Scope | Key | Project of each request |
|---|---|---|
project | Project key | Always the key's project. The key does not need the x-project-id header; a header naming another project responds 403 on project-scoped and project-optional endpoints |
tenant | Tenant key | Any project of the tenant: declared with the x-project-id header on project-scoped endpoints, as a user does. On project-optional endpoints the header is optional and narrows the request to that project |
In both cases the tenant comes from the key. See Integration → Authentication for how each key is used.
Example
curl -X POST https://api.reten.ai/api/api-keys \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline Key",
"permissions": ["commerce:manage", "activity:view"],
"expiresAt": "2026-12-31T23:59:59Z",
"scope": "project",
"projectId": "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34"
}'const response = await fetch("https://api.reten.ai/api/api-keys", {
method: "POST",
headers: {
"Authorization": "Bearer <token>",
"x-tenant-id": "<tenant-id>",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "CI/CD Pipeline Key",
permissions: ["commerce:manage", "activity:view"],
expiresAt: "2026-12-31T23:59:59Z",
scope: "project",
projectId: "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34",
}),
});
const data = await response.json();
const plaintextKey = data.plaintextKey; // Store securely — returned only onceimport requests
response = requests.post(
"https://api.reten.ai/api/api-keys",
headers={
"Authorization": "Bearer <token>",
"x-tenant-id": "<tenant-id>",
},
json={
"name": "CI/CD Pipeline Key",
"permissions": ["commerce:manage", "activity:view"],
"expiresAt": "2026-12-31T23:59:59Z",
"scope": "project",
"projectId": "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34",
},
)
data = response.json()
plaintext_key = data["plaintextKey"] # Store securely — returned only onceResponse 201 Created
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "CI/CD Pipeline Key",
"keyPrefix": "rtn_sk_a1b2c3d4",
"plaintextKey": "rtn_sk_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4",
"userId": "550e8400-e29b-41d4-a716-446655440000",
"tenantId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"projectId": "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34",
"isActive": true,
"permissions": ["commerce:manage", "activity:view"],
"expiresAt": "2026-12-31T23:59:59.000Z",
"lastUsedAt": null,
"createdAt": "2026-02-26T14:30:00.000Z",
"revokedAt": null
}The plaintextKey field is only included in the creation response. It cannot be retrieved again — if lost, revoke the key and create a new one.
The response does not include scope: the key's scope is read from projectId — the project's UUID for a project key, null for a tenant key. The same holds for List API Keys.
Error Responses
| Status | Description |
|---|---|
400 | Validation error — missing fields, empty or duplicated permissions array, or invalid permission names |
400 | scope missing, or not one of tenant / project |
400 | scope: "project" without a valid projectId — Una key de alcance project requiere el projectId (UUID) de un proyecto del tenant. |
400 | scope: "tenant" with a projectId — Una key de alcance tenant no lleva projectId. |
401 | Missing or invalid authentication token |
403 | The requested set includes a permission you do not currently hold — Cannot assign permissions you do not have: … |
403 | The requested set includes api-key:manage — Cannot assign api-key:manage to an API key |
404 | projectId is not a project of the tenant — El proyecto no existe. |