Prefer Swagger UI? Click hereThe same API in the classic OpenAPI explorer.
// get started · 7 min read

Key concepts

A definition-list map of every primitive you will meet in the Ringside API, from the developer key and customer through wallets, model references, vector stores, assistants, webhooks and batches. Each one gets a precise sentence or two and a link to its deep page. Read it once and the rest of the docs slot into place.

Ringside is a metering and routing layer in front of 19 LLM providers. You hold one developer account; you mint API keys from it; every call you make flows through the metering layer, gets priced, and draws down a wallet. Most of the surface is OpenAI wire-compatible, so the SDK you already use keeps working. The concepts below are the parts that are Ringside's own: the ones that turn a raw provider proxy into a billable multi-tenant platform you can resell.

Read this page as a territory map. Each term is defined precisely, with the real id prefix, header name, default or limit, and a link to the concept or reference page that covers it in full. Where a primitive is also an HTTP resource, the link points at its endpoint reference under /docs/<endpoint>.

Base URL and hosts

The API base is https://api.fightclub.pro/v1. The dashboard, playground and these docs live at https://ringside.fightclub.pro. The only versioning is the /v1 path segment. There is no version header.

Identity and access

Two kinds of credential reach the API, and the Authorization scheme is what tells them apart. A developer key is your root credential and uses Bearer. A client token is a short-lived, browser-safe credential scoped to a single customer and uses the Client scheme. Anything else is rejected with a 401 whose message reads "Authorization header must start with 'Bearer ' or 'Client '".

Developer account
Your tenant. It owns every key, customer, wallet, vector store, webhook and assistant you create. All resources are private to the account; cross-account access returns 404, never 403, so ids cannot be probed for existence. See Authentication.
API key
Your root credential, literally ko_ followed by 64 hex characters. Sent as Authorization: Bearer ko_<key>, SHA-256 hashed at rest. There is no test-vs-live distinction. Create and revoke keys in the dashboard at /app/api-keys. Keep it server-side only. See Authentication.
Client token
A short-lived JWT (jti prefix ctok_) minted server-side via POST /v1/customers/{id}/client_tokens so a browser can call the API without ever shipping your key. The browser sends Authorization: Client <jwt> (the Client scheme, not Bearer). Default ttl 900s (min 60, max 3600), default rpm 60 (max 600, clamped to the customer rpm). Minting needs a key with the api:client_tokens scope and is rate-limited to 100/min per customer. See Authentication.
# Developer key (server-side): Bearer scheme
curl https://api.fightclub.pro/v1/chat/completions \
  -H "Authorization: Bearer ko_<64-hex>" \
  -d '{"model":"fc:openai/gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}'

# Client token (browser): Client scheme, scoped to one customer
curl https://api.fightclub.pro/v1/chat/completions \
  -H "Authorization: Client <jwt>" \
  -H "Origin: https://app.example.com" \
  -d '{"model":"fc:openai/gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}'

Customers and spend

A customer is the first-class end-user primitive. It is how you attribute spend, enforce a budget and scope a client token. If you resell access to your own users, each one becomes a customer, and Ringside tracks their cost, optional markup and margin for you. Customers are cheap and optional. A bare metered call with no customer attribution just bills your developer wallet.

Customer
An end-user you provision (id prefix cus_). Carries external_id (your own id, addressable later as ext:<external_id> on every customer subroute), display_name, monthly_budget_usd, rpm_limit, tpm_limit and metadata up to 4 KB. Over budget returns 402 customer_budget_exceeded. See The customer and spend model and the Customers reference.
Provisioned account
A full, isolated Ringside account you create for a customer via POST /v1/accounts using your rsk_ developer provisioning key. Unlike a customer, it has its own login, vault, ko_ key and (optionally) Brain. Its owner_type is api_end_user and its id is a UUID, not a prefixed id. Distinct from a cus_ customer, which is a billing sub-entity with no login. See Provision accounts for your customers.
FC-Customer header
Attribute a metered call to a customer by setting FC-Customer to the cus_ id, an ext:<external_id>, or a plain external id string. Unknown values lazy-create the customer unless you also send FC-Customer-Strict: true. See The customer and spend model.
Developer wallet
Your prepaid balance. Every call draws from it unless the call is attributed to a customer that carries its own wallet. An empty developer wallet returns 402 wallet_empty. See Metering and billing.
Customer wallet
An optional prepaid balance per customer, topped up via POST .../wallet/topup and corrected via .../wallet/adjust. When a call is attributed to a customer that has a wallet, the charge lands there instead of your developer wallet; an empty one returns 402 customer_wallet_empty. See The customer and spend model.
Budget
A customer's monthly_budget_usd ceiling. It is independent of the wallet: a budget block (402 customer_budget_exceeded) can fire while your wallet still holds money, because the budget is a spend cap, not a balance. See The customer and spend model.

Metering, margin and billable amount

Every billable call is priced and the charge is reported back on the response. Two headers carry the numbers. X-Ringside-Wallet-Balance is the balance after the call and X-Ringside-Wallet-Deducted is the exact amount this call cost. If you resell, you set what the customer pays, separate from what the call actually cost you, and the gap is your margin.

Billable amount
What a customer is billed for a call, which can differ from raw provider cost. Set a default per customer with default_billable_markup_pct or default_billable_amount_usd (mutually exclusive; setting both returns 400 billable_defaults_mutex), or override one call with the X-FC-Billable-Amount request header in micro-dollars. See Metering and billing.
Margin
Billable amount minus raw cost, accumulated per customer. Read it at GET /v1/customers/{id}/margin. This is the number you watch when you resell at a markup. See Metering and billing.
Request id
A unique id per request, prefix req_, returned on the X-Request-Id response header. It is a header, never a body field. Quote it in support tickets and log it on every call. See Observability.
Idempotency key
A header, name exactly idempotency-key (lowercase), that de-dupes retries within a 24 h replay window, scoped to (developer, customer, key). Combining it with a model:[] fallback cascade returns 400 fallback_with_idempotency_unsupported. See Idempotency and reliability.
Branch on code, not message

Every error body is { "error": { "type", "code", "message", "param"?, "retry_after_seconds"? } }. type, code and message are always present; param appears on field-validation errors and retry_after_seconds on rate-limit errors. Always branch your code on code, never on the human-readable message. See the Error catalog.

Model references

You never pass a bare model name. Every model ref carries a prefix that tells Ringside how to resolve it, and a bare name like gpt-4o is rejected with 400 invalid_model_ref. The prefix is the difference between pinning an exact model forever and asking for "the current best Sonnet". Pick the one that matches how tightly you want to control the target.

The four model-ref prefixes. The resolved target comes back on X-Ringside-Model-Resolved.
PrefixFormResolution
fc:fc:openai/gpt-4o-miniPinned. Exactly this provider and model, no resolution.
match:match:anthropic/sonnet>=4Resolves a constraint to a concrete fc: model at call time.
slot:slot:fastA dev-defined alias you point at a model and can re-point without code changes.
dyn:dyn:cheap-codingA dynamic profile that picks a model per call; the choice is reported on X-Ringside-Dynamic-Profile.
Model reference
A prefixed string identifying which model to run. The concrete fc: target a match:/slot:/dyn: ref resolved to is returned on the X-Ringside-Model-Resolved response header. See Model references and resolution.
Region suffix
A @<region> suffix on fc: refs only, pinning where inference runs for data residency: @eu / @us (OpenAI in-region), @eu-bedrock / @us-bedrock (Claude via AWS Bedrock) and @eu-vertex / @us-vertex (Claude via Google Vertex). An unknown suffix returns 400. See Regions and data residency.
Fallback cascade
Pass model as an array (e.g. ["fc:openai/gpt-4o-mini", "fc:openai/gpt-4o"]) and Ringside tries each in turn, escalating on a 5xx or schema-validation failure. The winner comes back on X-Ringside-Model-Used. A cascade cannot combine with stream:true (400 fallback_with_stream_unsupported) or an idempotency key. See Add model fallback.

Retrieval: vector stores and RAG

A vector store is a managed RAG index. You create it, attach files, wait for ingest, then query it through the OpenAI file_search tool inside an assistants run. Embedding happens for you. The store is per-customer isolated, optionally encrypted at rest and optionally backed by a knowledge graph. You do not call a query endpoint directly; the store is attached to a run and the model retrieves from it.

Vector store
A managed embedding index (id prefix vs_) created with an embedding_model fixed at create time (change it via .../migrate). Files ingest asynchronously through pending | in_progress | completed | failed | cancelled. Query via tool_resources.file_search.vector_store_ids inside a run. See Retrieval-augmented generation and the Vector Stores reference.
GraphRAG
Set graphrag_enabled: true and ingest also builds a knowledge graph; file_search then returns a facts[] array of {subject, predicate, object} relationships next to the chunk citations, catching answers spread across documents. Bills as a per-GB-day line, first 1 GB-day per store per day free. See GraphRAG.
Sealed store
A store with encryption set to managed or byok, keeping chunk text and (with vector_sealing: full, the default when sealed) vectors encrypted at rest. A byok store needs a passphrase key (>=12 chars) that is never persisted; you take a lease via .../unlock or the FC-Vector-Store-Key header, and the store idle-locks otherwise. See Sealed RAG and encryption.

Stateful execution: assistants, threads and runs

The assistants surface is the OpenAI-compatible way to run a stateful agent. The three primitives split cleanly. An assistant is reusable config and holds no conversation state. A thread holds the message history. A run executes the assistant against a thread. When the assistant calls one of your function tools the run pauses and waits for you to feed the result back.

Assistant
Reusable config (id prefix asst_): model, instructions, tools (file_search or function), tool_resources, sampling and metadata. Its name is unique per developer (409 assistant_name_in_use). It carries no conversation state. See Assistants, threads and runs and the Assistants reference.
Thread
A message container (id prefix thread_), capped at 5,000 messages. Set metadata.customer_id or metadata.customer_external_id to attribute the thread's spend to a customer. See the Threads reference.
Run
Executes an assistant on a thread (id prefix run_), moving through queued | in_progress | requires_action | cancelling | cancelled | completed | failed | expired. When the assistant calls a function tool the run pauses at requires_action; you answer with POST .../submit_tool_outputs. Only one active run per thread (else 409 thread_locked). See the Runs reference.

Events and bulk work

Webhook
An HTTPS endpoint (id prefix wh_) Ringside POSTs signed events to: customer lifecycle, budget and rate-limit blocks, wallet.low/wallet.empty, moderation flags, batch lifecycle and more. The signing secret (whsec_) is returned once on create and on rotate. Each delivery carries X-FC-Signature: t=<unix>,v1=<hex hmac_sha256>; verify constant-time. A sustained failure streak auto-disables the endpoint. See Webhooks and events and the Webhooks reference.
Batch
An async bulk job (id prefix batch_) at half the synchronous per-call price. Submit a file uploaded with purpose=batch against one endpoint (/v1/chat/completions, /v1/embeddings or /v1/moderations), poll its status, then download output_file_id (and error_file_id for failed lines). Max 50,000 requests per batch; only a 24h completion window. See Batch processing and the Batches reference.
File
A stored object (id prefix file_) used as batch input (purpose=batch), a RAG attachment (purpose=attachments) or a vision input (purpose=vision). Upload is multipart, max 512 MB per file, Bearer key only (a client token gets 403). See the Files reference.

Cross-cutting mechanics

A few rules apply everywhere, not to one resource. Knowing them up front saves you debugging the same surprise on three different endpoints.

Pagination
List endpoints return { "data": [...], "has_more": boolean, "next_cursor": string | null }. There is no "object": "list" field. Page forward by passing the previous next_cursor as after; limit defaults to 20 (max 100 on most lists; vector-store queries default 50, max 200). See Limits and quotas.
Rate limits
Four enforcement layers, lowest wins: edge per-IP, per-client-token, per-developer and per-customer. The response carries X-RateLimit-Remaining; a 429 adds Retry-After in integer seconds. There is no X-RateLimit-Limit or X-RateLimit-Reset. See Rate limiting and quotas.
Tenant isolation
Every resource is private to your developer account, and accessing one that belongs to another tenant returns 404 (not_found_error), never 403. Ids are therefore not enumerable. See Security.
Id prefixes you will see across the API. Branch on the prefix when you log or route ids.
PrefixResource
cus_Customer
req_Request id
ctok_Client token (jti)
asst_Assistant
thread_Thread
run_Run
vs_Vector store
file_File
batch_Batch
conv_Conversation
wh_Webhook
chatcmpl-Chat completion
whsec_Webhook signing secret

A worked example

Here is most of the map in one call: a chat completion attributed to customer cus_42, with a fallback cascade, reading the metering and resolution headers back off the response. The request is plain OpenAI wire shape plus Ringside headers.

curl -i https://api.fightclub.pro/v1/chat/completions \
  -H "Authorization: Bearer ko_<64-hex>" \
  -H "FC-Customer: cus_42" \
  -H "Content-Type: application/json" \
  -d '{
    "model": ["fc:openai/gpt-4o-mini", "fc:openai/gpt-4o"],
    "messages": [{"role":"user","content":"Summarize our refund policy."}]
  }'

# Response headers worth reading:
#   X-Request-Id: req_8f2c...
#   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: true
#   X-Ringside-Wallet-Deducted: 0.000412
#   X-RateLimit-Remaining: 59
Failure modes on this call

If cus_42 is over budget you get 402 customer_budget_exceeded even with a funded developer wallet. If your developer wallet is empty it is 402 wallet_empty. Add stream: true to the cascade above and it is rejected 400 fallback_with_stream_unsupported. Add an idempotency-key to the cascade and it is rejected 400 fallback_with_idempotency_unsupported. Branch on error.code in every case.