Kasimir Docs DEEN To the app →

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

POST

URL

https://kasimir.ai/api/v1/chat/completions

Auth

Authorization: Bearer kasi_…

Required scope

chat:write

Content-Type

application/json

Response

application/json (sync) or text/event-stream (streaming)

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_abcdef0123456789abcdef0123456789

Each 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

model

string

yes

Kasimir model slug (see "Models"). Not the OpenAI model name.

messages

array

yes

At least 1 message in OpenAI chat format ({ role, content, … }).

stream

boolean

no

true → Server-Sent-Events stream. Defaults to false.

temperature, top_p, max_tokens, stop, tools, tool_choice, response_format, seed, …

various

no

Passed on to the model (full list above). n must be between 1 and 8.

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]
Request flow: auth and governance gates before the proxy, with usage metering after the response.
Request flow: auth and governance gates before the proxy, with usage metering after the response.

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

type

Meaning

400

invalid_request_error

Invalid body (missing model/messages, empty messages array).

401

authentication_error

Bearer token missing, key unknown or revoked.

402

insufficient_quota

Company monthly budget reached.

403

permission_error

Missing chat:write scope, model disabled for the company, non-EU model not enabled, model not included in the plan or company suspended.

404

not_found_error

Model slug unknown or inactive.

429

invalid_request_error

Too many requests in a short time with this key, or the model is busy right now.

5xx

api_error

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_tokens

Python — 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:
        raise

The same authentication, governance, and metering apply to:

Endpoint

Purpose

GET /api/v1/models

OpenAI-compatible list of the chat and embedding models allowed for your company ({ object: "list", data: [{ id, object, created, owned_by }] }). No scope required (just a valid key).

POST /api/v1/embeddings

OpenAI-compatible embeddings (model + input). Requires the chat:write scope.

POST /api/v1/assistants/{id}/run

Runs a company-wide shared Kasimir agent via API.

curl https://kasimir.ai/api/v1/models \
  -H "Authorization: Bearer $KASIMIR_API_KEY"