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-03
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.ai.itsincom.org/v1 |
API key, sk-… |
your servers, scripts, the OpenAI SDKs |
| App API | https://my.ai.itsincom.org/api/v1 |
user token, from sign-in | document search (RAG), chat sessions |
An API key sent to the app API gets 401, and so does a user token sent to /v1.
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.ai.itsincom.org/v1/models \
-H "Authorization: Bearer $API_KEY"
With the OpenAI SDKs, pass the key as the API key and https://api.ai.itsincom.org/v1 as the base URL: 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.ai.itsincom.org/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "X-Siati-Tier: fast" \
-H "Content-Type: application/json" \
-d '{"model": "gemma-4-26b", "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 ITS INCOM AI offers. A request never leaves the zones of its key. The dashboard has no setting for this: write to segreteria@itsincom.it. 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 for document search, which does not exist under /v1: a call to https://api.ai.itsincom.org/v1/rag/… answers 404.
Sign in
curl https://my.ai.itsincom.org/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.ai.itsincom.org/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.ai.itsincom.org/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.ai.itsincom.org/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": …}}. Read code: some messages are in Italian.
| 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 |
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.