---
title: "Generate an LLM response, as JSON or as a server-sent event stream"
method: POST
path: "/v1/llm/responses"
---

# Generate an LLM response, as JSON or as a server-sent event stream

`POST /v1/llm/responses`

Generates a response for the supplied conversation history. With stream omitted or false the body is a single application/json LLMResponse; with stream true the body is a text/event-stream whose event payloads are documented by the ResponseCreated, ResponseItemAdded, ResponseTextDelta, ResponseFunctionCallArgumentsDelta, ResponseItemCompleted, ResponseCompleted, and ErrorEnvelope schemas. Response ids are Speko-minted (resp_<request-id>); provider response and conversation ids never appear in output — they are captured only as content-free telemetry evidence. There is no previous_response_id anywhere in the contract: callers resend full history, including function results, on every request.

## Headers

- `Idempotency-Key` string, required

## Request body

- LLMRequest — The POST /v1/llm/responses body. max_output_tokens is required: the Router reserves credit for the worst-case generation before dispatch, and an unbounded generation cannot be priced.
  - `routing` Routing — Selects how the Router picks a provider for one request. It is a tagged union over mode: auto carries the objective and optional allow/deny provider filters, explicit carries exactly a provider and model. Fields from the other arm are rejected in both directions; a present-but-empty filter array ([]) counts as present and is rejected in explicit mode, while an empty-string provider or model in auto mode is indistinguishable from omission and tolerated as such. A wholly omitted routing object means {mode: auto, objective: balanced}, and an auto routing without an objective defaults to balanced — the default exists for omission, never for partially specified routing. Explicit mode also accepts the combined "provider/model" spelling in the model field: {"model": "openai/gpt-5.2"} with no provider splits at the first slash, and a model redundantly prefixed with the stated provider has the prefix stripped. Upstream model ids that themselves contain a slash must therefore name the provider one way or the other, e.g. "together/meta-llama/Llama-X" or provider "together" with model "meta-llama/Llama-X".
    - `mode` 'auto' | 'explicit' — Discriminates the routing tagged union. The set is closed: an empty or unknown mode is rejected so a typo can never silently fall back to automatic provider selection.
    - `objective` 'balanced' | 'quality' | 'latency' | 'cost' — Ranks auto-mode candidates. Unknown objectives are rejected rather than treated as balanced so callers learn about typos before any money is reserved.
    - `allow_providers` string[] — Auto mode only. Provider ids must not be blank.
    - `deny_providers` string[] — Auto mode only. Provider ids must not be blank.
    - `provider` string — Explicit mode only, and required there.
    - `model` string — Explicit mode only, and required there. Accepts either the bare model id with the provider field set, or the combined "provider/model" form with the provider field omitted.
  - `input` InputItem[], required — Full conversation history, resent on every request. The InputItem union has no structured_json branch — those items are output-only, and resent history represents earlier structured output as ordinary assistant text.
    - union — The item union as request input admits it: structured_json items are output-only, so the union has no branch for them — resent history represents earlier structured output as ordinary assistant text. This narrowing exists in the schema, not just in prose, so a generated client cannot construct an input the Router rejects at runtime.
      - MessageItem — A conversation message. There is no tool role: tool traffic uses the dedicated function_call and function_result item types.
        - `type` 'message', required
        - `role` 'system' | 'user' | 'assistant', required
        - `content` ContentPart[], required
          - `type` 'text', required
          - `text` string, required
      - FunctionCallItem — A model-issued function call. arguments is JSON carried as a string, matching how function call arguments stream as text deltas; call_id is shared with function_result and links a result to the call that produced it.
        - `type` 'function_call', required
        - `call_id` string, required
        - `name` string, required
        - `arguments` string, required — The call arguments as a JSON text.
      - FunctionResultItem — A caller-supplied function result, resent as part of full history. call_id links the result to the function_call that produced it.
        - `type` 'function_result', required
        - `call_id` string, required
        - `result` string — The function result as opaque text: it is not required to be JSON, and it may be omitted or empty — a function can succeed with nothing to say.
  - `tools` FunctionTool[] — Tool names must be unique.
    - `name` string, required
    - `description` string
    - `parameters` unknown
  - `response_format` ResponseFormat — Requests schema-conforming structured output. json_schema is the only response format type — the field is an enum of one so json_object-style loose modes can be added deliberately, never by accident. Admitted only on models advertising structured output support.
    - `type` 'json_schema', required
    - `name` string, required
    - `schema` unknown, required
    - `strict` boolean
  - `max_output_tokens` integer, required
  - `temperature` number
  - `top_p` number — Must be greater than 0 and at most 1.
  - `stream` boolean — When true the response is a text/event-stream instead of a single JSON body.

## Response `200`

The generated response, as a JSON body (stream omitted or false) or as a server-sent event stream (stream true).

- LLMResponse — The non-streaming POST /v1/llm/responses body. id is Speko-minted — resp_<request-id>, never a provider identifier; provider response and conversation ids are captured only as content-free telemetry evidence. There is no previous_response_id to feed it back into: callers resend full history.
  - `id` string, required
  - `route` Route, required — The response-side counterpart of Routing: the concrete decision that served a request. region is the Speko Router location, not a provider-processing residency guarantee, and attempt_id identifies the (possibly post-fallback) attempt that produced the output.
    - `provider` string, required
    - `model` string, required
    - `region` string, required
    - `attempt_id` string, required
  - `output` OutputItem[], required
    - union — The item union as response output emits it: function_result items only ever travel caller-to-Router and output messages are always assistant-authored, so the union carries no function_result branch and narrows message to AssistantMessageItem. Output is server-emitted — the narrowing makes a connector normalization bug that mislabels an item a loud contract violation instead of a plausible input shape.
      - AssistantMessageItem — The output-side narrowing of MessageItem: response output and streamed output items only ever carry assistant-authored messages, so the role enum admits nothing else. Request input uses the full MessageItem.
        - `type` 'message', required
        - `role` 'assistant', required
        - `content` ContentPart[], required
          - `type` 'text', required
          - `text` string, required
      - FunctionCallItem — A model-issued function call. arguments is JSON carried as a string, matching how function call arguments stream as text deltas; call_id is shared with function_result and links a result to the call that produced it.
        - `type` 'function_call', required
        - `call_id` string, required
        - `name` string, required
        - `arguments` string, required — The call arguments as a JSON text.
      - StructuredJSONItem — The model's schema-conforming JSON when the request asked for structured output. It is output-only: requests represent earlier structured output as ordinary assistant text when resending history.
        - `type` 'structured_json', required
        - `json` unknown, required
  - `stop_reason` 'stop' | 'max_output_tokens' | 'tool_call', required — Why generation ended. The set is closed.
  - `usage` Usage, required — Normalized, provider-neutral quantities for one request or stream. Only the lines relevant to the request's kind are set; zero lines are omitted on the wire. Split lines are mutually exclusive by contract: a token counted in cached_input_tokens is not repeated in input_tokens, and a reasoning token is not repeated in output_tokens, so the splits always sum to the totals. Providers that report no split report all-uncached / all-visible.
    - `duration_ms` integer
    - `characters` integer
    - `input_tokens` integer
    - `cached_input_tokens` integer
    - `output_tokens` integer
    - `reasoning_tokens` integer

## Other responses

- `429` — The request exceeded an edge request quota, an organization concurrency limit, or a provider rate limit. The body identifies the stable failure class; Retry-After gives the minimum safe delay.
- `default` — Normalized failure. Every non-2xx response carries this envelope; the Router never forwards a raw provider response body.

## Changes

- **2026-08-24** `9774b21e73da` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/speko/apis/speko-voice-router-api/changes/v1/llm/responses/post.md)

---

[API](https://skmtc.dev/speko/apis/speko-voice-router-api.md) · [All operations](https://skmtc.dev/speko/apis/speko-voice-router-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/speko/speko-voice-router-api/revisions/890238de6e30/schema)
