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

# OpenAI-compatible API

`decision-machine-1` serves an OpenAI-compatible surface at `https://api.milliseconds.ai/v1`.
Keep your existing OpenAI client. Change the base URL and the model name.

Set `base_url` to `https://api.milliseconds.ai/v1` and `model` to `decision-machine-1`.
Every request needs an API key. Set the client's `api_key` to your test or production key. See [Authentication](/authentication).

> **Note**
>
> The facade is a compatibility layer, not a chat model. It supports two modes: structured extraction and function calling. Other chat requests return `400`.
>
> Each admitted chat-completion call counts as one inference request unit for usage and RPM. See [Limits and rate limits](/reference/limits).

## The first call

This call extracts a typed object from one sentence.

#### curl

```bash
curl 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": "Table for 4 at Nobu on Friday."}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "booking",
        "schema": {
          "type": "object",
          "properties": {
            "restaurant": {"type": "string"},
            "party_size": {"type": "integer"},
            "day": {"type": "string"}
          }
        }
      }
    }
  }'
```

#### Python

```python
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": "Table for 4 at Nobu on Friday."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "booking",
            "schema": {
                "type": "object",
                "properties": {
                    "restaurant": {"type": "string"},
                    "party_size": {"type": "integer"},
                    "day": {"type": "string"},
                },
            },
        },
    },
)

print(completion.choices[0].message.content)
```

#### TypeScript

```typescript
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: "Table for 4 at Nobu on Friday." }],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "booking",
      schema: {
        type: "object",
        properties: {
          restaurant: { type: "string" },
          party_size: { type: "integer" },
          day: { type: "string" },
        },
      },
    },
  },
});

console.log(completion.choices[0].message.content);
```

The response is an ordinary chat completion. The JSON arrives as the assistant content string.

```json
{
  "id": "chatcmpl-c120b2346111481486fbf008",
  "object": "chat.completion",
  "created": 1789607270,
  "model": "decision-machine-1",
  "choices": [
    {
      "index": 0,
      "message": {"role":"assistant","content":"{\"restaurant\":\"Nobu\",\"party_size\":4,\"day\":\"Friday\"}"},
      "finish_reason": "stop"
    }
  ],
  "usage": {"prompt_tokens":8,"completion_tokens":13,"total_tokens":21}
}
```

## Which messages the model reads

The model reads the text of your `user` messages and nothing else.

| Role                                       | Treatment                                                                                                                                                          |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `user`                                     | The facade keeps the turn. It uses string content directly, and joins the `text` parts of array content with a newline. Several user turns join with a blank line. |
| `system`, `developer`, `assistant`, `tool` | The facade discards the message.                                                                                                                                   |

> **Warning**
>
> System prompts do nothing here. A decision model takes no instructions, only text. The facade drops those messages before it runs the decision.

Put the text you want a decision about in a user message. Put the instruction in the schema or the tool description. Descriptive field names carry the meaning, so follow the rules for [writing good statements and labels](/concepts/writing-good-questions).

## The two supported modes

#### [Structured extraction](/openai/structured-extraction)

`response_format.json_schema` runs `extract`. The assistant content is the JSON string, `finish_reason` is `stop`.

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

`tools` picks one tool and fills its arguments. `finish_reason` is `tool_calls`, `content` is `null`.

The server picks the mode in a fixed order:

1. A `model` other than `decision-machine-1` returns `404 model_not_found`.
2. No user message with text returns `400 invalid_request`.
3. `tools`, with `tool_choice` absent or other than `"none"`, runs function calling. `tools` wins over `response_format`.
4. `response_format.type` of `json_schema` runs structured extraction.
5. Anything else returns `400 unsupported_request`.

## Plain chat returns 400 on purpose

`decision-machine-1` generates no prose. A chat request without a schema or tools is a mistake, so the API says so rather than guessing.

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

The same `400` covers `response_format: {"type":"text"}`, `response_format: {"type":"json_object"}`, and `tool_choice: "none"` without a usable `response_format`.

## Model discovery

`GET /v1/models` lists exactly one model. Client libraries that probe the model list work without change.

```json
{"object":"list","data":[{"id":"decision-machine-1","object":"model","created":1789000000,"owned_by":"milliseconds"}]}
```

`GET /v1/models/decision-machine-1` returns that one object. Any other id returns `404`.

```json
{"error":{"code":"model_not_found","message":"Unknown model. Use \"decision-machine-1\"."}}
```

## What the facade does not carry

The facade returns OpenAI shapes only. Probabilities, confidence, character offsets and batches have no place in a chat completion, so the facade drops them. The [native capabilities](/capabilities/overview) return all four.

The facade accepts and ignores sampling fields such as `temperature`, `top_p` and `max_tokens`. The `usage` block reports `prompt_tokens`, the request's input tokens. [Streaming and differences](/openai/streaming-and-differences) lists every deviation.

## Next

* [Structured extraction](/openai/structured-extraction) — the full `json_schema` mode, with the supported types.
* [Function calling](/openai/function-calling) — tool selection and argument filling.
* [When to use the native API](/openai/when-to-use-native) — what you gain by leaving the facade.