> Keiro Labs API documentation - Chat Completions
> Page: https://docs.keirolabs.ai/chat-completions.html
> Markdown: https://docs.keirolabs.ai/chat-completions.md
> Agent index: https://docs.keirolabs.ai/llms.txt
> API base URL: https://api.keirolabs.ai/v1
> Auth: send "Authorization: Bearer <api-key>" with a key created in the
> Keiro console (https://console.keirolabs.ai/api-keys). Use credentials saved by
> `keiro setup`, or resolve the key from a secret manager or an owner-only
> secret file and pass it to the client explicitly. Never read another
> provider's variable, and never put raw key material in environment
> variables, code, docs, or logs.

# Chat Completions

Use `POST /v1/chat/completions` when your application already sends ordered
message arrays and reads `choices[0].message`.

## Request

A minimal request includes a public model and at least one message:

```json
{
  "model": "eb1-preview",
  "messages": [
    {
      "role": "user",
      "content": "Explain token buckets in one sentence."
    }
  ]
}
```

Use the `system` role or supported top-level system compatibility field for
stable instructions. Keep application-specific context in explicit messages.

**Send a Chat Completions request**

_Python_

```python
from getpass import getpass

from openai import OpenAI

client = OpenAI(
    base_url="https://api.keirolabs.ai/v1",
    api_key=getpass("Keiro API key: "),
)

response = client.chat.completions.create(
    model="eb1-preview",
    messages=[
        {"role": "system", "content": "Answer concisely."},
        {"role": "user", "content": "Explain vector databases."},
    ],
)

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

_JavaScript_

```javascript
import fs from "node:fs";
import OpenAI from "openai";

const keyPath = process.env.KEIRO_KEY_FILE;
if (!keyPath) {
  throw new Error("KEIRO_KEY_FILE must point to a protected secret file");
}

const client = new OpenAI({
  baseURL: "https://api.keirolabs.ai/v1",
  apiKey: fs.readFileSync(keyPath, "utf8").trim(),
});

const response = await client.chat.completions.create({
  model: "eb1-preview",
  messages: [
    { role: "system", content: "Answer concisely." },
    { role: "user", content: "Explain vector databases." },
  ],
});

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

_curl_

```bash
printf 'Keiro API key: '
IFS= read -rs KEIRO_BEARER
printf '\n'

curl -sS https://api.keirolabs.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "eb1-preview",
    "messages": [
      {"role": "system", "content": "Answer concisely."},
      {"role": "user", "content": "Explain vector databases."}
    ]
  }' \
  -H @- <<<"Authorization: Bearer $KEIRO_BEARER"
```

## Response

Read generated text from `choices[0].message.content`, the terminal reason from
`choices[0].finish_reason`, and canonical token accounting from `usage`.

Treat the public response layout as the contract. Do not depend on private
model selection, vendor metadata, or internal cost fields.

## Supported controls

The current Chat Completions surface supports the documented message input plus
controls including `stream`, `temperature`, `top_p`, `stop`, `max_tokens` or
`max_completion_tokens`, `seed`, `tools`, `tool_choice`,
`parallel_tool_calls`, text response format, `logprobs`, `user`, metadata, and
reasoning controls.

Strict validation rejects unknown fields. JSON mode and JSON Schema structured
output are not currently supported.

## Streaming

Set `stream` to `true` to receive server-sent events. Text arrives in
`choices[0].delta.content`; tool calls arrive in `delta.tool_calls`. Assemble
fragments by choice and tool-call index, and wait for the terminal finish
reason before executing a tool.

Streams are governed by documented idle and wall-clock time entitlements, not
a flat timeout. `reasoning_effort` selects the run's entitlement row — see
[Streaming](streaming.md#stream-lifetime-and-time-entitlements), and the same
page for terminal and error handling.

**Stream a Chat Completions request**

_Python_

```python
from getpass import getpass

from openai import OpenAI

client = OpenAI(
    base_url="https://api.keirolabs.ai/v1",
    api_key=getpass("Keiro API key: "),
)

stream = client.chat.completions.create(
    model="eb1-preview",
    messages=[{"role": "user", "content": "Count to three."}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
```

_JavaScript_

```javascript
import fs from "node:fs";
import OpenAI from "openai";

const keyPath = process.env.KEIRO_KEY_FILE;
if (!keyPath) {
  throw new Error("KEIRO_KEY_FILE must point to a protected secret file");
}

const client = new OpenAI({
  baseURL: "https://api.keirolabs.ai/v1",
  apiKey: fs.readFileSync(keyPath, "utf8").trim(),
});

const stream = await client.chat.completions.create({
  model: "eb1-preview",
  messages: [{ role: "user", content: "Count to three." }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

_curl_

```bash
printf 'Keiro API key: '
IFS= read -rs KEIRO_BEARER
printf '\n'

curl -sS -N https://api.keirolabs.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "eb1-preview",
    "stream": true,
    "messages": [
      {"role": "user", "content": "Count to three."}
    ]
  }' \
  -H @- <<<"Authorization: Bearer $KEIRO_BEARER"
```

## Tools and images

- Function tools use the Chat Completions nested `tools[].function` shape and
  return `message.tool_calls`; see [Tool calling](tool-calling.md).
- Images use `image_url` content parts inside a user message; see
  [Images and vision](images-vision.md).

## Related pages

- [API reference](api-reference.md)
- [Responses](responses.md)
- [Messages](messages.md)
- [Errors](errors.md)
