Documentation

Authentication

The x-api-key header

Every request to a /v1/* or /deposits/* route (the machine-facing JSON API) must carry an x-api-key header with your tenant's API key:

curl -s -X POST https://your-gateway-host/v1/ai/complete \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "hello"}'

The gateway looks that key up against its tenant table and resolves it to your tenant before your request handler ever runs. If the header is missing, or its value doesn't match any tenant, you get back the same response either way:

{"error": "Missing or invalid API key"}

HTTP/1.1 401 Unauthorized

Both cases return an identical status and message on purpose — a distinguishable "no such key" vs. "you sent nothing at all" response would let someone probe for which keys are almost-valid. If your tenant account has been suspended by an operator, a correctly-identified key instead gets:

{"error": "This tenant's access has been suspended"}

HTTP/1.1 403 Forbidden — a different status (403, not 401) because in this case you are correctly authenticated; you're just not currently allowed to use the gateway. Suspension blocks API usage and new actions like this one; it does not affect your ability to log into and view your dashboard.

This is separate from your dashboard login

Your tenant also has a dashboard login (email + password) at /login, used only to view your account, manage deposits, and reveal/regenerate your API key in a browser. It is a completely different authentication system from the x-api-key header:

API key (x-api-key)Dashboard login (/login)
Used for/v1/*, /deposits/* — machine-to-machine JSON calls/account/* — a human using a browser
CredentialA single long-lived key stringEmail + password, exchanged for a session cookie
Where it's checkedOn every request, via the x-api-key headerOnce at login; a signed session cookie carries it afterward

Your API key never grants access to the dashboard, and your dashboard password is never accepted as an API credential — they authenticate two genuinely different kinds of caller (a script/service vs. a person in a browser), and neither can be substituted for the other.