Authentication
The agent API is authenticated by per-user bearer tokens with scoped capabilities. There is no session — every request stands alone.
Key format
Every key looks like ac_live_ followed by 64 lowercase hex characters (256 bits of entropy from the OS RNG). Mint at /agent in the AC app — plaintext is shown exactly once.
ac_live_4f9c6e8a2b3d1e7f0c5a9b8d2e6f4a3c7b1d8e5f0a9c2b6e4d7a3f8c5b9e2d6aSending the token
Pass the token in the Authorization header on every request:
Authorization: Bearer ac_live_4f9c6e8a2b3d1e7f0c5a9b8d2e6f4a3c7b1d8e5f0a9c2b6e4d7a3f8c5b9e2d6aRequests without a valid header return 401 immediately. Requests with a valid key whose scope set doesn’t cover the endpoint return 401 with a Missing scope: <name> message.
Scopes
Scopes are additive. Every key carries one or more from the set below.
readscopewhoami, get_mandate, get_holdings, get_audit.validatescopeproposescopeRotation
- Mint the replacement key alongside the old one.
- Hot-swap your agent config (the old key still works while you flip).
- Revoke the old key from
/agent— the next call from it returns401immediately.
We recommend rotating every 90 days. Expirations are enforced server-side: a key with expiresAt in the past returns 401 even if it hasn’t been revoked.
Revocation
Revocation is instant. The next call from the key returns 401 Unauthorized — Invalid or revoked key. The row stays in the database so historic audit records remain attributable to a specific key.
How verification works server-side
- Hashing. We SHA-256 the presented bearer token and look it up by
hashedKey— no plaintext comparison happens. - Replay. Tokens are stateless. Two parallel calls with the same key are both valid.
- Last-used. Every successful auth bumps
lastUsedAtso you can see staleness in the UI.
Example: failed auth
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"statusCode": 401,
"message": "Invalid or revoked key",
"error": "Unauthorized"
}