Authentication

Authentication

API keys for your servers and notebooks, session tokens for account management, and how to keep both safe.

Every /v1 endpoint accepts a bearer token in the Authorization header. Two kinds of token exist.

  • API keys (km_live_… or km_test_…) act for your organization. Create them in the dashboard under API keys, or with POST /v1/api-keys from a signed-in session. Use them from servers, notebooks and CI.
  • Session tokens come from POST /v1/auth/login (or an OAuth sign-in) and act for one user. They expire after expires_in seconds, seven days by default.

The header form is preferred. X-API-Key: km_live_… is also accepted, and so is an access_token query parameter for clients that cannot set headers; avoid it where you can, because URLs end up in logs.

A few endpoints manage the account itself and refuse API keys: everything under /v1/api-keys, PATCH /v1/organization (owner or admin only), PATCH /v1/auth/me and the avatar endpoints. They return 403 user_session_required when called with a key. Keys cannot create other keys.

Each key has an environment, live or test, that appears in its prefix and is recorded on the explanations it creates (metadata.api_key), so you can tell test traffic from production in the dashboard. Keys can carry an expiry (expires_in_days); revocation is immediate. GET /v1/auth/me tells you which principal a token resolves to.

Keep keys server-side Keys are stored hashed and cannot be recovered; store the secret when it is shown. Never ship a key in browser or mobile code: anyone holding it can read every model and explanation in the workspace. Rotate by creating a new key, deploying it, then revoking the old one.

from kaimei import Kaimei

# an API key, explicit or from KAIMEI_API_KEY
client = Kaimei(api_key="km_live_...")

# a dashboard session instead (account management)
client = Kaimei()
session = client.auth.login(email="[email protected]", password="...")
# the client now sends the session token; create a key for your servers
key = client.api_keys.create(name="Scoring service", environment="live")
print(key.secret)   # km_live_...  shown exactly once

who = client.auth.me()
print(who.principal, who.organization.slug)   # user kaimei-demo
# API key
curl https://api.kaimei.ai/v1/auth/me \
  -H "Authorization: Bearer km_live_fPaBvCL6kRW9pi7Uve-1fzLD9s3HP8mO"

# ... or the same key in X-API-Key
curl https://api.kaimei.ai/v1/models -H "X-API-Key: km_live_fPaBvCL6kRW9pi7Uve-1fzLD9s3HP8mO"

# session token from a password login
curl https://api.kaimei.ai/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "..."}'
# {"access_token": "eyJhbGciOi...", "token_type": "bearer", "expires_in": 604800, ...}
200 OKGET /v1/auth/me with an API key
{
  "principal": "api_key",
  "user": null,
  "organization": {
    "object": "organization",
    "id": "org_c72a301b93331f42b7b9",
    "name": "Kaimei Demo",
    "slug": "kaimei-demo",
    "plan": "free",
    "settings": {},
    "created_at": "2026-10-31T22:37:25Z"
  },
  "api_key": {
    "id": "key_6f2a151d6e1cd7ca3c8f",
    "prefix": "km_live_fPaB",
    "environment": "live"
  }
}
401 Unauthorizedno token
{
  "error": {
    "type": "authentication_error",
    "code": "missing_credentials",
    "message": "Missing credentials. Pass an API key or access token as `Authorization: Bearer <token>`."
  }
}
403 ForbiddenAPI key on an account endpoint
{
  "error": {
    "type": "permission_error",
    "code": "user_session_required",
    "message": "This endpoint requires a signed-in user session, not an API key."
  }
}