Kasimir Docs DEEN To the app →

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/v1

All 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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys 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

chat:write

POST /chat/completions, POST /embeddings, POST /assistants/{id}/run

(none)

GET /models — any valid, non-revoked key suffices

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

GET

/models

List the company's allowed chat and embedding models

any valid

POST

/chat/completions

Chat completion (streaming + non-streaming)

chat:write

POST

/embeddings

Generate embedding vectors

chat:write

POST

/assistants/{id}/run

Run a stored agent as a one-shot function

chat:write

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

model

string

yes

Model slug from GET /models

messages

array

yes

At least 1 message in OpenAI format (role + content)

stream

boolean

no

true for Server-Sent-Events streaming (default: false)

temperature

number

no

Passed on to the model

max_tokens

number

no

Passed on to the model

tools, response_format, …

–

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

model

string

yes

Slug of an embedding model, e.g. jina-embeddings-v3

input

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

messages

array

yes

1–50 messages with role (system/user/assistant) + content (string)

model_slug

string

no

Overrides the agent's default model

temperature

number (0–2)

no

Overrides the agent setting

max_tokens

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

type

Meaning

400

invalid_request_error

Invalid request body (e.g. missing model/messages)

401

authentication_error

Missing, invalid, or revoked key

402

insufficient_quota

Company monthly budget reached

403

permission_error

Missing scope, model disabled for the company, non-EU model blocked by the EU gate, model not included in the plan, or company suspended

404

not_found_error

Model/assistant not found

429

invalid_request_error

Too many requests in a short time or model busy right now

502

api_error

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 tokens

For 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.