Chat completions
This page documents POST /v1/chat/completions end to end: every field you should care about as an integrator, how responses and streams are shaped, how usage and prompt caching surface, how rate limits behave, and how overage protection can silently tighten completion budgets on platform-metered traffic.
If you read nothing else: send Authorization: Bearer <api-key>, Content-Type: application/json, and a body with at least messages. Everything else is optional sugar—until it isn’t, which is why the tables below exist.
Endpoint & host
http
POST /v1/chat/completionsProduction base URL: https://api.rikaii.com
Full URL: https://api.rikaii.com/v1/chat/completions
Authentication
| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer <api-key> — opaque key from Dashboard → API keys (format is commonly rk_live_…). |
Content-Type | Yes | application/json |
See Authentication.
Request model vs. response model
You may send model for OpenAI SDK compatibility. Interpretation depends on BYOK and the Routing engine. The model field in JSON responses and SSE chunks is always the gateway catalog slug (same family as GET /v1/models → modelSlug), not necessarily the upstream vendor’s private SKU string.
Request body — parameter reference
Unless noted, omitted fields are left unset or forwarded as-is after sanitization. Unknown JSON keys are generally ignored at ingress where safe.
Core
| JSON key | JSON type | Required | Valid range / notes |
|---|---|---|---|
messages | array of message objects | Yes | At least one element for typical calls; each object follows OpenAI’s chat message shape (see Messages). |
model | string | No | Non-empty string when you want to pin or hint a catalog slug; routing may still override unless Tier 1 BYOK routing applies—see Routing. |
Sampling & length
| JSON key | JSON type | Required | Valid range / notes |
|---|---|---|---|
temperature | number | No | Typically 0 ≤ x ≤ 2 on OpenAI-style models; forwarded when the resolved upstream supports it. Unsupported values may be clamped or dropped during sanitization. |
top_p | number | No | 0 < x ≤ 1 nucleus sampling when supported. |
max_tokens | integer | null | No | When present as a positive integer, caps generated tokens on providers that use the legacy field. Merged with max_completion_tokens per family rules. |
max_completion_tokens | integer | null | No | Preferred on reasoning-first / newer OpenAI families; the gateway may send only max_completion_tokens or only max_tokens upstream depending on the resolved model. |
n | integer | No | Number of completion choices; must be positive when set. Values > 1 may be unsupported or downgraded on some routes—prefer 1 unless you have verified behavior for your slug. |
Effective completion ceiling: before the upstream call, the gateway may set the outbound completion cap to the minimum of:
- your requested cap (from
max_tokens/max_completion_tokenscoalescing), - the catalog hard maximum for the resolved upstream model,
- an optional budget-derived cap from Overage protection on platform-metered traffic.
So your requested 4096 might become a lower integer without error—that is expected when a tighter bound wins.
Penalties & seeds
| JSON key | JSON type | Required | Valid range / notes |
|---|---|---|---|
presence_penalty | number | No | OpenAI-style -2.0 … 2.0 when supported; stripped or clamped on incompatible providers. |
frequency_penalty | number | No | OpenAI-style -2.0 … 2.0 when supported; stripped or clamped on incompatible providers. |
seed | integer | No | Deterministic sampling when supported; may be stripped for some providers. |
Stop sequences & logprobs
| JSON key | JSON type | Required | Valid range / notes |
|---|---|---|---|
stop | string | string[] | null | No | Up to provider limits; arrays may be normalized. |
logprobs | boolean | No | When true, requests logprobs where the upstream supports them. |
top_logprobs | integer | No | 0–20 typical on OpenAI when logprobs is enabled. |
Tools & structured output
| JSON key | JSON type | Required | Valid range / notes |
|---|---|---|---|
tools | array | No | Tool definitions (OpenAI tool schema). |
tool_choice | string | object | No | auto, none, required, or a specific tool {"type":"function","function":{"name":"…"}} object. |
parallel_tool_calls | boolean | No | When true, allows parallel tool calls if the upstream supports it. |
response_format | object | No | Structured output hints (e.g. JSON mode) when supported. |
Streaming & misc
| JSON key | JSON type | Required | Valid range / notes |
|---|---|---|---|
stream | boolean | No | true → SSE stream (text/event-stream). Omit or false for single JSON object. |
logit_bias | object | null | No | Map of token id → bias; may be stripped when the resolved provider does not support it. |
user | string | No | End-user id for abuse analytics; opaque string (≤ 64 chars recommended). |
Rikaii may apply additional parameter sanitization—see Parameter sanitization.
Messages
Each element of messages is an object:
| Field | Type | Notes |
|---|---|---|
role | string | system, user, assistant, tool, or provider-specific roles when applicable. |
content | string | array | Plain text or multimodal parts per OpenAI’s structured content conventions. |
name | string | Optional short name for the speaker on some providers. |
tool_calls | array | On assistant messages when the model issued tool calls. |
tool_call_id | string | On tool messages—links the tool result to the call id. |
Keyword routing (Dashboard rules) only inspects the last user message text extracted from messages—see Routing.
Minimal & maximal examples
Minimal valid body:
json
{
"messages": [{ "role": "user", "content": "Hello" }]
}Typical production body:
json
{
"model": "gpt-4o-mini",
"messages": [
{ "role": "system", "content": "You are a concise assistant." },
{ "role": "user", "content": "Summarize TLS 1.3 in one paragraph." }
],
"temperature": 0.2,
"max_tokens": 512,
"stream": false
}Successful response (non-streaming)
HTTP 200 with Content-Type: application/json.
Top-level fields mirror OpenAI’s chat.completion object. Integrators should treat unknown keys as forward-compatible.
| Field | Type | Meaning |
|---|---|---|
id | string | Completion id (gateway-generated or forwarded where applicable). |
object | string | Typically chat.completion. |
created | integer | Unix timestamp (seconds). |
model | string | Gateway catalog slug used for billing and logs. |
choices | array | One entry per n (almost always length 1). |
usage | object | Token accounting—see Usage & caching below. |
choices[] shape
| Field | Type | Meaning |
|---|---|---|
index | integer | Zero-based choice index. |
message | object | { "role": "assistant", "content": "…" } plus optional tool_calls, reasoning_content, etc. |
finish_reason | string | null | stop, length, tool_calls, content_filter, etc., per OpenAI semantics. |
logprobs | object | null | Present when requested and supported. |
Reasoning & transparency
Some models return chain-of-thought or internal reasoning separately from user-visible content. Rikaii preserves OpenAI-compatible fields such as reasoning_content on assistant messages when the upstream exposes them.
If the model consumes completion tokens but returns no user-visible content or reasoning_content, the gateway may attach a short plain-language note in reasoning_content so non-zero usage.completion_tokens is not mysterious. Treat that as billing transparency, not end-user copy.
X-Rikaii-Fallback
When a fallback hop (configured fallback model on PAYG, or Safety Net retry on eligible BYOK) fulfills a non-streaming request, the gateway may add:
http
X-Rikaii-Fallback: trueOmitted when the primary path succeeded on the first try. Streaming completions do not attach this header today even if a fallback ran internally—see Fallback routing.
Usage & caching
How usage is produced
- The upstream LLM returns a JSON completion (or final stream chunk) that includes a
usageobject when the provider supports it. - The gateway parses that object into its internal OpenAI-shaped record.
prompt_tokens,completion_tokens,total_tokens, and nestedprompt_tokens_details/completion_tokens_detailsare not recomputed by Rikaii for billing display in the wire response—they are whatever the provider supplied (after JSON deserialization). - The gateway does rewrite other parts of the payload for consistency—most notably the top-level
modelstring to the gateway catalog slug, plus optional assistant message normalization (for example splitting or annotating reasoning-related text for SDK compatibility). Those edits do not replace the numericusageblock.
So usage.prompt_tokens_details.cached_tokens appears only when the upstream included it (or compatible fields that deserialize into that shape). Rikaii does not invent cache hit counts.
| JSON key | Type | Meaning |
|---|---|---|
prompt_tokens | integer | Input tokens as reported by the provider. |
completion_tokens | integer | Output tokens as reported (may include reasoning on some SKUs). |
total_tokens | integer | Sum when the provider sets it. |
prompt_tokens_details | object | omitted | When present, may include cached_tokens for providers that expose prompt-cache accounting (for example OpenAI-style APIs). Semantics are defined by that vendor. |
completion_tokens_details | object | omitted | When present, may include reasoning_tokens and other vendor counters. |
Prompt caching takeaway: treat cached_tokens as an upstream telemetry field. If it is missing, the provider may not expose cache splits for that model, or the response path omitted them. If it is non-zero, your prompt likely hit that provider’s prompt cache for part of the input—compare billing and latency across cold vs warm repeats in your integration tests.
Streaming (stream: true)
Set "stream": true. The gateway returns HTTP 200 with Content-Type: text/event-stream and a body encoded as Server-Sent Events (SSE).
Event framing
- Each logical chunk is delivered as an SSE
data:line containing a JSON object (OpenAIchat.completion.chunkshape). - The stream ends with a
data: [DONE]sentinel (OpenAI convention).
Chunk object shape
Streaming objects reuse OpenAI’s chunk schema:
| Field | Notes |
|---|---|
id, object, created, model | Metadata repeated on early chunks; model remains the gateway catalog slug. |
choices | Array with delta objects instead of full message objects mid-stream. |
choices[].delta | Partial content, optional role, tool_calls fragments, etc. |
choices[].finish_reason | Usually null until the final chunk for that choice, then stop, length, tool_calls, … |
usage | Often omitted on intermediate chunks. For Rikaii-built outbound streams, stream_options.include_usage is enabled toward the upstream so a final chunk may include usage when the provider supports it—still, do not depend on usage unless you observed it for your model. |
Client implementation notes
- Use a line-oriented reader; do not buffer the entire response.
- Official OpenAI SDKs handle streaming when
stream: trueis set; point the base URL athttps://api.rikaii.comand keep the same path. - Always handle
[DONE]and abrupt disconnects (TCP close) as terminal states.
Rate limiting (HTTP 429)
Chat completions enforce a workspace-level requests-per-minute bucket keyed off your API key’s owner. Defaults:
| Workspace tier | RPM |
|---|---|
| Standard (non‑Pro PAYG) | 60 |
| Pro | 120 |
A custom RPM stored on the workspace (support/operations only—not editable in the Dashboard) replaces those defaults for all keys; see Rate limits.
When exceeded:
- HTTP status:
429 Too Many Requests - Body: OpenAI-compatible JSON error, typically
error.code = "rate_limit_exceeded" - Headers: Do not rely on
Retry-AfterorX-RateLimit-*for programmatic backoff—the gateway does not document stable quota headers for chat. Use exponential backoff with jitter.
If the limiter backend is unhealthy, you may receive 503 instead (fail-closed). See Rate limits.
Billing guards (HTTP 402)
402 Payment Required indicates a billing prerequisite or no affordable headroom for a metered call. Common cases:
- Monthly spend cap reached.
- Prepaid / credit exhausted for the key or workspace.
- Overage protection math leaves zero affordable completion tokens after estimated prompt retail—see Overage protection.
Always parse the JSON error.message for operator-facing detail.
Errors & validation
All non-2xx responses with JSON bodies use the nested error object described in Errors. Branch on error.code first.
OpenAI SDK compatibility
Pattern (pseudo-code):
text
base_url = "https://api.rikaii.com"
default_headers["Authorization"] = "Bearer " + RIKAII_API_KEYThen call POST /v1/chat/completions exactly like OpenAI’s Chat Completions API. Tool calls, multimodal arrays, and streaming should work where the resolved upstream supports them.