Core Concepts
Authentication
Alloovium's public API supports two credential types: long-lived API keys for server-to-server integrations, and OAuth 2.1 with PKCE for end-user agents like Claude Desktop, Cursor, and ChatGPT.
Choosing between API keys and OAuth
| Dimension | API key | OAuth 2.1 |
|---|---|---|
| Best for | Your own backend calling Alloovium | Third-party apps acting on behalf of a user |
| Identity | Fixed user + tenant at key creation | End user signs in via Clerk; token carries that user's id |
| Lifetime | Until revoked | Access token 1 hour, refresh token 30 days |
| Registration | Developer Console | Dynamic Client Registration (RFC 7591) or Developer Console |
| Flow | Send Bearer header | PKCE authorization code → token exchange → Bearer |
| Scopes | Granted at key creation | Requested at authorize; capped by the registered client scope |
API key authentication
Key format
Keys are 60 characters, prefixed by environment, and base32-encoded for readability:
textak_live_AB7K2LJ8MZ4X3WQ9VYTC5H6NPRDFG8EJUSKBHMV2YXQW3TKRP4DL ak_test_NC9PQ2MJ8MZ4X3WQ9VYTC5H6NPRDFG8EJUSKBHMV2YXQW3TKRPAA
live keys run in the production environment. test keys are for CI and local development. The prefix determines which tenant context the key executes in, but both can call any of the same capabilities.
Only the first four characters after the environment prefix (e.g. example) are stored in plaintext for display. The rest is hashed at rest; if you lose a key you must create a new one.
Sending the key
Attach it as an header header on every request. The API rejects keys in query strings, cookies, or JSON bodies.
httpGET /api/v2/vault/projects HTTP/1.1 Host: api.alloovium.com Authorization: Bearer ak_live_AB7K2LJ8MZ4X3WQ9VYTC5H6NPRDFG8EJUSKBHMV2YXQW3TKRP4DL Accept: application/json
Never put keys in client-side code
401 responses
If the header is missing, malformed, revoked, expired, or matches no key, the API returns code with a header header and one of these codes:
| Code | Meaning |
|---|---|
| missing_api_key | No Authorization header or not Bearer scheme |
| invalid_api_key | Key does not match any active credential |
| api_key_revoked | Key was revoked via the dashboard |
| api_key_expired | Key passed its expiry date |
| user_inactive | Owning user has been deactivated |
| tenant_disabled | Owning tenant is suspended |
| payment_required | Billing is past due |
Rotating keys
Create a new key, cut traffic over, then revoke the old one. Both can live simultaneously, so zero-downtime rotation is straightforward. Revocation is immediate — within a few seconds across all regions.
OAuth 2.1 with PKCE
For integrations that act on behalf of an end user — LLM agents, IDE plugins, browser extensions — use the OAuth 2.1 authorization code flow with PKCE. Alloovium follows spec strictly: every client must use PKCE; implicit flow and password grant are not supported.
Metadata discovery (RFC 8414)
Every OAuth client starts by fetching the authorization server metadata:
bashcurl https://api.alloovium.com/.well-known/oauth-authorization-server
json{ "issuer": "https://api.alloovium.com", "authorization_endpoint": "https://api.alloovium.com/api/v2/oauth/authorize", "token_endpoint": "https://api.alloovium.com/api/v2/oauth/token", "revocation_endpoint": "https://api.alloovium.com/api/v2/oauth/revoke", "registration_endpoint": "https://api.alloovium.com/api/v2/oauth/register", "response_types_supported": ["code"], "grant_types_supported": ["authorization_code", "refresh_token"], "code_challenge_methods_supported": ["S256"], "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"], "scopes_supported": [ "vault:read", "chat:read", "templates:read", "workflows:read", "meta:read" ] }
A corresponding Protected Resource Metadata document (RFC 9728) is served at path.
Dynamic Client Registration (RFC 7591)
No pre-provisioning. Any client can register itself at path:
bashcurl -sS https://api.alloovium.com/api/v2/oauth/register \ -H "Content-Type: application/json" \ -d '{ "client_name": "My Claude Desktop Integration", "redirect_uris": ["http://127.0.0.1:51234/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "none" }'
json{ "client_id": "client_d6kQ6ZrW9v3sLx2n2h0k5Q", "client_name": "My Claude Desktop Integration", "redirect_uris": ["http://127.0.0.1:51234/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "none", "scope": "vault:read chat:read templates:read workflows:read meta:read" }
DCR scopes are clamped
Dashboard-managed clients
The Developer Console lets you create both public PKCE clients and confidential clients with a one-time secret. That is the recommended path when you want a stable client registration before shipping an integration.
Authorization code with PKCE
Generate a random verifier (43–128 chars URL-safe), compute challenge, then redirect the user to the authorize endpoint:
texthttps://api.alloovium.com/api/v2/oauth/authorize ?client_id=client_d6kQ6ZrW9v3sLx2n2h0k5Q &redirect_uri=http://127.0.0.1:51234/callback &response_type=code &scope=vault:read%20chat:read &state=xyz123 &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM &code_challenge_method=S256
If the user is not already signed in, they will be redirected to Clerk to authenticate. After approval the browser is redirected back to your uri with params.
Token exchange
Exchange the code for an access + refresh token pair:
bashcurl -sS https://api.alloovium.com/api/v2/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=AUTH_CODE_FROM_REDIRECT" \ -d "client_id=client_d6kQ6ZrW9v3sLx2n2h0k5Q" \ -d "redirect_uri=http://127.0.0.1:51234/callback" \ -d "code_verifier=THE_RANDOM_VERIFIER_YOU_GENERATED"
json{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "rt_9c1e4e4ebf97a4b5e76cf9c85e2f09bb", "scope": "vault:read chat:read" }
The access token is a signed JWT with Token exchange, tid, client_id, and scope claims. Attach it as bearer on every API call.
Refreshing access
bashcurl -sS https://api.alloovium.com/api/v2/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=refresh_token" \ -d "refresh_token=rt_9c1e4e4ebf97a4b5e76cf9c85e2f09bb" \ -d "client_id=client_d6kQ6ZrW9v3sLx2n2h0k5Q"
Refresh tokens rotate on every use. The old one is immediately invalidated, and the response contains a new refresh token. If a rotated token is ever replayed, Alloovium revokes the whole chain — a standard OAuth 2.1 defense against token theft.
Confidential clients
If your registered client uses basic or post, the token endpoint requires the matching secret on every exchange and refresh request. Store that secret server-side only.
Revocation (RFC 7009)
bashcurl -sS https://api.alloovium.com/api/v2/oauth/revoke \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "token=rt_9c1e4e4ebf97a4b5e76cf9c85e2f09bb" \ -d "client_id=client_d6kQ6ZrW9v3sLx2n2h0k5Q"
OAuth-compatible capabilities
Not every capability accepts OAuth tokens. Some handlers rely on full ORM fields that are only present for API-key principals; those return code when called with a Bearer JWT. You can check support on each capability page or the OAuth column in the capability matrix. The maintained capability docs are the clearest source for which operations currently accept OAuth bearer tokens.
Scopes
Scopes are the authorization vocabulary for the public API. Both API keys and OAuth tokens carry a set of scopes; handlers require specific ones.
| Scope | Grants |
|---|---|
| vault:read | List projects, get projects, list documents, get documents, search |
| vault:write | Create projects, upload documents |
| chat:read | List conversations, fetch conversation history |
| chat:write | Ask chat (streaming and non-streaming) |
| templates:read | Poll template-fill job status |
| templates:write | Start template-fill jobs |
| workflows:read | List workflows, get run status |
| workflows:write | Start workflow runs |
| meta:read | Reserved; not currently required by any capability |
meta.whoami is scope-free
path endpoint requires any valid credential but no specific scope, so health checks always succeed.When a handler rejects a call for missing scopes, you get code with a field array in the problem body — see Errors.
Storage recommendations
- Store API keys in a secrets manager (AWS Secrets Manager, HashiCorp Vault, Doppler, 1Password). Never commit them to source control.
- OAuth refresh tokens should be encrypted at rest. Rotate them every use; the old one is dead immediately.
- For browser-based integrations, never embed an API key. Use OAuth PKCE with a
typeclient and a localhost loopback redirect URI.