Alloovium

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

DimensionAPI keyOAuth 2.1
Best forYour own backend calling AllooviumThird-party apps acting on behalf of a user
IdentityFixed user + tenant at key creationEnd user signs in via Clerk; token carries that user's id
LifetimeUntil revokedAccess token 1 hour, refresh token 30 days
RegistrationDeveloper ConsoleDynamic Client Registration (RFC 7591) or Developer Console
FlowSend Bearer headerPKCE authorization code → token exchange → Bearer
ScopesGranted at key creationRequested 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:

text
ak_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.

http
GET /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

Keys grant full server-to-server access to the tenant. If you need end-user authentication in a browser or native app, use OAuth 2.1.

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:

CodeMeaning
missing_api_keyNo Authorization header or not Bearer scheme
invalid_api_keyKey does not match any active credential
api_key_revokedKey was revoked via the dashboard
api_key_expiredKey passed its expiry date
user_inactiveOwning user has been deactivated
tenant_disabledOwning tenant is suspended
payment_requiredBilling 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:

bash
curl 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:

bash
curl -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

Dynamically registered clients are always issued read-only scopes. To obtain a confidential client secret or pre-provision a client in advance, use the Developer Console.

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:

text
https://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:

bash
curl -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

bash
curl -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)

bash
curl -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.

ScopeGrants
vault:readList projects, get projects, list documents, get documents, search
vault:writeCreate projects, upload documents
chat:readList conversations, fetch conversation history
chat:writeAsk chat (streaming and non-streaming)
templates:readPoll template-fill job status
templates:writeStart template-fill jobs
workflows:readList workflows, get run status
workflows:writeStart workflow runs
meta:readReserved; not currently required by any capability

meta.whoami is scope-free

The 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 type client and a localhost loopback redirect URI.