API reference
Authentication
API keys for the /v1 API, user tokens for the app API: how to get them, how to use them, what the errors mean.
Last updated: 2026-10-06
There are two APIs, and each has its own credential. Both go in the same header, Authorization: Bearer …, but they are not interchangeable.
| API | Base URL | Credential | For |
|---|---|---|---|
| Developer API, OpenAI-compatible | https://api.aitokens.ch/v1 |
API key, sk-… |
your servers, scripts, the OpenAI SDKs |
| App API | https://my.aitokens.ch/api/v1 |
user token, from sign-in | managing knowledge bases (RAG), chat sessions |
An API key sent to the app API gets 401, and so does a user token sent to /v1. Knowledge bases are on both: a key lists them and asks them questions, a user token also creates them and uploads documents. See Who can do what.
API keys
Create one
In the dashboard: API keys → give it a name, choose its default tier → Create. The key is shown once: copy it straight away. We keep only a hash of it, and its first characters so you can recognise it in the list.
export API_KEY="sk-…"
Use it
curl https://api.aitokens.ch/v1/models \
-H "Authorization: Bearer $API_KEY"
With the OpenAI SDKs, pass the key as the API key, https://api.aitokens.ch/v1 as the base URL and a model of the catalogue: see the Quickstart.
Tier and zones of a key
A key has a default tier, chosen when you create it. The dashboard cannot change it later: create a new key, or override the tier of a single request with the X-Siati-Tier header.
curl https://api.aitokens.ch/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "X-Siati-Tier: fast" \
-H "Content-Type: application/json" \
-d '{"model": "qwen3.8-27b", "messages": [{"role": "user", "content": "Hello"}]}'
Every key may use any of the four tiers. An unknown value returns 400. What a tier changes is in Tiers.
A key may also be limited to some zones; by default it can use all the zones AI Tokens offers. A request never leaves the zones of its key. The dashboard has no setting for this: write to info@daikolab.ch. See Zones.
Revoke or rotate
Dashboard → API keys → Revoke. It applies from the next request: there is no grace period. To rotate a key, create the new one, move your code to it, then revoke the old one.
A key can also carry an expiry date, set by an administrator. After that date it is refused.
App API: user tokens
The app API acts as a signed-in user, not as a key. It is the one you need to create knowledge bases and upload documents, which /v1 cannot do: a call to https://api.aitokens.ch/v1/rag/… answers 404. Asking questions works with a key too, at https://api.aitokens.ch/v1/knowledge-bases/{slug}/ask.
Sign in
curl https://my.aitokens.ch/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "…"}'
The answer carries two tokens:
| Field | What it is |
|---|---|
access_token |
goes in Authorization: Bearer on every app API request |
refresh_token |
gets you a new pair when the access token expires |
access_expires_in_seconds, refresh_expires_in_seconds |
how long each one lasts |
user |
the account: id, email, whether the email is verified |
token and expires_in_seconds repeat the access token and its lifetime, for older clients.
export USER_TOKEN="eyJ…"
curl https://my.aitokens.ch/api/v1/auth/me \
-H "Authorization: Bearer $USER_TOKEN"
Sign-in answers 429 after 10 attempts in a minute from the same address. Until the email address is confirmed, most app API endpoints answer 403 with code: email_unverified.
Refresh
curl https://my.aitokens.ch/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "eyJ…"}'
You get a new pair, with the same fields as sign-in. The refresh token you sent stops working: keep the new one. If an already used refresh token is sent again, we treat it as stolen and revoke every refresh token of the account, so the next refresh fails and the user has to sign in again.
Sign out
curl https://my.aitokens.ch/api/v1/auth/logout \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"refresh_token": "eyJ…"}'
This revokes the refresh token. The access token cannot be revoked: it stays valid until it expires, so delete it on your side.
Errors
API key
Always 401, with type: invalid_request_error and code: invalid_api_key. The message says why:
message |
Cause |
|---|---|
missing Authorization header |
no Authorization: Bearer … header |
invalid api key format |
the value is not shaped like a key (a user token, for example) |
invalid api key |
the key does not exist or has been revoked |
api key expired |
the key is past its expiry date |
account disabled |
the account the key belongs to has been disabled |
{
"error": {
"message": "invalid api key",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
An unknown X-Siati-Tier gets 400 with the message unknown tier: ….
User token
The body is {"error": {"code": …, "message": …}}. Messages are in English on every brand and say what to do; in code, read code, not the text.
| HTTP | code |
Cause |
|---|---|---|
401 |
unauthorized |
token missing, invalid or expired (refresh it), or account disabled |
403 |
email_unverified |
the email address is not confirmed yet |
401 |
invalid_credentials |
sign-in: wrong email or password |
403 |
account_disabled |
sign-in: the account is disabled. The message says to write to info@daikolab.ch to restore it |
401 |
invalid_refresh_token |
refresh: token unknown, already used, revoked or expired |
422 |
missing_refresh_token |
refresh: no token in the body |
Everything else is in Errors.