---
title: "Create chat completions"
method: POST
path: "/v1/chat/completions"
---

# Create chat completions

`POST /v1/chat/completions`

## Request

- Base URL: `https://api.runbear.io`
- URL: `https://api.runbear.io/v1/chat/completions`
- Auth: HTTP bearer

Generate completions for a conversation based on the assistant's capabilities and conversation history.

## Request body

- CreateChatCompletionsRequest
  - `assistant_id` string, uuid, required: Unique identifier of the assistant
  - `messages` ChatMessage[], required: Array of conversation messages for context. The last message is the turn to answer, so at least one is required.
    - `role` 'user' | 'assistant', required: Role of the message sender
    - `content` string, required: Content of the message
    - `id` string: Optional message identifier
    - `attachments` object[]: Files attached to this message
      - `name` string, required: Name of the attached file
      - `url` string, uri, required: URL of the attached file
      - `contentType` string, required: MIME type of the attached file
  - `thread_id` string, uuid: Unique identifier of the thread
  - `stream` boolean: Whether to stream the completion results
  - `config` object: Configuration options for the completion
    - `userContext` object: User context metadata for scoping tool calls and retrievals

## Response `200`

Either a single JSON object (`stream` absent or false) or a stream. A newline-delimited JSON (NDJSON) stream: one complete JSON object per line, each terminated by `\n`. Despite the `text/event-stream` media type this is **not** Server-Sent Events — there is no `data:` prefix and no blank-line record separator, so an off-the-shelf SSE parser will not read it. Split the body on `\n` and `JSON.parse` each non-empty line, buffering any trailing partial line across reads: a line can straddle a transport chunk boundary. A `*_delta` event carries the whole value so far rather than an increment, so replace what you are holding instead of appending to it; note that `thread.message.delta` snapshots can transiently include tool-status text and are not guaranteed to grow monotonically. The stream normally ends with `{"event":"done"}` — a connection aborted or stalled mid-turn ends without one.

- NonStreamCreateChatCompletionsResponse
  - `message` object, required
    - `id` string: Unique identifier of the message
    - `content` string, required: Content of the message
  - `threadId` string, required: Unique identifier of the thread

## Other responses

- `404`: The thread does not exist, or it is not accessible with the presented credential. Absent and inaccessible threads are deliberately indistinguishable. Also returned when `assistant_id` does not name an agent in your organization.
- `422`: The request cannot be processed by the addressed agent: either its type cannot be executed by this API, or a message carries more file attachments than the agent's runtime accepts. Raised before the response begins, so it applies to the streaming variant too. A failure discovered *during* a turn cannot use this status — the response has already begun — and is reported per the streaming error contract instead.
- `429`: Rate limit exceeded for this organization and agent. Retry after the number of seconds in the `Retry-After` header. Every response — not only a 429 — carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.

---

[API](https://skmtc.dev/runbear/apis/runbear-api.md) - [All operations](https://skmtc.dev/runbear/apis/runbear-api/llms.txt) - [OpenAPI document](https://skmtc.dev/runbear/apis/runbear-api/revisions/c6a9c3066a5a?raw)
