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_…orkm_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_inseconds, 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, ...}{
"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"
}
}{
"error": {
"type": "authentication_error",
"code": "missing_credentials",
"message": "Missing credentials. Pass an API key or access token as `Authorization: Bearer <token>`."
}
}{
"error": {
"type": "permission_error",
"code": "user_session_required",
"message": "This endpoint requires a signed-in user session, not an API key."
}
}