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": ["models:read"],
"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 | Org API scopes. Defaults to ["models:read"]. |
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
Keys are minted with org API scopes. Defaults and available scopes:
| Scope | Grants |
|---|---|
models:read | List models (GET /api/v1/models). Default for new keys. |
terminal | Authenticate the Alumia CLI via device login. |
billing:machine:charge | Create machine payments (org owner/admin only). |
Selected REST prefixes enforce scopes (/api/v1/models, /api/v1/files, /api/v1/connections, /api/v1/api-keys, /api/v1/billing/machine-payments). Other /api/v1 routes inherit the owning user's org permissions when called with a valid key.
Hosted MCP (https://app.alumia.com/mcp) applies a separate MCP-layer read / write check on each tool — distinct from org API scopes above. See Connect via MCP.
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.
Browser-only mutations
Some routes reject API-key auth even when the key belongs to an org owner. Examples include workspace create/delete, project share links, skill and guardrail edits, org settings updates, memory deletes, and adding session participants. Automate these from the dashboard (with step-up) or extend your integration to use a browser session — not an API key.
Dashboard auth (passkeys and 2FA)
API keys authenticate programmatic /api/v1 calls. Dashboard login uses email/password, magic link, OAuth, TOTP, recovery codes, and WebAuthn passkeys. Sensitive mutations require step-up within about ten minutes — see Passkeys and 2FA.
| Auth surface | Use for |
|---|---|
API key (Authorization: Bearer alm_…) | Automation, MCP, most CRUD reads/writes |
| Browser session + step-up | API key mint/revoke, project sharing, skills, guardrails, workspace mutations |
PATCH /api/v1/api-keys/:id
Rename a key or update its scopes array. Requires step-up when scopes change.
CLI device login
The @hasna/alumia CLI uses a browser approval flow instead of pasting API keys on first run. Endpoints (rate-limited per IP):
| Method | Path | Description |
|---|---|---|
POST | /api/v1/auth/device | Start device login; returns a browser approval URL. |
GET | /api/v1/auth/device | Poll device login code status. |
POST | /api/v1/auth/device/approve | Approve from a signed-in browser session (requires step-up). |
POST | /api/v1/auth/device/token | Exchange an approved code for a one-time terminal API key. |
Approval uses the same fresh passkey or TOTP step-up requirement as API key minting. See Terminal CLI.
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. |