Skip to content

Authentication

Two credentials, for two different callers.

Credential Header / transport For Grants
API key Authorization: Bearer vk_live_… Agents, SDK, MCP, servers Organization scope
Session JWT venkai_session httponly cookie The dashboard, in a browser Organization and user scope

Most endpoints accept either. A few — /api/auth/me, API-key management, team management — require a session and explicitly reject API keys:

{"detail": "This endpoint requires a dashboard session; API keys are not accepted here."}

That is by design: a key cannot mint or revoke keys.

API keys

Create

curl -sX POST https://api.venkai.fr/api/auth/api-keys \
  -H 'Content-Type: application/json' -b cookies.txt \
  -d '{"label":"production-agent","environment":"production"}'
{
  "id": "key_b8f08563afb5",
  "org_id": "org_1f200d429c50",
  "label": "production-agent",
  "environment": "production",
  "api_key": "vk_live_EXAMPLE_KEY_NOT_A_REAL_CREDENTIAL_0000000000"
}
Field Constraint
label ≤ 100 chars, default "default"
environment productionvk_live_ · developmentvk_test_

Returned once

Only a hash is stored. GET /api/auth/api-keys lists keys but never returns the raw value again.

Both prefixes authenticate identically and reach the same data. The prefix is a labelling convention for humans and log scrubbers — not a permission boundary and not a separate environment.

List / revoke / rotate

curl -s  https://api.venkai.fr/api/auth/api-keys                      -b cookies.txt
curl -sX DELETE https://api.venkai.fr/api/auth/api-keys/key_b8f08563afb5 -b cookies.txt
curl -sX POST  https://api.venkai.fr/api/auth/api-keys/key_b8f08563afb5/rotate -b cookies.txt

rotate revokes the old key and mints a new one with the same label, returning the new raw key. There is no grace period — the old key stops working immediately, so deploy the new one first if you cannot tolerate a gap.

Sessions

curl -sX POST https://api.venkai.fr/api/auth/register \
  -H 'Content-Type: application/json' -c cookies.txt \
  -d '{"email":"you@example.com","password":"a-real-password","organization":"Acme"}'
{
  "token": "eyJhbGciOiJIUzI1NiIs…",
  "user": {"id": "usr_…", "email": "you@example.com"},
  "organization": {"id": "org_…", "name": "Acme"}
}

The same JWT is returned in the body and set as an httponly venkai_session cookie (Secure only when the server runs with VENKAI_ENV=production).

Endpoint Purpose
POST /api/auth/login {email, password} → token + cookie
POST /api/auth/logout Clears the cookie
GET /api/auth/me {id, email, org_id}
POST /api/auth/forgot-password Sends a reset mail. Rate-limited more strictly than general traffic.
POST /api/auth/reset-password {token, password} — password ≥ 8 chars
GET /api/auth/reset-password/verify Check a reset token before showing the form

Registration requires a password ≥ 6 characters; reset requires ≥ 8. A password valid at signup can be too short to re-set — a real inconsistency, noted here rather than smoothed over.

OAuth Beta

curl -s https://api.venkai.fr/api/auth/oauth/providers

Returns the providers configured on this deployment, which depends on its environment — do not hard-code the list. GET /api/auth/oauth/{provider} starts the flow; the callback exchanges {code, state, code_verifier} (PKCE) for a session.

OAuth produces a dashboard session, not an API key. Agents still need a key.

Failure modes

Status Meaning
401 Not authenticated No credential at all
401 Invalid API key Key unknown or revoked
401 User not found Valid JWT for a deleted user
401 …requires a dashboard session… API key used on a session-only endpoint
429 Rate limited — honour Retry-After

An unauthenticated read is a 401, never an empty result — verified by the docs test suite. If you get {"memories": []}, you are authenticated and the project really is empty.

Practices

  • Keep keys in a secret store or .env, never in source control.
  • One key per deployed component: revoking one then does not take down the rest.
  • Use vk_test_ in non-production so log scrubbing and greps can tell them apart.
  • Never ship a key to a browser. CORS is restricted, but a key in client-side JavaScript is a published key.