# Chat completions > POST /chat/completions reference: every request parameter with its support status, response and stream chunk fields, errors, limits and examples. Source: https://kiln.bricksum.com/docs/en/api-reference/chat-completions `POST https://api.bricksum.com/v1/chat/completions` Generates a model response for a conversation. The request and response follow the OpenAI Chat Completions format. Each parameter below is marked with what the gateway does with it today. ## Authentication Authentication is the same on every endpoint the gateway fronts, and is documented on the [API Reference](https://kiln.bricksum.com/docs/en/api-reference) page. ## Request body | Status | Description | | --- | --- | | supported | The gateway handles it, or a published measurement shows it works. | | passed through | Forwarded to the model server; not verified by Kiln. | | ignored | Accepted with 200, but not applied. The rest of the response is normal. | | not supported | Rejected with an error, or not working on the models named in the description. | ### Common parameters These are documented once on the [API Reference](https://kiln.bricksum.com/docs/en/api-reference) page. Listed here with what this endpoint does differently. | Field | Type | required | Default | Status | Description | | --- | --- | --- | --- | --- | --- | | `model` | string | required | | supported | Same as the shared definition. | | `messages` | array | required | | supported | An empty or missing array is rejected with 400 (empty_array). A message is a role and a content, in the shapes below. | | `messages[].role (system message)` | string | required | | supported | Set to "system". Allowed values: system | | `messages[].content (system message)` | string | required | | supported | Instructions that apply to the whole conversation. | | `messages[].role (user message)` | string | required | | supported | Set to "user". Allowed values: user | | `messages[].content (user message)` | string \| array | required | | supported | Text, or an array of content parts. The gateway forwards a parts array unchanged. Image parts (image_url) work only on models whose input includes images; see the Models page. | | `messages[].role (assistant message)` | string | required | | supported | Set to "assistant". Allowed values: assistant | | `messages[].content (assistant message)` | string \| null | | | supported | An earlier model reply. It can be null when the reply was a tool call. | | `messages[].tool_calls (assistant message)` | array | | | supported | The tool_calls array the model returned. Send it back unchanged before the matching tool message. | | `messages[].role (tool message)` | string | required | | supported | Set to "tool". Allowed values: tool | | `messages[].tool_call_id (tool message)` | string | required | | supported | The id of the tool call this result answers. | | `messages[].content (tool message)` | string | required | | supported | The result of running the tool, usually a JSON string. | | `max_tokens` | integer | | | supported | Sent to the model server as given. If generation stops at this cap (finish_reason: "length"), a non-streaming response can return empty content. | | `stream` | boolean | | `false` | supported | The gateway also reads the string "true" and the number 1 as true, and rewrites the field to a boolean before forwarding. | | `tools` | array | | | supported | Sent as OpenAI function definitions. | | `tools[].type` | string | required | | supported | Set to "function". Allowed values: function | | `tools[].function.name` | string | required | | supported | The name the model uses in tool_calls. | | `tools[].function.description` | string | required | | supported | What the function does. A function without a description is rejected with 400. | | `tools[].function.parameters` | object | | | supported | JSON Schema of the function arguments. | | `tool_choice = "auto"` | string | | | supported | Sent as the string "auto". | | `tool_choice = "required" \| {"type": "function", ...}` | string \| object | | | not supported | Forced tool choice, sent as "required" or as a named function. On qwen3-32b (measured 2026-09-26), forced tool choice fails without a clear error: tool_choice "required" returns HTTP 500 with a plain-text "Internal Server Error" body, and naming a function returns 200 with empty arguments (finish_reason "stop"). Neither is a 4xx, so do not retry it — send tool_choice "auto" only. | ### Only on this endpoint | Field | Type | required | Default | Status | Description | | --- | --- | --- | --- | --- | --- | | `stream_options` | object | | | supported | Options for a streaming response. Send it only together with stream: true. | | `stream_options.include_usage` | boolean | | `false` | supported | When true, the stream ends with one extra chunk whose choices is empty and whose usage object holds the token counts and cost. The gateway sets this flag on every streaming request to meter tokens, and removes the usage chunk when you did not ask for it. | | `parallel_tool_calls` | boolean | | | not supported | Applied only on models whose Parallel tool calls cell on the Models page is yes; qwen3-32b is not one of them. Measured on gpt-oss-120b: the request is accepted with 200, and the model calls at most one tool per reply. On qwen3-32b the parallel_tool_calls setting is not applied, which is not the same as replies holding one call: even with parallel_tool_calls: false, one reply can carry several tool calls. Answer every entry in tool_calls with its own tool message. | | `reasoning_effort` | string | | | supported | Forwarded to the model as sent. gpt-oss-120b accepts low, medium and high; whether another model honours it is not published. Higher values spend more reasoning tokens, which adds latency and output tokens. Allowed values: low, medium, high | | `temperature` | number | | | passed through | Forwarded to the model server; not verified by Kiln. Range: 0–2 | | `top_p` | number | | | passed through | Forwarded to the model server; not verified by Kiln. Range: 0–1 | | `n` | integer | | | passed through | Forwarded to the model server; not verified by Kiln. | | `seed` | integer | | | passed through | Forwarded to the model server; not verified by Kiln. | | `presence_penalty` | number | | | passed through | Forwarded to the model server; not verified by Kiln. Range: -2–2 | | `frequency_penalty` | number | | | passed through | Forwarded to the model server; not verified by Kiln. Range: -2–2 | | `stop` | string \| array | | | ignored | Accepted, but not applied. Generation continues past the stop string. | | `response_format` | object | | | not supported | json_object and json_schema do not work yet. The request is accepted with 200, but content comes back empty. To get JSON, tell the model in the prompt to reply with JSON only, then parse and validate the result in your client. | | `ignore_eos` | boolean | | | not supported | On gpt-oss models, true is rejected with 400 (unsupported_parameter) before the request reaches the model. On other models it is forwarded to the model server. | OpenAI chat parameters that are not listed here are forwarded to the model server in the same way. Kiln has not verified them. ## Response body | Field | Type | Description | | --- | --- | --- | | `id` | string | ID of the chat completion. | | `object` | string | "chat.completion". | | `created` | integer | Creation time in Unix seconds. | | `model` | string | The model ID that produced the response. | | `choices[].index` | integer | Position of the choice. | | `choices[].message.role` | string | "assistant". | | `choices[].message.content` | string \| null | The generated text. It can be empty when finish_reason is "length", and null when the reply is a tool call. | | `choices[].message.tool_calls` | array | Tool calls the model wants you to run: id, type and function.name with function.arguments as a JSON string. | | `choices[].finish_reason` | string | "stop", "length" (cut off by max_tokens) or "tool_calls". | | `usage.prompt_tokens` | integer | Input tokens. | | `usage.completion_tokens` | integer | Output tokens, including reasoning tokens. | | `usage.total_tokens` | integer | Input plus output tokens. | | `usage.prompt_tokens_details.cached_tokens` | integer | Input tokens served from the prefix cache. Present only when the model server reports it. | | `usage.cost` | number | Amount in USD deducted from credits for this request. The gateway adds it. | | `X-Neocloud-Generation-Id` | header | Response header with the ID of the usage event for this request. Store it with your logs. | ## Stream chunk | Field | Type | Description | | --- | --- | --- | | `object` | string | "chat.completion.chunk". | | `choices[].delta.content` | string | The new text of this chunk. | | `choices[].delta.tool_calls` | array | Tool call fragments. Concatenate function.arguments across chunks by index. | | `choices[].finish_reason` | string \| null | null until the last content chunk, which carries the reason. | | `usage` | object | Only in the final chunk, and only when stream_options.include_usage is true. That chunk has an empty choices array. | | `usage.cost` | number | Amount in USD deducted from credits for this request. The gateway adds it to the usage chunk. | With stream: true, each data: line carries one chunk with the fields below. Keepalive comments, data: [DONE] and disconnects are described on the [Streaming](https://kiln.bricksum.com/docs/en/streaming) page. ## Errors | Status | error.code | Cause | What to do | | --- | --- | --- | --- | | 400 | `empty_array` | messages is empty or missing. | Send at least one message. | | 400 | `unsupported_parameter` | ignore_eos: true on a gpt-oss model. | Remove ignore_eos. | | 400 | | The model server rejected the request: malformed JSON, invalid parameters, a function without a description, or more than the model's context window (the Models page lists it per model). | Fix the payload. Do not retry as sent. | | 404 | `model_not_found` | The model ID is not in the catalog. | Check the ID against GET /models. | | 429 | `rate_limit_exceeded` | The organization already has 8 requests in flight. No rate-limit headers are sent. | Back off exponentially and retry. | | 502 | | The model backend returned an error or was unreachable. The body is plain text. | Retry with exponential backoff. | | 503 | | The service is in maintenance mode, or no model backend is available. | During maintenance, wait until the Retry-After date. Otherwise retry with exponential backoff. | 401, 402, 403 and 429 come from the gateway before the request reaches this endpoint. They are listed once on the [API Reference](https://kiln.bricksum.com/docs/en/api-reference) page. Branch on the HTTP status and error.code, not on the message text. An empty error.code cell means the body carries no code. Every status code is listed on the [Errors](https://kiln.bricksum.com/docs/en/errors) page. ## Limits Context length, request body size, timeouts and the per-organization rate limits apply here as well; they are listed once on the [API Reference](https://kiln.bricksum.com/docs/en/api-reference) page. Headers and retry guidance for 429 are on the [Rate limits](https://kiln.bricksum.com/docs/en/rate-limits) page. ## Examples ### Request **cURL** ```bash curl https://api.bricksum.com/v1/chat/completions \ -H "Authorization: Bearer $KILN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-32b", "messages": [ {"role": "system", "content": "Answer briefly."}, {"role": "user", "content": "Hello!"} ], "max_tokens": 800 }' ``` **Python** ```python import os from openai import OpenAI client = OpenAI( base_url="https://api.bricksum.com/v1", api_key=os.environ["KILN_API_KEY"], ) resp = client.chat.completions.create( model="qwen3-32b", messages=[ {"role": "system", "content": "Answer briefly."}, {"role": "user", "content": "Hello!"}, ], max_tokens=800, ) print(resp.choices[0].message.content) print(resp.usage) ``` **JavaScript** ```javascript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.bricksum.com/v1", apiKey: process.env.KILN_API_KEY, }); const resp = await client.chat.completions.create({ model: "qwen3-32b", messages: [ { role: "system", content: "Answer briefly." }, { role: "user", content: "Hello!" }, ], max_tokens: 800, }); console.log(resp.choices[0].message.content); console.log(resp.usage); ``` ### Response **200 OK** ```json { "id": "chatcmpl-...", "object": "chat.completion", "created": 1710000000, "model": "qwen3-32b", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Hello! How can I help?" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 9, "completion_tokens": 12, "total_tokens": 21, "cost": 0.000001155 } } ``` **text/event-stream** ```text data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1710000000,"model":"qwen3-32b","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1710000000,"model":"qwen3-32b","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1710000000,"model":"qwen3-32b","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":12,"total_tokens":21,"cost":0.000001155}} data: [DONE] ``` **400 Bad Request** ```json { "error": { "message": "Invalid 'messages': the array must contain at least one message.", "type": "invalid_request_error", "param": "messages", "code": "empty_array" } } ```