Authentication
Lumio API keys authenticate with a bearer token in the HTTP Authorization header. The scheme match is exact and case-sensitive:
Authorization: Bearer lm_usr_your_key
Use exactly Bearer, one space, then the key. Lumio does not read API keys from X-API-Key, cookies, request bodies, or ?token= query parameters.
Key Kinds
| Prefix | Created in | Bound to | Carries user identity? | Assignable scopes |
|---|---|---|---|---|
lm_usr_ | Dashboard -> API Keys | A user and the active account | Yes | 119 account scopes plus 10 user-scoped ideas:* scopes |
lm_svc_ | Dashboard -> API Keys | The account | No | 119 account scopes |
lm_dev_ | Account -> Developer -> API Keys | A developer profile or developer team | No | The 7 extension-dev:* scopes only |
Personal and service keys are account credentials. Developer keys are publishing credentials for the extension-developer surface; they are not account credentials and do not carry an account id.
Creating Keys
Personal and service keys are created from Dashboard -> API Keys. Creating, renaming, and rotating them require the feature:apikeys plan feature and the matching apikeys:* permission. Creating or rotating a service key also requires an interactive user session; the caller must hold every scope granted to the key, including its existing scopes at rotation. Listing and deleting are deliberately not plan-gated, so a downgraded account can still audit and revoke live keys.
Developer keys are created from Account -> Developer -> API Keys after the caller has approved-developer status. They are not gated by the account plan.
The full key is returned only once, on creation or rotation. Store it immediately in your secret manager.
Storage And Lifecycle
Lumio stores API-key secrets as SHA-256 hashes, never as retrievable plaintext. Metadata responses show a non-secret key_prefix, label, timestamps, expiry, and scope list.
| Action | Personal lm_usr_* | Service lm_svc_* | Developer lm_dev_* |
|---|---|---|---|
| Expiry | Optional RFC3339 expires_at / expiresAt; expired keys stop authenticating | Optional RFC3339 expires_at / expiresAt; expired keys stop authenticating | Optional RFC3339 expires_at / expiresAt; expired keys stop authenticating |
| Rename | Updates the label only | Updates the label only | Updates the label only |
| Rotate | Replaces the stored hash and prefix; returns the new full key once | Replaces the stored hash and prefix; returns the new full key once | Not exposed as a public developer-key operation |
| Revoke | Hard delete | Hard delete | Soft revoke with revoked_at |
| Owner leaves | Stops resolving when the owning user is no longer a member or owner of the account | Keeps working because it is account-owned | Team keys keep working after the creator leaves the team |
Invalid, expired, or revoked keys resolve as anonymous credentials. REST and GraphQL then return authorization errors when a guarded operation is attempted. WebSocket upgrades can still complete as an anonymous session; the first unauthorized subscribe frame returns an error frame.
WebSocket Authentication
Server-side WebSocket clients can send the same bearer header during the upgrade:
Authorization: Bearer lm_svc_your_key
Browser WebSocket clients cannot set custom upgrade headers. Because API keys are not accepted in ?token=, browser clients must first mint a WebSocket token with POST /v1/auth/ws-token or the GraphQL issueWsToken mutation, then connect with ?token=<ws_token>.