API Overview
Kasimir's OpenAI-compatible, EU-sovereign LLM API: authentication, endpoints, models, streaming, errors and metering.
The Kasimir API makes your company’s AI models available programmatically. It is compatible with the OpenAI API – existing tools and SDKs work once you swap base_url and the API key. The same rules apply as in the app: only the models enabled for your company, the same EU approval, your plan and the same monthly budget.
Base URL
https://kasimir.ai/api/v1All endpoints are relative to this base URL. It is exactly what you set as base_url in an OpenAI SDK.
Authentication
Every request carries a company API key in the Authorization header as a bearer token:
Authorization: Bearer kasi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxKeys start with kasi_. They are created under Administration → Access and shown in full only once at creation; afterwards the interface only shows their beginning so you can recognise them.
Treat the key like a password and only use it server-side. If it is lost, revoke it and create a new one.
Scopes
Keys carry scopes, enforced server-side:
Scope | Required for |
|---|---|
|
|
(none) |
|
A missing required scope returns 403. An invalid or revoked key returns 401. If a key sends too many requests in a short time, you get 429 – wait briefly and try again.
Endpoints at a glance
Method | Path | Purpose | Scope |
|---|---|---|---|
|
| List the company's allowed chat and embedding models | any valid |
|
| Chat completion (streaming + non-streaming) |
|
|
| Generate embedding vectors |
|
|
| Run a stored agent as a one-shot function |
|
The first three are OpenAI-shaped. assistants/{id}/run is a Kasimir-specific convenience endpoint (see below).
GET /models
Returns the models allowed for your company, OpenAI-shaped. The list already applies your company filters: disabled models are hidden, non-EU models appear only if your company has enabled them, and on the Basic plan every model except the self-hosted ones (gemma-4-31b, jina-embeddings-v3) is missing.
curl https://kasimir.ai/api/v1/models \
-H "Authorization: Bearer kasi_..."Response:
{
"object": "list",
"data": [
{
"id": "gemma-4-31b",
"object": "model",
"created": 1745971200,
"owned_by": "kasimir"
}
]
}The id field is the model slug – this exact value goes into the model field of completion and embedding calls.
Model slugs are company- and configuration-specific. Don't rely on hard-coded names — query GET /models to get the slugs valid for your key.
POST /chat/completions
Accepts an OpenAI chat body. Required fields are model and messages. The parameters of the OpenAI chat API are passed on (temperature, max_tokens, tools, response_format, …, full list under Chat Completions); the API silently ignores any other fields. This way function calling, structured outputs and finish_reason work as the chosen model supports them. Images and other media are only accepted embedded as a data: URI or base64: an https:// address in image_url returns 400, as does an image sent to a model without image understanding.
Parameters
Field | Type | Required | Description |
|---|---|---|---|
| string | yes | Model slug from |
| array | yes | At least 1 message in OpenAI format ( |
| boolean | no |
|
| number | no | Passed on to the model |
| number | no | Passed on to the model |
| – | no | Passed on to the model |
Non-streaming
curl https://kasimir.ai/api/v1/chat/completions \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{
"model": "gemma-4-31b",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain GDPR in one sentence."}
]
}'Response (OpenAI-shaped; the model field carries the slug back):
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1745971200,
"model": "gemma-4-31b",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "GDPR is..." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 24, "completion_tokens": 18, "total_tokens": 42 }
}Streaming
With "stream": true the API returns a text/event-stream in OpenAI format. It ends with the usual data: [DONE] line.
curl -N https://kasimir.ai/api/v1/chat/completions \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{
"model": "gemma-4-31b",
"messages": [{"role": "user", "content": "Count to three."}],
"stream": true
}'data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"gemma-4-31b","choices":[{"index":0,"delta":{"content":"One"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":5,"total_tokens":17}}
data: [DONE]POST /embeddings
OpenAI-shaped embeddings. Required: model (an embedding slug) and input (string or string array).
Field | Type | Required | Description |
|---|---|---|---|
| string | yes | Slug of an embedding model, e.g. |
| string | string[] | yes | Text(s) to embed |
curl https://kasimir.ai/api/v1/embeddings \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{
"model": "jina-embeddings-v3",
"input": "Sample text to embed"
}'{
"object": "list",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.0123, -0.045, "..."] }
],
"model": "jina-embeddings-v3",
"usage": { "prompt_tokens": 6, "total_tokens": 6 }
}The default embedding model's vector dimension is fixed at 1024 (jinaai/jina-embeddings-v3).
POST /assistants/{id}/run
A convenience endpoint (not an OpenAI standard) that runs an agent stored in Kasimir as a one-shot function. The agent's system prompt is prepended automatically (plus the current date); no SSE streaming, no conversation persistence, no tool-calling round-trip.
Only agents that are shared company-wide and have no home project run via the API. For private agents, agents shared only with people, teams or projects, and project agents the endpoint returns 404.
Field | Type | Required | Description |
|---|---|---|---|
| array | yes | 1–50 messages with |
| string | no | Overrides the agent's default model |
| number (0–2) | no | Overrides the agent setting |
| integer (1–32000) | no | Overrides the agent setting |
curl https://kasimir.ai/api/v1/assistants/<assistant-id>/run \
-H "Authorization: Bearer kasi_..." \
-H "Content-Type: application/json" \
-d '{ "messages": [{"role": "user", "content": "Summarize the quarterly report."}] }'{
"text": "The quarterly report shows...",
"model": "gemma-4-31b",
"usage": { "prompt_tokens": 312, "completion_tokens": 88 }
}If neither model_slug nor a default model on the agent is set, the endpoint returns 400. An unknown agent or one from another company returns 404.
Using the OpenAI Python SDK
Because the endpoints are OpenAI-shaped, you only set base_url and api_key:
from openai import OpenAI
client = OpenAI(
base_url="https://kasimir.ai/api/v1",
api_key="kasi_...",
)
# List models
for m in client.models.list().data:
print(m.id)
# Chat completion
resp = client.chat.completions.create(
model="gemma-4-31b",
messages=[{"role": "user", "content": "Explain GDPR in one sentence."}],
)
print(resp.choices[0].message.content)
# Streaming
stream = client.chat.completions.create(
model="gemma-4-31b",
messages=[{"role": "user", "content": "Count to three."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
# Embeddings
emb = client.embeddings.create(
model="jina-embeddings-v3",
input="Sample text to embed",
)
print(len(emb.data[0].embedding))Errors
Errors are OpenAI-shaped, with the correct HTTP status code, so standard SDKs surface them normally:
{ "error": { "message": "Model \"foo\" not found", "type": "not_found_error", "code": null } }HTTP |
| Meaning |
|---|---|---|
|
| Invalid request body (e.g. missing |
|
| Missing, invalid, or revoked key |
|
| Company monthly budget reached |
|
| Missing scope, model disabled for the company, non-EU model blocked by the EU gate, model not included in the plan, or company suspended |
|
| Model/assistant not found |
|
| Too many requests in a short time or model busy right now |
|
| Temporary processing error |
EU governance & sovereignty
Every API call is subject to the same EU approval as the model picker in the app: models outside the EU can only be used if your company has explicitly enabled them – otherwise you get 403.
Metering & budget
Every successful completion and embedding call is recorded with model, tokens and cost in euros and counts towards your company’s monthly budget. The cost is calculated as follows:
cost = (prompt_tokens / 1000) × price per 1,000 input tokens
+ (completion_tokens / 1000) × price per 1,000 output tokensFor streaming, usage is recorded at the end of the stream.
Budget enforcement: before every request the API checks the company’s monthly budget. Monthly spend includes all of your company’s AI costs for the current calendar month – API, chat, workflows, images, translations, meetings and more. When the budget is reached, the API returns 402. Without a budget there is no limit. Owners and admins find an API usage summary (requests, tokens, euros this month) under Administration → Access.
Set a monthly budget in Administration to enforce cost control — without a budget, API calls run unlimited.