API: Chat Completions
Kasimir's OpenAI-compatible chat completions endpoint with bearer API keys, streaming, model governance, and usage metering.
At https://kasimir.ai/api/v1 Kasimir provides an OpenAI-compatible interface. POST /api/v1/chat/completions accepts the same request body as the OpenAI Chat Completions API. Your company’s rules apply: company API key, enabled models only, EU approval and monthly budget. Existing OpenAI SDKs work without code changes – just set base_url and api_key.
Endpoint
Method |
|
URL |
|
Auth |
|
Required scope |
|
Content-Type |
|
Response |
|
Authentication
Every call is authenticated with a company API key passed in the Authorization: Bearer <secret> header. Keys use the format kasi_<32-chars> and are created in the Kasimir UI under Administration → Access by workspace owners or admins.
The full key is shown only once – right after creation. Kasimir cannot display it again afterwards. If it is lost, revoke it and create a new one.
Each key carries scopes. Newly created keys default to chat:read and chat:write. Chat completions require chat:write — without it the API returns 403.
# Fails with 401 if the header is missing or the key is invalid/revoked.
Authorization: Bearer kasi_abcdef0123456789abcdef0123456789Each successful call updates the key’s “last used” date, so unused keys are easy to spot in the admin UI.
Request body
The body follows the OpenAI schema. model and messages are required. Only these fields of the OpenAI chat API are passed on: temperature, top_p, max_tokens, max_completion_tokens, stop, n, presence_penalty, frequency_penalty, seed, response_format, tools, tool_choice, parallel_tool_calls, user, logit_bias, logprobs, top_logprobs and reasoning_effort. The API silently ignores any other fields.
Field | Type | Required | Description |
|---|---|---|---|
|
| yes | Kasimir model slug (see "Models"). Not the OpenAI model name. |
|
| yes | At least 1 message in OpenAI chat format ( |
|
| no |
|
| various | no | Passed on to the model (full list above). |
In streaming mode Kasimir automatically includes the usage information at the end – you don’t need to set stream_options yourself.
Images, audio and files in messages are only accepted embedded – as a data: URI or base64. An https:// address (e.g. in image_url) returns 400: fetch remote media yourself and send it base64-encoded. An image sent to a model without image understanding also returns 400.
Models
model expects a Kasimir slug, not the provider’s model name. The same rules apply as in the app’s model picker:
The model must be active for your company and of type
chat.Models disabled for your company are not callable → 403.
Non-EU models are only available if your company has enabled them → otherwise 403.
On the Basic plan only the self-hosted models are callable; all others return 403.
GET /api/v1/models lists the slugs valid for your key (see below). The response always returns the Kasimir slug in the model field.
Response (synchronous)
With stream: false (or omitted), the response is a complete OpenAI chat-completion object. The model field is normalized to the Kasimir slug.
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1750000000,
"model": "gemma-4-31b",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hi! How can I help?" },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 18, "completion_tokens": 9, "total_tokens": 27 }
}Response (streaming)
With stream: true the endpoint responds with Content-Type: text/event-stream in OpenAI format (data: {…} lines, ending with data: [DONE]).
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"gemma-4-31b","choices":[{"index":0,"delta":{"role":"assistant","content":"Hi"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"gemma-4-31b","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":18,"completion_tokens":9,"total_tokens":27}}
data: [DONE]Budget & usage metering
Every call is checked against the company's monthly budget. If a monthly budget is set and your company's total AI spend for the current calendar month (API, chat, workflows and everything else) has reached it, the API returns 402. Without a budget there is no limit.
Every call is recorded with model, tokens and cost in euros and counts towards the monthly budget. For streaming this happens at the end of the stream – on early abort for the tokens generated up to that point.
Cost = (prompt_tokens / 1000) × price_input + (completion_tokens / 1000) × price_output, rounded to 6 decimals, in EUR. Prices come from the model configuration.
Errors
Errors are returned in the OpenAI error schema so off-the-shelf SDKs surface them normally:
{ "error": { "message": "Missing scope: chat:write", "type": "permission_error", "code": null } }HTTP status |
| Meaning |
|---|---|---|
|
| Invalid body (missing |
|
| Bearer token missing, key unknown or revoked. |
|
| Company monthly budget reached. |
|
| Missing |
|
| Model slug unknown or inactive. |
|
| Too many requests in a short time with this key, or the model is busy right now. |
|
| Temporary processing error. |
If an error occurs after a stream has already started, it can no longer be reshaped into the JSON error schema — the connection simply drops. Handle stream interruptions on the client side.
Examples
curl — synchronous
curl https://kasimir.ai/api/v1/chat/completions \
-H "Authorization: Bearer $KASIMIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemma-4-31b",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Write a one-liner about data privacy." }
],
"temperature": 0.7
}'curl — streaming
curl -N https://kasimir.ai/api/v1/chat/completions \
-H "Authorization: Bearer $KASIMIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemma-4-31b",
"messages": [{ "role": "user", "content": "Count from 1 to 5." }],
"stream": true
}'Python — OpenAI SDK (synchronous)
Since Kasimir is OpenAI-compatible, you only need to set base_url and api_key.
from openai import OpenAI
client = OpenAI(
base_url="https://kasimir.ai/api/v1",
api_key="kasi_…", # your Kasimir API key
)
resp = client.chat.completions.create(
model="gemma-4-31b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "List three benefits of EU hosting."},
],
temperature=0.7,
)
print(resp.choices[0].message.content)
print(resp.usage) # prompt_tokens / completion_tokens / total_tokensPython — OpenAI SDK (streaming)
from openai import OpenAI
client = OpenAI(base_url="https://kasimir.ai/api/v1", api_key="kasi_…")
stream = client.chat.completions.create(
model="gemma-4-31b",
messages=[{"role": "user", "content": "Explain RAG in two sentences."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content if chunk.choices else None
if delta:
print(delta, end="", flush=True)
print()Error handling (Python)
from openai import OpenAI, APIStatusError
client = OpenAI(base_url="https://kasimir.ai/api/v1", api_key="kasi_…")
try:
client.chat.completions.create(
model="gemma-4-31b",
messages=[{"role": "user", "content": "Hi"}],
)
except APIStatusError as e:
if e.status_code == 402:
print("Monthly budget reached — please raise the budget.")
elif e.status_code in (401, 403):
print("Auth/scope problem:", e.message)
else:
raiseRelated endpoints
The same authentication, governance, and metering apply to:
Endpoint | Purpose |
|---|---|
| OpenAI-compatible list of the chat and embedding models allowed for your company ( |
| OpenAI-compatible embeddings ( |
| Runs a company-wide shared Kasimir agent via API. |
curl https://kasimir.ai/api/v1/models \
-H "Authorization: Bearer $KASIMIR_API_KEY"