> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.milliseconds.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.milliseconds.ai/_mcp/server.

# Streaming and differences

The OpenAI surface is a compatibility shim over a decision model. It accepts
the request shape your client already sends. It does not behave like a chat
model.

Read this page before you point a production client at
`https://api.milliseconds.ai/v1`.

## Streaming

Set `stream: true` on `POST /v1/chat/completions`. The response has
`content-type: text/event-stream` and `cache-control: no-cache`.

The server does the full decision first, then serializes it. The stream is
always three `data:` lines:

1. One chunk with the complete content or the complete tool call.
2. One chunk with an empty `delta` and `finish_reason`, carrying `usage`.
3. `data: [DONE]`.

There is no incremental delta. No text arrives before the decision finishes.

> **Warning**
>
> Streaming does not reduce time to first token. The first chunk arrives after
> the model completes. Use it only for client compatibility.

### Structured extraction stream

A real stream from the `response_format.json_schema` request below:

```text
data: {"id":"chatcmpl-ab88beba37d64886aaaa6135","object":"chat.completion.chunk","created":1789607831,"model":"decision-machine-1","choices":[{"index":0,"delta":{"role":"assistant","content":"{\"restaurant\":\"Nobu\",\"party_size\":2}"},"finish_reason":null}]}

data: {"id":"chatcmpl-ab88beba37d64886aaaa6135","object":"chat.completion.chunk","created":1789607831,"model":"decision-machine-1","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":7,"completion_tokens":9,"total_tokens":16}}

data: [DONE]
```

### Tool call stream

A real stream from `tools`, over the text `Weather in Paris` with a
`get_weather` tool. Each tool call carries an `index`:

```text
data: {"id":"chatcmpl-97c7c0978e44463a95976267","object":"chat.completion.chunk","created":1789606542,"model":"decision-machine-1","choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_243386d795404cfa80ab6dcf","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"Paris\"}"}}]},"finish_reason":null}]}

data: {"id":"chatcmpl-97c7c0978e44463a95976267","object":"chat.completion.chunk","created":1789606542,"model":"decision-machine-1","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":4,"completion_tokens":4,"total_tokens":8}}

data: [DONE]
```

### Call it

#### curl

```bash
curl -N https://api.milliseconds.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "model": "decision-machine-1",
    "stream": true,
    "messages": [{"role": "user", "content": "Book Nobu for 2 on Friday"}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "booking",
        "schema": {
          "type": "object",
          "properties": {
            "restaurant": {"type": "string"},
            "party_size": {"type": "integer"}
          }
        }
      }
    }
  }'
```

#### Python

```python
from openai import OpenAI

client = OpenAI(base_url="https://api.milliseconds.ai/v1", api_key="test_sk-...")

schema = {
    "type": "object",
    "properties": {
        "restaurant": {"type": "string"},
        "party_size": {"type": "integer"},
    },
}

stream = client.chat.completions.create(
    model="decision-machine-1",
    stream=True,
    messages=[{"role": "user", "content": "Book Nobu for 2 on Friday"}],
    response_format={"type": "json_schema", "json_schema": {"name": "booking", "schema": schema}},
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content)
```

#### TypeScript

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.milliseconds.ai/v1",
  apiKey: "test_sk-...",
});

const schema = {
  type: "object",
  properties: { restaurant: { type: "string" }, party_size: { type: "integer" } },
};

const stream = await client.chat.completions.create({
  model: "decision-machine-1",
  stream: true,
  messages: [{ role: "user", content: "Book Nobu for 2 on Friday" }],
  response_format: { type: "json_schema", json_schema: { name: "booking", schema } },
});

for await (const chunk of stream) {
  const text = chunk.choices[0]?.delta?.content;
  if (text) console.log(text);
}
```

Every request needs an API key. Pass your test or production key as the client's
`api_key`. See [Authentication](/authentication).

## Usage tokens

The `usage` object reports `prompt_tokens`, the request's input tokens. It also
reports `completion_tokens` and `total_tokens`.

The price is \$0.04 per million input tokens and \$0 per output token. See
[Pricing](/reference/pricing).

## What the API ignores

The request body is permissive. Unknown and unsupported fields pass validation
and change nothing.

| Field                                                             | Effect                     |
| ----------------------------------------------------------------- | -------------------------- |
| `temperature`, `top_p`, `max_tokens`, and similar sampling fields | Accepted, ignored          |
| `system`, `developer`, `assistant`, `tool` messages               | Discarded before inference |
| `json_schema.name`, `json_schema.strict`                          | Accepted, ignored          |
| `required` inside an extract schema                               | Read, never enforced       |

Only `user` messages carry text into the model. Multiple user turns join with
blank lines.

> **Note**
>
> A system prompt cannot steer a decision model. A pirate-speak system prompt
> changed nothing in testing.

## What returns an error

| Request                                                        | Status | Code                  |
| -------------------------------------------------------------- | ------ | --------------------- |
| Plain chat, no `response_format` and no `tools`                | 400    | `unsupported_request` |
| `response_format: {"type":"text"}` or `{"type":"json_object"}` | 400    | `unsupported_request` |
| `tool_choice: "none"` without a usable `response_format`       | 400    | `unsupported_request` |
| No user message with text                                      | 400    | `invalid_request`     |
| `tool_choice` names a tool absent from `tools`                 | 400    | `invalid_request`     |
| A `model` other than `decision-machine-1`                      | 404    | `model_not_found`     |
| `GET /v1/models/{id}` with a wrong id                          | 404    | `model_not_found`     |

The plain-chat refusal is deliberate:

```json
{"error":{"code":"unsupported_request","message":"decision-machine-1 decides, it does not chat. Send response_format.json_schema for structured extraction, tools for function calling, or use the capability endpoints under /v1/decision-machine-1/."}}
```

## What does not exist

The OpenAI surface is three routes: `GET /v1/models`, `GET /v1/models/{id}`
and `POST /v1/chat/completions`.

* **No embeddings.** There is no `/v1/embeddings` endpoint.
* **No image URLs.** Array content may carry one `image_url` part, and its `url` must be a `data:image/(jpeg|png|webp);base64,` URL of at most 5 MB decoded. An `http(s)` url returns `400` `invalid_image`. The part's `detail` reads `low` and `high`; `auto` and anything else fall back to `medium`. An image bills the `extract` tier, x2: 2,000 for `low`, 4,000 for `medium`, 8,000 for `high`. See [Images](/concepts/images).
* **No audio, files, assistants or responses API.** Those routes do not exist.
* **No multiple tool calls.** One choice comes back, and it carries exactly one tool call.
* **No generation.** Every output is a decision over the text you sent.

An unrouted path or a wrong method returns a plain-text `404 Not Found`,
outside the JSON error envelope.

## Next

* [When to use the native API](/openai/when-to-use-native) shows what the capability endpoints return that this surface cannot.
* [Errors](/reference/errors) lists every code, status and cause.
* [Structured extraction](/openai/structured-extraction) covers the `json_schema` mode in full.