Authentication & API keys

The header

Every /v1/* gateway call (except GET /v1/models, which also accepts the console session cookie) requires a Bearer key:

Authorization: Bearer tak_your_key_here

A missing header returns 401 missing_key; an unknown, revoked, or inactive key returns 401 bad_key. See Errors for the full envelope shape.

Key format

A minted key looks like:

tak_8sK3vQmZx1pR7t9WnL2yF...

tak_ is the configured prefix; the rest is 32 random bytes, URL-safe base64 encoded. Only a SHA-256 hash of the full secret is ever stored, the plaintext is returned exactly once, in the mint response below, and cannot be retrieved again. The console shows the key’s stored prefix (the first several characters) afterwards, enough to tell keys apart without exposing them.

Minting a key

Keys are minted from the console by an administrator, or by calling the admin API directly. This surface is cookie-authenticated (an admin console session), separate from the Bearer-key gateway surface above, it is not something an end-user application calls.

curl https://ai.rcfi.co.ke/v1/keys \
  -H "Cookie: taifa_ai_session=..." \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0d3a...-uuid",
    "name": "revenue-bot-prod",
    "allowed_models": ["ministral-3:3b", "nomic-embed-text"],
    "requests_per_minute": 120,
    "monthly_token_budget": 5000000
  }'

allowed_models is validated against every model the gateway currently serves (local + cloud); an unknown id is rejected with a 400 and the offending names. Leave it empty to allow every served model. Omitted limits fall back to the gateway’s defaults: 120 requests/minute and a 5,000,000-token monthly budget (0 disables the budget check entirely).

Revoking a key

curl -X DELETE https://ai.rcfi.co.ke/v1/keys/b8e1...-uuid \
  -H "Cookie: taifa_ai_session=..."

This sets revoked_at rather than deleting the row, so the key stays in the audit trail. Revoking an already-revoked key returns 409. There is no rotation endpoint, mint a replacement key and revoke the old one.

Next: how a key’s allowed_models interacts with local vs. cloud routing.