---
title: "Create a chat completion"
method: POST
path: "/api/ai/v1/completions"
tags: ["AI"]
---

# Create a chat completion

`POST /api/ai/v1/completions`

## Request

- Base URL: `https://your-deployment.example.com`
- URL: `https://your-deployment.example.com/api/ai/v1/completions`
- Auth: one of:
  - HTTP bearer
  - API key in header `x-api-key`

OpenAI-compatible completion. A successful request is ALWAYS answered with an SSE stream (`text/event-stream`), regardless of the `stream` flag: a `meta` event, then `content`/`tool_use` events carrying `usage`/`credits`, terminated by `data: [DONE]`. The terminal event carries `stopReason`; treat `max_tokens` as a TRUNCATED reply rather than a complete one, and note that omitting `max_tokens` on the request lets the server size the output ceiling for the model (recommended for reasoning models, which spend thinking tokens inside that ceiling). A message `content` may be a string or an array of parts; image parts are accepted in OpenAI Chat (`image_url`), OpenAI Responses (`input_image`) or Anthropic (`image` with a `source`) form and are translated to whatever the target model speaks. Authenticate with an API key (`b4m_live_`) or a JWT.

TOOL SEARCH. On a model whose `GET /api/v1/models` row has `supports_tool_search: true` and `backend: "anthropic"`, a `tools` entry may set `toolSchema.defer_loading: true`, and a `{ "serverTool": { "type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex" } }` (or the `bm25` pair) entry adds Anthropic's tool search, so deferred definitions load only when the model searches for them. The stream then carries `provider_block` events (`{ type, provider: "anthropic", block }`) for the server-run search; echo each block back verbatim, in order, in the assistant `messages` content of the next request, and never answer one with a `tool_result`. When the server-run search loop hits its iteration limit the stream ends with `stopReason: "pause_turn"`; continue by re-sending the conversation with that assistant turn's provider blocks echoed. A client-side search may return `{ "type": "tool_reference", "tool_name": "..." }` blocks inside a `tool_result`'s content. On every other model, Bedrock-served Claude included, `defer_loading` is ignored (every tool is sent as usual) and a server tool, a `tool_reference` or an echoed tool search block is refused with a `400` rather than run without it.

ERRORS. Everything checked before the model provider is called fails with a JSON error body and a real HTTP status, before any stream opens: a malformed or otherwise invalid request or an unknown `model` `400` (`errorCode: "unknown_model"` for the latter), a bad credential `401`, a key without a required scope `403` (with `required_scopes`), a body over 25 MB `413`, a body that fails schema validation `422`, insufficient credits `422` with `errorCode: "insufficient_credits"` (personal or org balance, or an org per-member credit cap), a rate limit `429` with `Retry-After`, and a catalogued model whose provider is not configured on this deployment `503` with `errorCode: "provider_not_configured"` (retryable). Once the stream has opened the status stays `200` and a failure arrives as one terminal in-band `error` event. Branch on that event's `code`, never on `message`, which is prose and may change: `insufficient_credits` (buy credits) when credits run out mid-generation, `provider_rejected` when the model provider refused our credentials, `content_blocked` when the model declined the request (do not retry it unchanged), and `provider_error` for any other provider failure. Treat `code` as optional.

## Request body

- CreateCompletionRequest
  - `model` string, required
  - `messages` object[], required
    - `role` 'user' | 'assistant' | 'system', required
    - `content` union, required
      - string
      - unknown[]
        - unknown
    - `cache` boolean
  - `response_format` union
    - object
      - `type` 'text', required
    - object
      - `type` 'json_schema', required
      - `json_schema` object, required
        - `name` string, required
        - `description` string
        - `schema` object, required
        - `strict` boolean
  - `stream` boolean
  - `tools` union[]
    - union
      - object
        - `toolSchema` object, required
          - `name` string, required
          - `description` string, required
          - `parameters` object, required
            - `type` 'object', required
            - `properties` object
            - `additionalProperties` boolean
            - `required` string[]
          - `strict` boolean
          - `defer_loading` boolean: Keep this definition out of the model context until the tool search server tool (or a `tool_reference`) loads it. Honoured only on models served by the direct Anthropic API whose `supports_tool_search` is true; on every other model (including Bedrock-served Claude, for now) it is ignored and the tool is sent as usual. At least one tool, or a `serverTool` entry, must stay non-deferred (`errorCode: "all_tools_deferred"`).
          - `cache_control` unknown
        - `_isMcpTool` boolean
      - object
        - `serverTool` union, required: A provider-run tool, sent to the provider as `{ type, name }`. Only the Anthropic tool search tools exist today, and only on models served by the direct Anthropic API whose `supports_tool_search` is true; any other model, Bedrock-served Claude included, is refused with `errorCode: "tool_search_unsupported"` rather than run without it.
          - object
            - `type` 'tool_search_tool_regex_20251119', required
            - `name` 'tool_search_tool_regex', required
          - object
            - `type` 'tool_search_tool_bm25_20251119', required
            - `name` 'tool_search_tool_bm25', required
  - `temperature` number
  - `max_tokens` number
  - `options` object
    - `temperature` number
    - `maxTokens` number
    - `stream` boolean
    - `tools` union[]
      - union
        - object
          - `toolSchema` object, required
            - `name` string, required
            - `description` string, required
            - `parameters` object, required
              - …
            - `strict` boolean
            - `defer_loading` boolean: Keep this definition out of the model context until the tool search server tool (or a `tool_reference`) loads it. Honoured only on models served by the direct Anthropic API whose `supports_tool_search` is true; on every other model (including Bedrock-served Claude, for now) it is ignored and the tool is sent as usual. At least one tool, or a `serverTool` entry, must stay non-deferred (`errorCode: "all_tools_deferred"`).
            - `cache_control` unknown
          - `_isMcpTool` boolean
        - object
          - `serverTool` union, required: A provider-run tool, sent to the provider as `{ type, name }`. Only the Anthropic tool search tools exist today, and only on models served by the direct Anthropic API whose `supports_tool_search` is true; any other model, Bedrock-served Claude included, is refused with `errorCode: "tool_search_unsupported"` rather than run without it.
            - object
              - …
            - object
              - …
    - `response_format` union
      - object
        - `type` 'text', required
      - object
        - `type` 'json_schema', required
        - `json_schema` object, required
          - `name` string, required
          - `description` string
          - `schema` object, required
          - `strict` boolean
    - `reasoningEffort` 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh': Reasoning effort hint. Honoured by the OpenAI (reasoning models only, and not alongside tools on some of them), Kimi (K3 only), DeepSeek and Anthropic Claude (direct and Bedrock) adapters; each maps it onto its own scale. On Claude, `none` turns thinking off where the model allows it (otherwise it runs at the lowest effort), so short `max_tokens` replies stream visible text from the start; `minimal` maps to `low`. When `thinking.enabled` is also set on a Claude model, thinking stays on and this sets its effort instead, with `none` and `minimal` mapping to `low`. Claude models that reason inside `max_tokens` never have that ceiling raised, and on older Claude models any level above `none` turns thinking on (it is off by default there), forces temperature to 1 and drops top_p, and a thinking budget that cannot fit under `max_tokens` is skipped. On the direct Anthropic API, a json_schema `response_format` with no tools is served by a forced tool on models that support forced tool use, and that tool cannot run alongside thinking, so there only `none` is honoured; models served by native structured outputs honour every level. Silently ignored by every other adapter (Gemini, xAI, Ollama) and by non-reasoning models, so sending it there is a no-op.
    - `thinking` object: Ask a model that can think to think on this turn. On Claude, `reasoningEffort` then sets how much it thinks, and `budget_tokens` (default 16000, minimum 1024) sizes thinking on older Claude models. Ignored by models that cannot think, and on the direct Anthropic API when a json_schema `response_format` is served by a forced tool, which cannot run alongside thinking.
      - `enabled` boolean, required
      - `budget_tokens` integer

## Response `200`

SSE stream of completion events (see the CompletionStreamEvent shape).

## Other responses

- `400`: Malformed or otherwise invalid request (no `errorCode`), or a `model` the caller cannot use (`errorCode: "unknown_model"`): not in the catalog or not available to the caller. List valid ids with `GET /api/v1/models`. Tool search requests are refused with: `tool_search_unsupported` (a server tool, `tool_reference` or echoed tool search block on a model that cannot serve tool search here), `all_tools_deferred` (every tool deferred and no server tool), `deferred_tool_cache_control` (`cache_control` on a deferred tool), `unknown_tool_reference` (a `tool_reference` naming a tool not in `tools`, or carrying no string `tool_name`).
- `401`: Missing or invalid credentials.
- `403`: The API key does not hold any of the required scopes.
- `405`: The path does not serve this HTTP method.
- `413`: Request body over 25 MB.
- `422`: The body failed schema validation, or the caller cannot afford the request (`errorCode: "insufficient_credits"`: personal or org balance, or an org per-member credit cap).
- `429`: Rate limit exceeded.
- `500`: Internal error before the stream opened.
- `503`: Temporarily unavailable: the model's provider is not configured on this deployment (`errorCode: "provider_not_configured"`), or the database is unreachable (no `errorCode`). Retry later.

---

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