Chat

OpenAI-compatible chat completions across the Cruxy tiers, streaming or not.

POST /api/v1/chat runs an OpenAI-compatible chat completion. You pick a public tier, send a list of messages, and read back either a single JSON completion or a stream of Server-Sent Events. Here is the smallest complete call:

curl https://api.cruxy.ai/api/v1/chat \
  -H "Authorization: Bearer $CRUXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kavi",
    "stream": false,
    "messages": [
      { "role": "user", "content": "Hello" }
    ]
  }'

Tiers, not provider models

model is a public tier, one of:

TierProfile
kaviDeep
vaaniBalanced
miraFast

The gateway resolves each tier to an upstream provider model on the server. You never send or receive an upstream provider id. Tiers are the stable contract, so your code keeps working when the backing model changes. Choosing a tier compares them; to discover the catalog at runtime rather than hardcoding these strings, see Models.

model also accepts "auto", which hands tier selection to the gateway's router. It resolves to a concrete tier before billing and the upstream call, and routed_tier on the response (see The cruxy block) names the tier that served. Auto routing covers what the router guarantees.

Request options

Beyond model and messages, the request accepts:

FieldTypeExamplePurpose
streambooleantrueStream SSE chunks. Optional and false by default, which returns one JSON object.
temperaturenumber0.7Sampling temperature.
max_tokensinteger1024Cap on tokens generated.
reasoning_effortstring"medium"How much the model thinks before answering: none, low, medium, or high (see Reasoning effort).
toolsarrayFunctions the model may call (see Define tools).
tool_choicestring or object"auto"Whether and which tool the model calls.
memoryobject{ "recall": true }Memory recall and capture for this request (see Memory).
rulesbooleanfalseStanding-rules injection for this request (see Standing rules).

Each messages entry has a role and content. Assistant turns that call tools carry tool_calls, and your replies to those calls carry a tool_call_id; both are covered in Handle tool calls.

Streaming vs. not

stream controls the response format.

stream: false

This is what you get when you omit stream: a single JSON object. Cruxy flattens it: the assistant reply is the top-level message, alongside finish_reason, usage, and the cruxy block. There is no OpenAI choices[] array. Read message.content directly, not choices[0].message.

{
  "id": "chatcmpl-8f3a1c2d",
  "model": "kavi",
  "message": { "role": "assistant", "content": "Namaste." },
  "finish_reason": "stop",
  "usage": {
    "input_tokens": 12,
    "output_tokens": 48,
    "billable_input_tokens": 12,
    "cache_read_tokens": 0,
    "cache_write_tokens": 0,
    "cost": { "tier": "kavi", "amount": "<amount>", "currency": "<currency>" }
  },
  "cruxy": {
    "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "routed_tier": "kavi",
    "reasoning_effort": "high"
  }
}

<amount> and <currency> are placeholders: a real amount is a number. See Usage and cost for the full cost object.

stream: true

You get an OpenAI-style Server-Sent Events stream: a sequence of chat.completion.chunk events, each on a data: line, terminated by a final data: [DONE]. Unlike the non-streaming reply, a chunk uses the OpenAI choices[] shape: each token arrives in choices[0].delta.content.

curl -N https://api.cruxy.ai/api/v1/chat \
  -H "Authorization: Bearer $CRUXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kavi",
    "stream": true,
    "messages": [
      { "role": "user", "content": "Write a haiku about monsoon rain." }
    ]
  }'

data: [DONE] is the only signal that the completion finished: a stream that ends without it failed partway. Streaming covers the frame order, the chunk and usage shapes, a complete TypeScript reader, and how to detect a failed stream.

The cruxy block

Cruxy-native fields are additive and namespaced under a cruxy key. They never alter the OpenAI-shaped fields, so a vanilla OpenAI client parses the response unchanged and ignores the extra key. On a chat response the block carries three fields for the turn:

  • request_id: a UUID for the call, e.g. a1b2c3d4-e5f6-7890-abcd-ef1234567890. Log it; it identifies the call in support requests and pairs with Feedback.
  • routed_tier: the tier that actually served the request. This is how you learn which tier ran when you send model: "auto".
  • reasoning_effort: the effort the call ran at, after any tier default is applied.

When they apply, the block also reports what the turn recalled, captured, or injected: memory_recall, memory_capture, rules_applied, files_omitted, and undeclared_tool_replay. Request IDs and metadata lists every field, and how to quote request_id when you report a problem.

Cost and token counts are not in this block; they live in usage.

Usage

Every chat response carries a usage object with the token accounting and the call's cost. The token fields depend on the response mode: non-streaming uses input_tokens and output_tokens; streaming uses prompt_tokens, completion_tokens, and total_tokens. Both also carry billable_input_tokens, cache_read_tokens, and cache_write_tokens.

usage.cost breaks down the price of the call:

FieldMeaning
tierThe public tier that served the request.
amountTotal cost of the call.
currencyCurrency of amount.
input_unitInput rate, per million tokens.
output_unitOutput rate, per million tokens.
cache_read_unitinput_unit × the cache discount, for cached reads.

Amounts and rates are specific to your account and shown in your Cruxy Console. Usage and cost covers which tokens are billed and how a stream reports usage.

Errors

Chat returns 400 for a malformed request, 401 unauthorized for a missing, expired, or revoked key, 429 when you hit a limit, such as rate_limited or budget_exhausted, and 5xx when the model call fails. With stream: false the gateway makes one attempt, bounded by the tier's deadline; an attempt that runs past it is 504 upstream_timeout, which the gateway does not retry and does not bill (see Timeouts). See Errors for the response shape, Errors and retries for every code and which ones to retry, and Rate limits and budgets for how to tell a rate limit from a budget.

Next steps

Was this page helpful?