API keys
API keys are the agent-facing equivalent of a user session. Issue one, hand it to your agent or service, and the agent gains exactly the permissions of whatever the key is bound to — narrowed by optional scope qualifiers.
What a key is bound to#
A key is bound to either a user (acts as them) or a group (acts as the group, no user attribution). Exactly one; never both. Both routes funnel through the same RBAC engine — there is no parallel permission system for keys.
Scope qualifiers#
A key's scopes array can narrow what the key is allowed to do without
changing the underlying role:
| Scope | Permits |
|---|---|
| (no scopes) | Everything the bound principal has |
docs:read |
Maps to docs.read permission |
docs:write:scaigrid |
Write only within the scaigrid namespace |
docs:write:scaigrid/v2/** |
Write only within version v2 (or a path prefix) |
docs:* |
All docs.* perms |
* |
Wildcard — same as no scopes |
Scopes only narrow; they cannot grant a permission the principal doesn't
already hold. This is enforced both for permissions globally (the
docs:write part) and per-resource (the :scaigrid/... part — checked at
write-time).
Auth header#
Same Authorization header as user JWTs; the backend disambiguates by the
scai_ prefix.
1 2 | |
Lifecycle#
- Issue in admin →
/api-keys→ New API key. - The plaintext is shown once in a one-time modal. Copy it.
- The key is identified afterwards by its prefix (
scai_live_NbZRFjU6). - Revoke by clicking Revoke — the key becomes
is_active=falseand subsequent requests get401 Invalid API key.
Audit trail#
api_key_logs records every authenticated request: endpoint, method, status,
IP, user agent. Group-bound keys log against their api_key_id, so even
without user attribution you can answer "who/what touched this".