Chat Completions
POST https://api.devotel.com/v1/chat/completionsThe one endpoint you need. Request and response shapes match the OpenAI Chat Completions API.
Request parameters
These are validated by the gateway:
| Parameter | Type | Notes |
|---|---|---|
model | string, required | kimi-k3. See Models. |
messages | array, required | At least one message. Roles below. |
stream | boolean | true for SSE. See Streaming. |
stream_options | object | { "include_usage": true } is forced on for every stream. |
temperature | number | 0–2. |
top_p | number | 0–1. |
max_tokens | integer | Positive. Read the warning below before setting it. |
stop | string | string[] | At most 4 strings. |
tools | array | Function definitions. See Tool Calling. |
tool_choice | string | object | auto, none, required, or a named function. |
Unlisted parameters are forwarded unchanged. seed, presence_penalty, frequency_penalty,
response_format, parallel_tool_calls, user and anything else the provider accepts pass
straight through — the gateway validates what it knows and does not strip the rest, so SDK
features keep working without waiting on us.
Message roles
| Role | Use |
|---|---|
system | Instructions that frame the conversation |
user | Input from the person |
assistant | A previous model reply, including one carrying tool_calls |
tool | The result of a tool call; needs tool_call_id |
content may be a string, null (on assistant turns that only call tools), or an array of parts
for image input:
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in this image?" },
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0..." } }
]
}Response
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1787058728,
"model": "kimi-k3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The weather in Izmir right now is 31°C and sunny.",
"reasoning_content": "The tool returned 31°C and sunny, so I will report that."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 200,
"completion_tokens": 115,
"total_tokens": 315
}
}finish_reason is stop when the model finished, tool_calls when it wants a tool run, or
length if it hit a token cap.
Reasoning content
Kimi K3 thinks before it answers, and returns that thinking separately.
"message": {
"content": "Hello there, how are you?",
"reasoning_content": "The user asked for exactly five words. Let me count: Hello(1) there(2)..."
}contentis the answer. Show it to users.reasoning_contentis the thinking. It is not addressed to the user. Hide it, or put it behind a “Thinking” toggle the way the Devotel chat app does.- Do not send
reasoning_contentback in later turns. Sendroleandcontentonly.
Reasoning tokens are output tokens
Reasoning is counted inside completion_tokens and billed at the output rate. Some responses also
break it out:
"usage": {
"prompt_tokens": 94,
"completion_tokens": 799,
"total_tokens": 893,
"completion_tokens_details": { "reasoning_tokens": 782 }
}That is a real measurement: a “write a haiku about proxies” request spent 782 reasoning tokens to produce a 72-character poem. Budget accordingly — short answers are not cheap answers with this model.
Be careful with max_tokens. The cap covers reasoning and the answer. Set it too low and
the model spends the budget thinking, then gets cut off with finish_reason: "length" and an
empty content. If you do not have a specific reason to cap output, leave max_tokens unset —
that is what the Devotel chat app does.
Cached prompt tokens
The provider reports prompt-cache activity when it applies. These fields pass through untouched and vary by request:
"usage": {
"prompt_tokens": 200,
"prompt_tokens_details": { "cached_tokens": 12 },
"cache_read_input_tokens": 0,
"cache_created_input_tokens": 0
}Treat them as informational — prompt_tokens and completion_tokens are what Devotel meters.
Request size
Request bodies are capped at 2 MB. Larger requests get 413.
Not available yet
/v1/embeddings, /v1/completions (the legacy endpoint) and image generation are not implemented.
Unknown paths return 404 with code unknown_url.