Chat
The Chat API gives your own systems the models your organisation uses in Chat. POST /v1/chat/completions takes an OpenAI-compatible chat request with text messages and returns the whole answer as one chat.completion. You choose where the model runs: on komplai's local infrastructure, in komplai's EU cloud or through one of your organisation's own AI connections.
Before you start
- Your organisation has Chat, and you are assigned Chat.
- You have a personal API key (
kp_live_...) with the scopechat:complete. Create it on the hub's API keys page at hub.komplai.dk/api-access; API keys and authentication explains how. An organisation integration key (kp_org_...) gets 403 integration_not_permitted.
Your first call
Without processing and model, the call runs where your organisation's Chat runs, with your default model there.
curl https://api.komplai.dk/v1/chat/completions \
-H "Authorization: Bearer $KOMPLAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "system", "content": "Answer briefly."},
{"role": "user", "content": "How long do I have to appeal a decision on housing benefit?"}
],
"max_tokens": 256
}'{
"id": "chatcmpl-8c1d5e3a-2f7b-4a9c-b6e0-3d4f5a6b7c8d",
"object": "chat.completion",
"created": 1792483200,
"model": "qwen3.8-27b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Usually four weeks from the day you received the decision. The deadline is in the decision's guidance on how to appeal."
},
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 41, "completion_tokens": 33 }
}The answer is in choices[0].message.content. finish_reason: "length" means the answer reached max_tokens and is incomplete.
Choose where the model runs
processing decides where the call runs. The same values work for GET /v1/models.
processing | Where the model runs | AI usage |
|---|---|---|
komplai-local | komplai's local infrastructure in Denmark, run by komplai. Data does not leave the EU. The queue is shared with other organisations, without priority. local is accepted too. | No AI charge |
eu-cloud | komplai's EU cloud in France. Data does not leave the EU. Needs EU processing approved for Chat in your organisation. | Metered at the provider's price and counts towards the organisation's monthly AI spending cap |
connection:<id> | One of your organisation's own AI connections that its administrator has made available through the API. | Your organisation's own agreement with the provider. It does not count towards the cap. |
| left out | Where your organisation's Chat runs: local infrastructure, or EU cloud when that is what your organisation's Chat uses. | As for that processing |
The API never moves a call to other processing or to another model, also not when the chosen one fails. A choice your organisation does not allow through the API answers processing_choice_not_allowed.
Administrators decide this in the hub under AI and processing: the Processing tab (/settings/processing) sets where each tool may process text through the API, and the Connections tab (/settings/ai, the AI connections page) adds connections and switches Can be used through the API on for each one.
Models
GET /v1/models lists the models your key may use at a processing, with their context, their output limit and the reasoning efforts they take. In the EU cloud each model also carries the provider's token rates in rates.
curl "https://api.komplai.dk/v1/models?processing=komplai-local" \
-H "Authorization: Bearer $KOMPLAI_TOKEN"{
"object": "list",
"data": [
{
"id": "qwen3.8-27b",
"object": "model",
"owned_by": "komplai",
"name": "Qwen3.8 27B",
"context_tokens": 32768,
"max_output_tokens": 4000,
"reasoning_efforts": ["none", "low", "medium", "high"]
}
]
}- On local infrastructure the list holds the models your organisation has switched on, or every selectable model while it has switched on none. In the EU cloud it holds the models on your organisation's EU route. For a connection it holds the one model or router the connection names.
- Send
modelwith an id from the list at the same processing. Leave it out for your default there: the model you chose in the hub, else your organisation's default. - A model you may not use answers model_not_enabled. The API never picks another one.
reasoning_efforttakes one of the model'sreasoning_efforts; another value answers reasoning_not_enabled. More reasoning uses more of the output limit.
curl https://api.komplai.dk/v1/chat/completions \
-H "Authorization: Bearer $KOMPLAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"processing": "komplai-local",
"model": "qwen3.8-27b",
"reasoning_effort": "none",
"max_tokens": 256,
"messages": [
{"role": "user", "content": "How long do I have to appeal a decision on housing benefit?"}
]
}'Use one of your organisation's connections
A connection is your organisation's own infrastructure, such as Azure OpenAI, or its own account with a provider such as Anthropic, OpenAI, Mistral or OpenRouter. It names one model or a router. Its value for processing is on the AI connections page, under Value for processing in the API; ask your administrator for it.
curl https://api.komplai.dk/v1/chat/completions \
-H "Authorization: Bearer $KOMPLAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"processing": "connection:6d3f9a2e-1b4c-4e7d-8a5f-0c2b9e4d7a31",
"max_tokens": 256,
"messages": [
{"role": "user", "content": "How long do I have to appeal a decision on housing benefit?"}
]
}'The answer adds processing and requested_model. With a router such as openrouter/free, model is the model that actually answered and requested_model the router the connection names; komplai also records the answering model in your organisation's usage log.
- Leave
modelout, or send the model the connection names. reasoning_efforttakes onlynone. A connection to Anthropic takestemperatureup to 1 and noresponse_formatother thantext.- Where a connection processes data, who runs it and whether data stays in the EU/EEA is what your administrator stated for it on the AI connections page. The provider is not a komplai subprocessor.
- With a free model, the provider may store and use what is sent. Use it for testing, synthetic data and ideas.
- A connection id your organisation does not have answers connection_not_found. A connection your administrator has not made available through the API answers processing_choice_not_allowed on POST /v1/chat/completions, and GET /v1/models does not list it (connection_not_found). When the provider refuses the call, the code names the reason, such as connection_unauthorized or connection_payment_required.
Your own AI connections shows how an administrator adds one.
The monthly AI spending cap
Only calls to the EU cloud count towards the organisation's monthly AI spending cap. Local infrastructure and your organisation's own connections do not.
- Before a call to the EU cloud, komplai reserves a conservative estimate of the input and the whole
max_tokens. When the answer is in, the known usage is recorded and the rest released. Amax_tokensthat fits the task leaves room for other calls. - The cap applies across the organisation's apps, people and keys. An administrator can set a lower limit for the organisation and personal limits on the AI usage limits page (
/settings/ai-budget). - When the cap or the organisation's own limit is reached, calls stop with ai_credit_exhausted; a reached personal limit answers person_ai_credit_exhausted. Retrying does not help until the cap starts again next month. Local processing keeps working.
- Without a cap for the month, the EU cloud cannot run: ai_credit_not_granted.
The answer comes back whole
The Chat API does not stream. stream is always false, and stream: true answers 400 invalid_input, as do tools, images and files. For streamed answers with function calls, coding clients use Coding.
- A request holds at most 32 messages and 80,000 UTF-16 code units of text.
max_tokensdefaults to 2,000 and may not exceed the model's ownmax_output_tokens(GET /v1/models); with the input it must also fit the model's context, otherwise context_limit. - The model has 120 seconds to answer. Give your HTTP client a slightly longer timeout, for example 150 seconds.
- After timeout or a 502 service_unavailable, the call may have reached the model and used capacity. Do not send it again automatically, and turn off automatic retries of POST in your HTTP client or SDK.
- eu_cloud_refused means komplai's EU cloud refused the call. Choose local processing or a connection, and send komplai the
request_id.
Structured JSON
response_format asks for a JSON object (json_object) or JSON that follows your schema (json_schema). The answer is still text in choices[0].message.content: check finish_reason, parse it and validate it before you rely on it.
{
"processing": "komplai-local",
"model": "qwen3.8-27b",
"max_tokens": 256,
"messages": [
{"role": "user", "content": "Answer in JSON. How long do I have to appeal a decision on housing benefit?"}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "appeal_deadline",
"strict": true,
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["weeks", "source"],
"properties": {
"weeks": {"type": "integer"},
"source": {"type": "string"}
}
}
}
}
}The whole response_format may hold at most 32,768 bytes, 24 levels and 2,048 values, and a $ref must point inside the schema.
What is stored
Chat through the API stores no messages and no answers. komplai records the call's metadata: who called, when, which model and how many tokens. Each response has a Request-Id header; quote it when you ask komplai about a call.
Reference and errors
- Chat reference: every field of GET /v1/models and POST /v1/chat/completions, with code samples and a console to try each call.
- Errors: every code with what to do. Request limits are on Limits.
- Where data is processed compares local infrastructure, the EU cloud and your own connections.