Chat Completions

The OpenAI-compatible endpoint. Anything built on the openai packages works against it unchanged.

POST/v1/chat/completions
curl https://api.xkiro.com/v1/chat/completions \
  -H "Authorization: Bearer $XKIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.6-sol",
    "messages": [
      { "role": "system", "content": "You are a concise assistant." },
      { "role": "user", "content": "Name three uses for a paperclip." }
    ],
    "temperature": 0.7,
    "max_tokens": 500
  }'

Request body#

Core fields
FieldTypeDescription
modelrequiredstringFull model ID including the vendor prefix, e.g. openai/gpt-5.6-sol. See Models.
messagesrequiredarrayThe conversation so far. Each entry has a role (system, user, assistant or tool) and content.
streambooleanStream tokens as server-sent events. Default false. See Streaming.
max_tokensintegerUpper bound on generated tokens. Never raised on your behalf; clamped down if it exceeds what the model allows.
temperaturenumberRandomness, 0–2. Clamped to each model's accepted range. Ignored while reasoning is active, because reasoning models reject it.
top_pnumberNucleus sampling. Use this or temperature, not both.
stopstring | string[]Up to four sequences that end generation.
frequency_penaltynumber−2 to 2. Discourages repeating tokens already used.
presence_penaltynumber−2 to 2. Encourages introducing new topics.
seedintegerBest-effort determinism. Not guaranteed across providers.
response_formatobjectSet { "type": "json_object" } to constrain output to valid JSON. Your prompt must still ask for JSON.
userstringOpaque end-user identifier, echoed into your logs for abuse tracing.
Tools
FieldTypeDescription
toolsarrayFunction definitions the model may call. See Tool calling.
tool_choicestring | objectauto (default), none, required, or { "type": "function", "function": { "name": "..." } } to force one.
Reasoning
FieldTypeDescription
reasoning_effortstringnone, low, medium, high, xhigh or max. Omit to keep the model's own default. See Reasoning.

Message content#

content is either a plain string or an array of parts. Use the array form to mix text and images.

Text and image in one message
{
  "role": "user",
  "content": [
    { "type": "text", "text": "What is unusual about this chart?" },
    {
      "type": "image_url",
      "image_url": { "url": "https://example.com/chart.png" }
    }
  ]
}

Base64 data URLs work too. xKiro detects the real media type from the bytes rather than trusting the label, so a JPEG mislabelled as PNG still reaches the model correctly.

Response#

200 OK
{
  "id": "chatcmpl-9f1c2a4e",
  "object": "chat.completion",
  "created": 1785734400,
  "model": "openai/gpt-5.6-sol",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "..." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 24,
    "completion_tokens": 88,
    "total_tokens": 112
  }
}

finish_reason

FieldTypeDescription
stopstringThe model finished on its own.
lengthstringHit max_tokens or the context window. The answer is cut off — raise the limit or shorten the input.
tool_callsstringThe model wants to call a tool. Run it and send the result back.
content_filterstringThe provider blocked the content. Retrying the same input will not help.

model always reflects what you requested

If routing had to serve your request with a different model, the response still reports the model you asked for and you are billed at its price. Your dashboard shows what actually ran.

Behaviour worth knowing#

  • Non-streaming requests time out at 95 seconds. Use stream: true for long generations.
  • Cancelling stops the upstream call. Close the connection and xKiro aborts the provider request. You are billed for what was generated before that point, not for the whole request.
  • Identical blocking requests are de-duplicated. The same body sent twice within about two minutes replays the first result instead of billing twice — see Idempotency & retries.