Authentication
Issue a gateway API key or obtain a user JWT, and know which CID222 surface accepts which of the two.
- Version: 0.4
- Role: admin_user, normal_user
- Type: task
CID222 accepts two credentials on the Authorization header, and they are not interchangeable. A
gateway API key identifies a tenant for server-to-server traffic. A user JWT identifies a signed-in
person and is the only credential that reaches sessions, image analysis and document analysis.
What do I need?
- Licence
- Any
- Role
admin_usernormal_user
Prerequisites
- The appliance has completed the first-boot setup wizard.
- Issuing or revoking a gateway API key requires the admin_user role; obtaining a JWT requires only a tenant account.
- Your client can reach the gateway on TCP 443, or on TCP 3000 if you are calling the container directly.
Which credential reaches which surface
Both credentials are presented the same way, as Authorization: Bearer <credential>. CID222 tells
them apart by the cid_key_ prefix. What differs is where each one is accepted.
| Surface | Gateway API key | User JWT |
|---|---|---|
POST /chat/completions | Yes | Yes |
POST /chat/harden-prompt | Yes | Yes |
GET /chat/providers | Yes | Yes |
GET /models | Yes | Yes |
POST /api/v1/guardrails/detect | Yes | Yes |
/sessions and /sessions/:id/… | No | Yes |
/image-analysis/… | No | Yes |
/document-analysis/… | No | Yes |
/admin/…, including /admin/api-keys | No | Yes, with admin_user |
Warning
A gateway API key on /sessions, /image-analysis or /document-analysis returns 401. Those
controllers are guarded by JWT validation alone; the key is never examined. Documentation that
says otherwise is describing an intent, not the code.
Issue and use a gateway API key
Open the API key list
Select Administration → API Keys.
The page lists the tenant's existing keys with their names, scopes and expiry dates. The secret itself is not listed — CID222 stores only a SHA-256 hash of it.
Create the key
Select Create API key, name the key after the caller that will use it, and set an expiry date if the key should stop working on its own. Leaving the expiry empty means the key never expires.
The new key is displayed once, in the form cid_key_ followed by 64 hexadecimal characters —
32 random bytes rendered as hex.
Copy the key out before you close the dialog
Select Copy API Key and store the value in your secret manager or an environment variable.
Closing the dialog discards the plaintext. If you lose it, select Regenerate API Key on the key's row, which mints a new secret and immediately invalidates the old one.
Call the gateway with the key
Send the key as a bearer token.
The response is an SSE stream. A rejected key returns 401 with the message
Invalid API key.
Danger
A key scoped to a tenant group is attributed to the group's earliest-added member. Removing that user transfers the key's identity to another member instead of revoking the key. Revoke the key itself when the person who owned it leaves.
Obtain and use a user JWT
Exchange credentials for a token
Post the username and password to POST /auth/login.
The endpoint returns 200 with the token and the caller's identity.
Send the token on every subsequent request
Put access_token on the Authorization header.
The request succeeds for the tenant encoded in the token. A token for a deleted or deactivated tenant returns 401 even before it expires.
Re-authenticate when the token expires
Tokens are HS256 and live for the value of JWT_EXPIRES_IN, which defaults to 24 hours.
CID222 issues no refresh token, so an expired token is replaced by calling POST /auth/login
again.
An expired token returns 401. Treat 401 on any authenticated call as the signal to log in again and retry once.
What the token carries
| Claim | Meaning |
|---|---|
sub | The tenant id the token authenticates |
username | The account name |
role | The role at the moment the token was minted |
iat | Issue time, seconds since the Unix epoch |
exp | Expiry time, seconds since the Unix epoch |
The role claim is informational. CID222 re-reads the role from the database on every request, so
a token minted before a demotion does not keep the old role.
Roles
| Role | What it can do |
|---|---|
superadmin | Everything, across all tenants |
admin_user | Tenant, credential, filter and key administration, plus chat and sessions |
normal_user | Chat, sessions, and the analysis endpoints for its own tenant |
viewer | Read-only. Any mutating request is refused with READ_ONLY_ROLE |
auditor | Reviews the estate. Chat and session writes are refused with ROLE_NOT_FOR_CHAT |
A request authenticated with a gateway API key is given the synthetic role api_key. It is not a
role you can assign to a tenant.
Rate limiting on sign-in
POST /auth/login accepts 10 requests per minute per client IP address. POST /auth/forgot-password
and POST /auth/reset-password accept 5 per minute. Exceeding a limit returns 429.
No other endpoint is rate limited, and CID222 sends no X-RateLimit-* headers on any response.
Error shapes
The error body is not uniform, so match on the HTTP status first and on code only where it is
present.
A framework-level rejection, such as a bad password or a missing bearer token, carries no code:
A CID222 policy rejection carries a machine-readable code, and sometimes extra fields:
Verify
- Call
GET /modelswith the gateway API key. It returns 200 and a JSON array. - Call
GET /sessionswith the same gateway API key. It returns 401 — this is the expected refusal, and it confirms the key is scoped as documented. - Call
GET /sessionswith a JWT fromPOST /auth/login. It returns 200 and an array of the tenant's sessions. - Select Administration → API Keys. The key's row shows a last-used timestamp reflecting your test call.
Next steps
- API overview — every endpoint, its base path and its guard.
- Chat API — the request body and the SSE contract on
/chat/completions. - Sessions API — the JWT-only surface that keeps conversation state.
Last updated on