POST
/v1/chat/completionsChat Completions
Creates a chat completion for the given messages. Streaming supported via SSE. Pass model as an array for cost-aware fallback (e.g. ["fc:openai/gpt-4o-mini", "fc:openai/gpt-4o"]) — Ringside walks the chain on 5xx / schema-validation failure and surfaces the winner via X-Ringside-Model-Used.
Request
- HTTP
- POST
- URL
- /v1/chat/completions
- Auth
- api_key, client_token
Try it
curl https://api.fightclub.pro/v1/chat/completions \
-H "Authorization: Bearer $FC_API_KEY" \
-H "FC-Customer: cus_42" \
-H "Content-Type: application/json" \
-d '{"model":"fc:openai/gpt-4o-mini","messages":[{"role":"user","content":"Hello"}]}'
# Cost-aware fallback: try cheap first, escalate on failure
curl https://api.fightclub.pro/v1/chat/completions \
-H "Authorization: Bearer $FC_API_KEY" \
-d '{
"model": ["fc:openai/gpt-4o-mini", "fc:openai/gpt-4o"],
"messages": [{"role":"user","content":"Hello"}]
}'
# Response headers indicate which model served:
# X-Ringside-Model-Used: fc:openai/gpt-4o
# X-Ringside-Models-Tried: fc:openai/gpt-4o-mini,fc:openai/gpt-4o
# X-Ringside-Fallback-Triggered: trueExample response
{
"id": "chatcmpl_8Xb1cD2eF",
"object": "chat.completion",
"created": 1782300000,
"model": "openai/gpt-4o-mini",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": { "role": "assistant", "content": "Hello, good to meet you." }
}
],
"usage": { "prompt_tokens": 9, "completion_tokens": 7, "total_tokens": 16 }
}
// Response headers also carry:
// X-Ringside-Model-Resolved: openai/gpt-4o-mini
// X-Ringside-Cost-Usd: 0.0000042A representative 200 body. Ids and timestamps are illustrative.
Body parameters
| Name | Type | Default | Description |
|---|---|---|---|
| model* | string | string[] | — | Prefixed model ref (e.g. "fc:openai/gpt-4o-mini"). Must start with fc:, match:, slot:, or dyn: — a bare name like "gpt-4o" returns 400 invalid_model_ref. Append @<region> for data residency (e.g. fc:openai/gpt-4o@eu). Pass an array to enable a cost-aware cascade: the first model is tried, escalating to the next on 5xx / upstream_unavailable / response_schema_validation_failed. An array with stream:true is rejected 400 fallback_with_stream_unsupported. |
| messages* | array | — | List of message objects, each { role, content }. role is system | user | assistant | tool. |
| stream | boolean | false | When true the response is an SSE stream of chat.completion.chunk objects terminated by data: [DONE]. Cannot be combined with a model:[] cascade or a response_format json_schema. |
| stream_options | object | optional | { include_usage: true } appends a final chunk carrying the usage object. Only meaningful with stream:true. |
| max_tokens | integer | optional | Cap on generated tokens. |
| temperature | number | optional | Sampling temperature, 0-2. Higher is more random. |
| top_p | number | optional | Nucleus sampling, 0-1. Use this or temperature, not both. |
| tools | array | optional | OpenAI-compatible function tool definitions: [{ type: "function", function: { name, description, parameters } }]. |
| tool_choice | string | object | optional | auto | none | required, or { type: "function", function: { name } } to force a specific tool. |
| response_format | object | optional | { type: "json_object" } for loose JSON, or { type: "json_schema", json_schema: {...} } for strict output enforced across all 19 providers — non-native models run a runtime ajv loop with up to 2 re-prompts; persistent failure is 422 response_schema_validation_failed. Cannot be combined with stream:true (400 streaming_with_schema_fallback_unsupported). |
| moderation | string | none | none | pre | post | pre_and_post. pre screens the prompt before spending, post screens the completion. A flagged call returns 400 moderation_flagged. |
| prompt_cache | string | off | auto | off. auto lets Ringside reuse provider-side prompt caches where supported. |
| cache_control | object | optional | { type: "ephemeral", ttl?: "5m" | "1h" } marks the response cacheable by Ringside's own response cache. See also the FC-Cache request headers. |
| user | string | optional | Opaque end-user id passed through to providers that accept it. |
* required.
Headers
| Header | Dir | Description |
|---|---|---|
| FC-Customer | req → | Attribute the call to a customer (by id or ext:<external_id>) for budget enforcement + reporting. Auto-creates the customer if new. |
| X-FC-Billable-Amount | req → | Override the metered amount in micro-dollars (for resold / marked-up pricing). |
| FC-Session-Id | req → | Group related calls under one session id in reporting. |
| FC-Property-* | req → | Arbitrary dimension tags (e.g. FC-Property-Team: growth) surfaced in usage breakdowns. |
| FC-Tag-* | req → | Free-form labels attached to the usage record. |
| idempotency-key | req → | De-dupe retries (24 h replay window, scoped per dev + customer). Rejected with a cascade (400 fallback_with_idempotency_unsupported). |
| FC-Cache / FC-Cache-TTL | req → | Opt the call into Ringside's response cache and set its TTL. |
| X-Request-Id | ← res | Unique id for this request; quote it in support tickets. |
| X-Ringside-Model-Resolved | ← res | The concrete fc: model a match:/slot:/dyn: ref resolved to. |
| X-Ringside-Model-Used / -Models-Tried / -Fallback-Triggered | ← res | On a cascade: the winning model, the full chain tried, and whether fallback fired. |
| X-Ringside-Model-Newer-Available | ← res | Set when a newer model in the same family exists. |
| X-Ringside-Dynamic-Profile / -Reason | ← res | For dyn: refs: which profile was picked and why. |
| X-Ringside-Wallet-Balance / -Wallet-Deducted | ← res | Remaining wallet balance and the amount this call deducted. |
| X-RateLimit-Remaining | ← res | Requests left in the current window. |
| FC-Cache-Status / -Cache-Key / -Cache-Age | ← res | hit | miss, the cache key, and age in seconds when served from cache. |
Response fields
| Name | Type | Description |
|---|---|---|
| id | string | Completion id (chatcmpl-*). |
| object | string | "chat.completion" (or "chat.completion.chunk" when streaming). |
| created | integer | Unix timestamp. |
| model | string | The model that served the request. |
| choices | array | [{ index, message: { role, content, tool_calls? }, finish_reason }]. |
| usage | object | { prompt_tokens, completion_tokens, total_tokens }. |
Errors
- 401
missing_tokenNo Authorization header was sent. - 401
invalid_auth_schemeThe scheme was neither Bearer nor Client. - 401
invalid_token_formatA Bearer token not prefixed ko_. - 401
invalid_tokenThe API key is unknown, revoked or expired. - 403
insufficient_scopeThe key is valid but lacks the required scope. - 400
invalid_jsonRequest body is not valid JSON. - 400
missing_modelmodel is absent. - 400
missing_messagesmessages is absent or empty. - 400
invalid_model_refmodel is not a fc:/match:/slot:/dyn: ref. - 400
invalid_model_for_chatThe model resolved to one that cannot do chat. - 400
moderation_flaggedPrompt or completion failed moderation. - 400
streaming_with_schema_fallback_unsupportedresponse_format json_schema requested with stream:true. - 400
fallback_with_stream_unsupportedmodel:[...] cascade requested with stream:true. - 400
fallback_with_idempotency_unsupportedmodel:[...] cascade requested with an Idempotency-Key. - 402
customer_budget_exceededThe FC-Customer's monthly budget is spent. - 402
customer_wallet_emptyThe customer's prepaid balance is zero/insufficient. - 402
wallet_emptyYour developer wallet balance is zero; top up. - 422
response_schema_validation_failedOutput did not match response_format.json_schema after 2 re-prompts. - 429
customer_rate_limit_exceededThe customer's rpm cap was hit. - 429
edge_rate_limit_exceededThe per-IP edge limiter tripped. - 429
client_token_rate_limit_exceededThe per-client-token rpm ceiling was hit. - 429
lazy_create_rate_exceededThe customer auto-create ceiling was hit (100/hour or 1000/day). - 503
platform_not_configuredNo upstream provider key is configured for the resolved model. - 503
upstream_unavailableProvider errored and no fallback succeeded.
See the full error reference.
Notes
- ·This endpoint is OpenAI wire-compatible: point the OpenAI SDK at base_url https://api.fightclub.pro/v1 and the only required change is the model ref prefix.
- ·Spend is metered per call against your wallet, or against the FC-Customer when that header is present. Read X-Ringside-Wallet-Deducted to see the exact charge.
- ·Streaming uses Server-Sent Events; add stream_options:{include_usage:true} to get a final usage chunk.
- ·A cascade (model array) is mutually exclusive with both streaming and idempotency keys.