# Chat

> Send messages to Chat's models through the API, choose where the model runs, and read the answer as one OpenAI-compatible response.

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 scope `chat:complete`. Create it on the hub's **API keys** page at [hub.komplai.dk/api-access](https://hub.komplai.dk/api-access); [API keys and authentication](https://developers.komplai.dk/authentication) explains how. An organisation integration key (`kp_org_...`) gets 403 [integration_not_permitted](https://developers.komplai.dk/errors/integration_not_permitted).

## Your first call

Without `processing` and `model`, the call runs where your organisation's Chat runs, with your default model there.

```bash
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
  }'
```

```json
{
  "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](https://developers.komplai.dk/errors/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`.

```bash
curl "https://api.komplai.dk/v1/models?processing=komplai-local" \
  -H "Authorization: Bearer $KOMPLAI_TOKEN"
```

```json
{
  "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 `model` with 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](https://developers.komplai.dk/errors/model_not_enabled). The API never picks another one.
- `reasoning_effort` takes one of the model's `reasoning_efforts`; another value answers [reasoning_not_enabled](https://developers.komplai.dk/errors/reasoning_not_enabled). More reasoning uses more of the output limit.

```bash
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.

```bash
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 `model` out, or send the model the connection names.
- `reasoning_effort` takes only `none`. A connection to Anthropic takes `temperature` up to 1 and no `response_format` other than `text`.
- 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](https://developers.komplai.dk/errors/connection_not_found). A connection your administrator has not made available through the API answers [processing_choice_not_allowed](https://developers.komplai.dk/errors/processing_choice_not_allowed) on POST /v1/chat/completions, and GET /v1/models does not list it ([connection_not_found](https://developers.komplai.dk/errors/connection_not_found)). When the provider refuses the call, the code names the reason, such as [connection_unauthorized](https://developers.komplai.dk/errors/connection_unauthorized) or [connection_payment_required](https://developers.komplai.dk/errors/connection_payment_required).

[Your own AI connections](https://developers.komplai.dk/guides/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. A `max_tokens` that 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](https://developers.komplai.dk/errors/ai_credit_exhausted); a reached personal limit answers [person_ai_credit_exhausted](https://developers.komplai.dk/errors/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](https://developers.komplai.dk/errors/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](https://developers.komplai.dk/errors/invalid_input), as do tools, images and files. For streamed answers with function calls, coding clients use [Coding](https://developers.komplai.dk/tools/coding).

- A request holds at most 32 messages and 80,000 UTF-16 code units of text. `max_tokens` defaults to 2,000 and may not exceed the model's own `max_output_tokens` (GET /v1/models); with the input it must also fit the model's context, otherwise [context_limit](https://developers.komplai.dk/errors/context_limit).
- The model has 120 seconds to answer. Give your HTTP client a slightly longer timeout, for example 150 seconds.
- After [timeout](https://developers.komplai.dk/errors/timeout) or a 502 [service_unavailable](https://developers.komplai.dk/errors/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](https://developers.komplai.dk/errors/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.

```json
{
  "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](https://developers.komplai.dk/reference/chat): every field of GET /v1/models and POST /v1/chat/completions, with code samples and a console to try each call.
- [Errors](https://developers.komplai.dk/errors): every code with what to do. Request limits are on [Limits](https://developers.komplai.dk/limits).
- [Where data is processed](https://developers.komplai.dk/data-processing) compares local infrastructure, the EU cloud and your own connections.

---

https://developers.komplai.dk/tools/chat
