> 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.

# Structured extraction

`POST /v1/chat/completions` with `response_format.type: "json_schema"` runs the [extract](/capabilities/extract) capability. The model reads the user text and fills your schema. It returns the filled object as the assistant **content string**, with `finish_reason: "stop"`.

This is the fastest way to add typed extraction to code that already speaks OpenAI. You change the base URL and the model id. You change nothing else.

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

## What the facade does with your request

#### It joins the user turns

The facade keeps messages with `role: "user"`. It joins several user turns with a blank line. It joins the `text` parts of array content with a newline.

#### It drops system prompts

`system`, `developer`, `assistant` and `tool` messages are discarded. A decision model takes no instructions, so a system prompt changes nothing.

#### It runs extract over your schema

The facade passes `response_format.json_schema.schema` straight to `extract`. `json_schema.name` and `json_schema.strict` are accepted and ignored.

#### It returns the object as a string

`choices[0].message.content` holds the JSON text. Parse it yourself, or let the SDK parse it for you.

## Make the call

**`curl`**

```bash title="curl"
curl -s -X POST https://api.milliseconds.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "authorization: Bearer test_sk-..." \
  -d '{
    "model": "decision-machine-1",
    "messages": [
      {"role": "user", "content": "Order #A-2291 shipped on 4 September 2026 to Berlin. The customer paid 149.90 EUR and asked for express delivery."}
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "order",
        "schema": {
          "type": "object",
          "properties": {
            "order_number": {"type": "string", "description": "The order reference"},
            "city": {"type": "string", "description": "Destination city"},
            "amount": {"type": "number", "description": "Amount paid"},
            "express": {"type": "boolean", "description": "Customer asked for express delivery"}
          }
        }
      }
    }
  }'
```

**`Python (openai SDK)`**

```python title="Python (openai SDK)"
import json
from openai import OpenAI

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

completion = client.chat.completions.create(
    model="decision-machine-1",
    messages=[{
        "role": "user",
        "content": (
            "Order #A-2291 shipped on 4 September 2026 to Berlin. "
            "The customer paid 149.90 EUR and asked for express delivery."
        ),
    }],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "order",
            "schema": {
                "type": "object",
                "properties": {
                    "order_number": {"type": "string", "description": "The order reference"},
                    "city": {"type": "string", "description": "Destination city"},
                    "amount": {"type": "number", "description": "Amount paid"},
                    "express": {"type": "boolean", "description": "Customer asked for express delivery"},
                },
            },
        },
    },
)

order = json.loads(completion.choices[0].message.content)
print(order["order_number"], order["amount"])
```

**`TypeScript (openai SDK)`**

```typescript title="TypeScript (openai SDK)"
import OpenAI from "openai";

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

const completion = await client.chat.completions.create({
  model: "decision-machine-1",
  messages: [{
    role: "user",
    content:
      "Order #A-2291 shipped on 4 September 2026 to Berlin. " +
      "The customer paid 149.90 EUR and asked for express delivery.",
  }],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "order",
      schema: {
        type: "object",
        properties: {
          order_number: { type: "string", description: "The order reference" },
          city: { type: "string", description: "Destination city" },
          amount: { type: "number", description: "Amount paid" },
          express: { type: "boolean", description: "Customer asked for express delivery" },
        },
      },
    },
  },
});

const order = JSON.parse(completion.choices[0].message.content ?? "{}");
console.log(order.order_number, order.amount);
```

## The response

The call above returned this. A chat `json_schema` call measures 0.39 s end to end.

```json
{
  "id": "chatcmpl-306e6ee7881f4c9284cdbe0e",
  "object": "chat.completion",
  "created": 1789607702,
  "model": "decision-machine-1",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\"order_number\":\"A-2291\",\"city\":\"Berlin\",\"amount\":149.9,\"express\":true}"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {"prompt_tokens": 28, "completion_tokens": 18, "total_tokens": 46}
}
```

Read `content` as a string, then parse it. A field the text does not carry comes back as `null`. Nothing is invented.

> **Tip**
>
> Give every property a `description`. The description is what the extractor looks for in the text. A bare property name works, but a described field wins the right span more often.

## Schema support

The facade applies the same rules as the native endpoint. Strings, numbers, integers, booleans, enums, string arrays and nested objects all work. An array-of-objects property is accepted and returned empty for now. `oneOf`, `anyOf`, `allOf` and `$ref` do not.

Read the full support matrix on the [Extract](/capabilities/extract) page before you design a schema.

A root schema that is not an object with `properties` returns `400 invalid_schema`, message `schema must be an object with properties`.

## Limits and errors

| Rule           | Value                                                                                               |
| -------------- | --------------------------------------------------------------------------------------------------- |
| Model id       | `decision-machine-1`. Anything else returns `404 model_not_found`.                                  |
| Input size     | The [native extract endpoint](/capabilities/extract) caps `text` at 20,000 characters               |
| Batching       | None here. Use `texts` on the [native extract endpoint](/capabilities/extract) for up to 32 inputs. |
| Ignored fields | `temperature`, `max_tokens`, `top_p` and other chat parameters are accepted and ignored             |
| No user text   | `400 invalid_request`, message `At least one user message with text is required.`                   |

> **Warning**
>
> `tools` beats `response_format`. When a request carries both, the facade takes the [function-calling](/openai/function-calling) path and your schema is never used. Send `tool_choice: "none"` to keep the extraction path.

A request with no `response_format.json_schema` and no tools returns `400 unsupported_request`:

```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/."}}
```

`response_format: {"type":"json_object"}` and `{"type":"text"}` return the same 400. `tool_choice: "none"` without a `json_schema` also returns it. Only `json_schema` does work.

## Next

#### [Extract](/capabilities/extract)

The native endpoint, the full schema matrix, and batching over 32 texts.

#### [Function calling](/openai/function-calling)

Pass `tools` and let the model pick one and fill its arguments.

#### [When to use the native API](/openai/when-to-use-native)

What probabilities, spans and batches the OpenAI surface cannot return.