Chat completions
Kirim percakapan dan terima pesan berikutnya dari model. Endpoint ini kompatibel dengan OpenAI Chat Completions API, jadi SDK resmi OpenAI langsung bisa dipakai setelah base URL diarahkan ke https://kenari.id/v1.
POST /v1/chat/completions
Autentikasi dengan Authorization: Bearer kn-.... Lihat Autentikasi & API key.
Request
Section titled “Request”| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
model | string | Ya | Apa yang dijalankan. Lihat Pilih model. |
messages | array | Ya | Percakapan sejauh ini. Lihat Messages. |
stream | boolean | Tidak | Setel true untuk menerima Server-Sent Events. Bawaan false. Lihat Streaming. |
stream_options | object | Tidak | Diteruskan ke provider yang dilayani dalam format OpenAI. Ini tidak mengubah chunk usage milik kenari sendiri. Lihat Streaming. |
max_tokens | integer | Tidak | Batas atas token yang dihasilkan, termasuk penalaran. Bawaan 65.536. |
max_completion_tokens | integer | Tidak | Alias max_tokens. max_tokens dipakai bila keduanya dikirim. |
temperature | number | Tidak | Temperatur sampling. |
top_p | number | Tidak | Nucleus sampling. |
stop | string atau array | Tidak | Teks yang menghentikan generasi. |
n | integer | Tidak | Hanya 1 yang didukung. Nilai lebih besar mengembalikan 400. |
user | string | Tidak | Id untuk pengguna akhirmu. Diteruskan sebagai metadata request kalau provider menerimanya. |
tools | array | Tidak | Function tool, dan server tools kenari seperti {"type": "kenari:web_search"}. Lihat Function calling dan Server tools. |
tool_choice | string atau object | Tidak | auto, none, required, atau {"type": "function", "function": {"name": "..."}}. Bentuk lain diperlakukan sebagai auto, kecuali {"type": "function"} tanpa function.name, yang mengembalikan 400. |
response_format | object | Tidak | Minta output JSON. Lihat Output terstruktur. |
reasoning_effort | string | Tidak | none, minimal, low, medium, high, xhigh, atau max. Lihat Penalaran. |
reasoning | object | Tidak | {"effort", "enabled", "max_tokens", "exclude"}. Dipakai menggantikan reasoning_effort bila keduanya ada. {"enabled": true} tanpa effort atau max_tokens berarti medium, dan {"enabled": false} mematikan penalaran. Lihat Penalaran. |
plugins | array | Tidak | Opsional. file-parser memilih cara dokumen yang dilampirkan dibaca: auto, pdf-text, native, atau ocr. Lihat Baca dokumen. |
prompt_cache_key | string | Tidak | Id tetap untuk satu percakapan, supaya request lanjutan memakai ulang prefix yang sudah di-cache. Maksimal 512 byte. Nilai yang lebih panjang atau kosong diabaikan. Lihat Prompt caching. |
kenari tidak menafsirkan field OpenAI lain seperti frequency_penalty, presence_penalty, atau seed. Field itu diteruskan apa adanya ke provider bila model dilayani dalam format OpenAI, dan dibuang bila tidak, jadi jangan bergantung padanya. Hal yang sama berlaku untuk response_format bila kamu juga menyertakan server tool kenari.
Pilih model
Section titled “Pilih model”Nilai model | Contoh | Yang dijalankan |
|---|---|---|
| Id katalog | claude-sonnet-5-5 | Modelnya, ditagih per token. Lihat id yang tersedia di Model & harga. |
Id :free | step-3-7-flash:free | Versi gratis sebuah model. Lihat Model gratis. |
| Nama rute | kenari-free | Rute buatanmu, atau kenari-free bawaan. Lihat Routing & rute. |
| Id BYOK | byok/deepseek/deepseek-v4-flash | Model di provider milikmu sendiri, dengan format byok/<provider>/<model>. kenari tidak menagih token untuknya. Lihat BYOK. |
Bagian <model> pada id BYOK adalah id milik provider, disalin persis. Huruf besar dan kecil dibedakan, dan id boleh memuat garis miring.
Messages
Section titled “Messages”messages adalah array objek {"role", "content"}.
| Role | Catatan |
|---|---|
system, developer | Instruksi. Yang berada di awal digabung menjadi satu system prompt. Yang muncul setelah pesan user tetap di posisinya. |
user | content berupa string atau array bagian. |
assistant | Balasan model sebelumnya. Boleh membawa tool_calls, serta reasoning_content atau reasoning untuk mengembalikan penalaran sebelumnya ke model. |
tool | Hasil sebuah fungsi. Setel tool_call_id ke id panggilan yang dijawabnya. |
Role lama function mengembalikan 400.
Pesan user bisa mencampur bagian konten berikut. Jenis bagian yang tidak dikenal diabaikan.
| Bagian | Bentuk | Lihat |
|---|---|---|
text | {"type": "text", "text": "..."} | |
image_url | {"type": "image_url", "image_url": {"url": "https://example.com/a.png"}} | Gambar & berkas sebagai input |
file | {"type": "file", "file": {"filename": "a.pdf", "file_data": "data:application/pdf;base64,..."}} | Gambar & berkas sebagai input |
url berisi URL https atau data URL seperti data:image/png;base64,....
Bagian file membawa dokumen (PDF, teks, atau gambar) dan bekerja di model apa pun. kenari membacanya sebelum request dirutekan, dan model menerima teks serta gambar halamannya, ditagih sebagai token input biasa, jadi paket yang mencakup model itu juga mencakup dokumennya. Satu request boleh membawa beberapa dokumen. Plugin file-parser yang opsional mengubah cara dokumen dibaca, misalnya "plugins": [{"id": "file-parser", "pdf": {"engine": "ocr"}}] untuk OCR berbayar, yang menerima satu dokumen per request dan ditagih per halaman. Lihat Baca dokumen.
Response
Section titled “Response”Response berupa objek OpenAI chat.completion dengan satu choice.
{ "id": "4f1c9a2e7b3d5a60", "object": "chat.completion", "created": 1790000000, "model": "step-3-7-flash:free", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Halo! Ada yang bisa dibantu?" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 12, "completion_tokens": 9, "total_tokens": 21, "prompt_tokens_details": { "cached_tokens": 0 } }}| Field | Keterangan |
|---|---|
choices[0].finish_reason | stop, length (mencapai max_tokens), tool_calls, atau content_filter. |
choices[0].message.tool_calls | Ada bila model memanggil fungsi. arguments berupa string JSON. |
choices[0].message.reasoning dan reasoning_content | Teks penalaran model, dengan isi yang sama di kedua nama. Hanya ada bila model bernalar dan kamu tidak menyetel reasoning.exclude. |
choices[0].message.sources | Ada bila server tool kenari mencari di web. Berisi daftar {url, title} untuk tiap sumber. |
choices[0].message.annotations | Entri url_citation untuk sumber tersebut, dan satu entri file untuk dokumen yang dibaca dengan engine ocr. Lihat Baca dokumen. |
usage.prompt_tokens | Token input, termasuk yang dari cache. |
usage.completion_tokens | Token output. Token penalaran ditagih sebagai output. |
usage.prompt_tokens_details.cached_tokens | Token input yang dilayani dari cache. Di response non-streaming, field ini selalu ada dan bernilai 0 bila tidak ada. Di stream, prompt_tokens_details muncul di chunk terakhir hanya bila token cache di atas 0. Lihat Prompt caching. |
id bernilai sama dengan header response x-request-id. Sebutkan nilainya saat menghubungi dukungan.
Contoh
Section titled “Contoh”curl https://kenari.id/v1/chat/completions \ -H "Authorization: Bearer $KENARI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "step-3-7-flash:free", "messages": [ { "role": "system", "content": "Kamu menjawab dengan singkat." }, { "role": "user", "content": "Halo!" } ] }'Python
Section titled “Python”import osfrom openai import OpenAI
client = OpenAI(base_url="https://kenari.id/v1", api_key=os.environ["KENARI_API_KEY"])
response = client.chat.completions.create( model="step-3-7-flash:free", messages=[ {"role": "system", "content": "Kamu menjawab dengan singkat."}, {"role": "user", "content": "Halo!"}, ],)print(response.choices[0].message.content)JavaScript
Section titled “JavaScript”import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://kenari.id/v1", apiKey: process.env.KENARI_API_KEY,});
const response = await client.chat.completions.create({ model: "step-3-7-flash:free", messages: [ { role: "system", content: "Kamu menjawab dengan singkat." }, { role: "user", content: "Halo!" }, ],});console.log(response.choices[0].message.content);Penagihan
Section titled “Penagihan”Setiap request ditagih per token sesuai harga Input, Output, Cache read, dan Cache write model tersebut. Model :free tidak dikenai biaya. Sebelum request berbayar dijalankan, kenari memeriksa saldomu terhadap kemungkinan terburuk dari max_tokens, jadi setel batas yang lebih kecil bila kamu memanggil model mahal dengan saldo tipis. Lihat Cara penagihan.
Error memakai envelope OpenAI: {"error": {"message", "type", "param", "code"}}. Di sini hanya error yang khusus untuk endpoint ini. Sisanya ada di Error.
| Status | Kode | Kapan |
|---|---|---|
| 400 | bad_request | Body bukan JSON yang valid atau tidak memuat model atau messages. n di atas 1. Ada role yang tidak dikenal. Bagian file tidak punya objek file. Dokumen bertipe tidak didukung, terlalu besar, rusak, atau terlalu banyak halamannya. PDF punya lebih dari 20 halaman tanpa lapisan teks. Dokumen membuat request lebih panjang dari konteks model. Engine file-parser bukan auto, pdf-text, native, atau ocr. Dengan native, model tidak membaca jenis berkas itu, atau bagian itu berisi gambar. Dengan ocr, request membawa lebih dari satu bagian file. |
| 400 | model_not_found | model tidak ada di katalog dan bukan salah satu rutemu. |
| 400 | bad_request | Model bukan model chat, seperti model gambar atau suara. Pesan errornya menyebut endpoint yang harus dipakai. |
| 400 | context_length_exceeded | Request lebih panjang dari jendela konteks model, yang tampil sebagai context_length di Model & harga. |
| 400 | byok_malformed_model_id | Id byok/ tidak berformat byok/<provider>/<model>. |
| 400 | byok_provider_not_found | Kamu belum menyimpan key untuk provider yang disebut di id byok/. |
| 403 | subscribers_only | Model hanya untuk akun dengan langganan aktif. Lihat Langganan. |
| 403 | byok_credential_unavailable | Kamu punya key untuk provider itu, tetapi tidak ada yang bisa melayani saat ini. |
| 403 | byok_spend_capped_key | API key punya batas pengeluaran, yang tidak bisa dipakai untuk request BYOK. |