Authentication
Mint, use, and revoke API keys for programmatic access to Alumia.
Alumia API keys are organization-scoped Bearer tokens issued to a specific user. They authenticate every request to /api/v1/* and inherit the org and workspace permissions of the user who minted them.
Key format
Keys follow the pattern:
alm_<env>_<48-hex-chars>
envislivewhen minted in production,testotherwise.- The trailing 48 hex characters come from 24 random bytes generated server-side.
- The first 12 characters (
alm_<env>_) are stored as aprefixand surfaced on list endpoints so you can recognize a key without seeing its secret. - The full key is hashed with SHA-256 before storage. The plaintext is returned once, in the create response.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/api-keys | List the caller's non-revoked keys. |
POST | /api/v1/api-keys | Mint a new key. Requires step-up authentication. |
DELETE | /api/v1/api-keys/:id | Revoke a key by ID. |
GET /api/v1/api-keys
Returns metadata for keys belonging to the authenticated user in the current org. Plaintext keys and hashes are never returned.
Response:
{
"success": true,
"data": {
"items": [
{
"id": "uuid",
"name": "CI deploy bot",
"prefix": "alm_live_a1",
"scopes": [],
"lastUsedAt": "2026-04-30T12:01:00.000Z",
"createdAt": "2026-03-01T00:00:00.000Z"
}
]
}
}
POST /api/v1/api-keys
Creates a new key. The plaintext key field appears only in this response.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Human-readable label. |
scopes | string[] | no | Reserved. Defaults to []. |
Response (201):
{
"success": true,
"data": {
"id": "uuid",
"name": "CI deploy bot",
"prefix": "alm_live_a1",
"key": "alm_live_a1b2c3...full_secret",
"createdAt": "2026-05-02T10:00:00.000Z"
}
}
This route requires step-up authentication: the caller must have re-authenticated recently (TOTP, passkey, or password) within the session step-up window. API-key callers cannot mint other API keys; minting is only available from a logged-in dashboard session.
A token-bucket rate limit applies per userId + IP. Exceeding the limit returns BAD_REQUEST with the message Too many requests.
DELETE /api/v1/api-keys/:id
Revokes the key by setting revokedAt. Subsequent requests that present the revoked key are rejected with UNAUTHORIZED because the lookup filters on revokedAt IS NULL.
Response:
{ "success": true, "data": { "revoked": true } }
A revoked or unknown ID returns 404 NOT_FOUND.
Scopes
The scopes field is persisted but not yet enforced; all keys carry the full permissions of their owning user. Treat this field as a forward-compatibility hook and do not rely on it for least-privilege today.
Rotation and revocation
Keys do not expire automatically. To rotate:
- Mint a new key.
- Deploy the new key to your client.
- Revoke the old key once the rollout completes.
Revocation is immediate. The next request authenticated with the revoked key will fail with 401 UNAUTHORIZED.
Using a key
curl https://alumia.com/api/v1/agents \
-H "Authorization: Bearer alm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"The key alone identifies both the org and the user. Do not send a separate org header. Keep keys server-side — never embed them in browser bundles, mobile apps, or public repositories.
Errors
| Code | When |
|---|---|
UNAUTHORIZED | Header missing, malformed, or key revoked. |
BAD_REQUEST | Missing name on create, or rate limit exceeded. |
FORBIDDEN | Step-up authentication has not been performed in this session. |
NOT_FOUND | Key ID does not belong to the caller's org. |