Authentication
API keys, scopes, budgets, and rotation for the OwnLLM public API.
Every request to the OwnLLM API needs an API key. Keys are scoped to a single user and a single tenant; they expire on rotation, on user deactivation (manual or via SCIM), and on the optional TTL the user sets at creation.
Key format
sk-ownllm-<24-char-secret>The sk-ownllm- prefix is visible — it's how we know the key
belongs to OwnLLM and route it to the right path. The 24-character
secret is the actual credential and is never stored in plain
text on our side: we hash it (sha256) at creation and only store
the hash.
You see the full key once, at creation time, in the modal that generates it. Copy it then; we cannot retrieve it later.
Generate a key
In the chat web at https://<your-slug>.ownllm.app:
- Go to Profile → API keys.
- Click Generate a key.
- Pick a name (a short label so you remember which tool uses it), scopes (which models the key can use), an optional monthly budget in cents, and an optional expiration.
- Click Generate. Copy the key.
Use the key
curl https://acme-prod.ownllm.app/v1/chat/completions \
-H "Authorization: Bearer sk-ownllm-..." \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.3-70b",
"messages": [{"role": "user", "content": "Hello"}]
}'The Authorization: Bearer <key> header is the only auth shape we
accept. We do not look at ?api_key= query params or
X-API-Key headers.
Scopes
A scope is a model id (llama-3.3-70b) the key is allowed to call.
By default, a new key is scoped to every model the tenant has
installed. Tighten this at creation if you want to restrict a key
to, say, only the code model:
scopes: ["model:qwen2.5-coder:32b"]The scope check happens before the request is forwarded. A key
without model:<id> (or the wildcard model:*) gets a 403 with
scope_required.
Budgets
A monthly budget caps how many tokens a key can spend in a calendar month. Budgets are tracked in cents internally — we use a flat internal cost-per-token table per model so a single budget applies across all models the key can call.
When a request would push the key over budget, we return:
{
"error": {
"type": "rate_limit_exceeded",
"code": "budget_exceeded",
"message": "Monthly budget of 5000c exceeded. Bump the budget in Profile → API keys."
}
}See Rate limits for the difference between budgets and per-second rate limits.
Expiration
You can set an expiration when generating a key (90 days, 6 months,
1 year, custom). Expired keys return 401 with key_expired.
Generate a fresh one — you don't need to remove the old one
explicitly, but doing so keeps the key list tidy.
Rotation
To rotate:
- Generate a new key with the same scopes and budget.
- Update the client to use the new key.
- Revoke the old key from Profile → API keys.
The site does not support an "in-place" rotation that keeps the same key string — that would defeat the purpose.
Revocation
A key is revoked the moment you click Revoke in the UI:
- The hash is wiped on our side.
- In-flight requests using the key are not cancelled but they return their last byte and then can't issue follow-ups.
When a user is deactivated (manual, or SCIM deprovision in Enterprise), all their keys are revoked automatically.
Per-key audit
Every request the key makes is recorded in audit logs at
Admin → Audit (the admin web). The recorded fields are:
timestamp, user, key prefix (last 6 chars), model, tokens in/out,
duration, via (api). Prompt and completion content are not
stored — this is metadata only.