Ringside · Migration guide

Persona · Team Lead Tanya

Updated 2026-04-20

Migrating from OpenAI Chat Completions to Ringside

Change your base_url. Add a user field. Watch your attribution dashboard light up. Three lines of code get you per-end-customer billing, budget caps, webhooks, Client Tokens, margin reporting, and 19 LLM providers behind one SDK. Your existing OpenAI SDK stays as-is.

Status: v1 (2026-04-20).

See also: Migration library index.


TL;DR: the three-line diff

Python

python
# Before from openai import OpenAI client = OpenAI(api_key="sk-...") resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello"}], ) # After from openai import OpenAI client = OpenAI( api_key="ko_...", # <- your Ringside server token base_url="https://api.fightclub.pro/v1", # <- Ringside endpoint ) resp = client.chat.completions.create( model="fc:openai/gpt-4o-mini", # <- fc:<provider>/<model> ref messages=[{"role": "user", "content": "Hello"}], user="cus_42", # <- your end-customer ID (attribution!) )

Node.js / TypeScript

javascript
// Before import OpenAI from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); const resp = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Hello" }], }); // After const openai = new OpenAI({ apiKey: process.env.RINGSIDE_API_KEY, baseURL: "https://api.fightclub.pro/v1", }); const resp = await openai.chat.completions.create({ model: "fc:openai/gpt-4o-mini", messages: [{ role: "user", content: "Hello" }], user: "cus_42", });

That's the entire migration for the basic flow. Everything else (streaming, tool calls, JSON mode, vision, embeddings, moderations) keeps working unchanged.


Why migrate

If you're calling api.openai.com/v1/chat/completions directly today, four things are probably in your backlog:

  1. Per-end-customer attribution. Your bill from OpenAI is one number for your whole project. Ringside's user: "cus_42" body field auto-creates a tracked Customer server-side, so you can query GET /v1/customers/cus_42/usage any time and bill your customers however you want: subscription, usage-based, flat fee.
  2. Budget caps that actually stop the bleeding. One PATCH /v1/customers/cus_42 {monthly_budget_usd: 50} sets a hard ceiling. When the next call would exceed $50, Ringside returns 402 customer_budget_exceeded. No more 3am $10k runaway-loop alerts.
  3. Webhooks on operational events. Get pushed notifications for customer.budget_exceeded, wallet.low, run.failed, moderation.flagged, and more. No polling loops.
  4. 19 providers behind one URL. OpenAI, Anthropic, Google, Mistral, Groq, Together, Fireworks, Perplexity, DeepSeek, xAI, Cerebras, SambaNova, Cohere, OpenRouter, Ollama, Azure OpenAI, Bedrock, HuggingFace, Replicate. Change the model ref, not the SDK.

Step-by-step (5 minutes)

  1. Sign up at ringside.fightclub.pro/register?intent=ringside. Every signup gets $10 one-time credit, no card required.
  2. Create a server token at /ringside/app/api-keys/new. Copy the ko_... token (shown once; if you lose it, rotate).
  3. Point your SDK at https://api.fightclub.pro/v1 and swap the key.
  4. Pass user: "<your-end-customer-id>" on every call. It can be any stable opaque string (UUID, internal ID, tenant ID). Ringside auto-creates the Customer on first pass.
  5. Deploy. Hit /ringside/app to watch usage light up per Customer in real-time.

Drop-in code diffs

Streaming chat completions

python
# Works identically to OpenAI — just the base_url + model ref change stream = client.chat.completions.create( model="fc:openai/gpt-4o-mini", messages=[{"role": "user", "content": "Write me a haiku"}], stream=True, user="cus_42", ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="", flush=True)

Ringside emits byte-exact OpenAI chat-completion-chunk shape across all 19 providers (see the C.6 canonical streaming layer). Your existing SSE parser doesn't change when you switch providers.

Embeddings

python
emb = client.embeddings.create( model="fc:openai/text-embedding-3-small", input=["hello", "world"], user="cus_42", )

Moderations (free tier)

python
mod = client.moderations.create( model="fc:openai/omni-moderation-latest", input="Check this text", ) # Ringside routes moderations through its platform OpenAI key at-cost (no markup on Pro+).

Tool calling

python
resp = client.chat.completions.create( model="fc:openai/gpt-4o", messages=[{"role": "user", "content": "What's the weather in SF?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "parameters": { "type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"], }, }, }], user="cus_42", )

Tools pass through verbatim on OpenAI-compatible providers. On Anthropic/Google/Cohere/Bedrock, Ringside's C.6 canonical tool-call translator normalizes the wire format.

JSON schema (response_format)

python
resp = client.chat.completions.create( model="fc:anthropic/claude-haiku-4-5", # non-native json_schema provider messages=[{"role": "user", "content": "Generate a user record"}], response_format={ "type": "json_schema", "json_schema": { "name": "user", "schema": { "type": "object", "properties": {"name": {"type": "string"}, "age": {"type": "integer"}}, "required": ["name", "age"], }, }, }, user="cus_42", )

Ringside's enforcer (C.6) validates the response against your schema server-side. Up to 2 auto-retries with a reinforcement prompt on mismatch. Persistent failure returns 422 response_schema_validation_failed.


Using Customer attribution (the whole point)

Attribution via body field (default, OpenAI-compat)

python
client.chat.completions.create(model="...", messages=[...], user="cus_42")

Attribution via header (multi-tag)

python
client.chat.completions.create( model="fc:openai/gpt-4o-mini", messages=[...], user="cus_42", extra_headers={ "FC-Tag": "chat-support", "FC-Property-Plan": "enterprise", "FC-Session-Id": "workflow_abc", "X-FC-Billable-Amount": "0.25", # optional: what you're charging your customer for this call }, )

Then query per-Customer spend:

bash
curl -H "Authorization: Bearer ko_..." \ "https://api.fightclub.pro/v1/customers/cus_42/usage?from=2026-08-01"

Or margin (your charge vs. Ringside's cost):

bash
curl -H "Authorization: Bearer ko_..." \ "https://api.fightclub.pro/v1/customers/cus_42/margin?group_by=day"

Feature mapping

OpenAI Chat CompletionsRingside equivalent
api.openai.com/v1/chat/completionsapi.fightclub.pro/v1/chat/completions
api.openai.com/v1/embeddingsapi.fightclub.pro/v1/embeddings
api.openai.com/v1/moderationsapi.fightclub.pro/v1/moderations (free at-cost on Pro+)
model: "gpt-4o-mini"model: "fc:openai/gpt-4o-mini"
user: "<id>" (logging only)user: "<id>" auto-creates tracked Customer + attribution
n/aFC-Property-*, FC-Tag, FC-Session-Id, X-FC-Billable-Amount headers
n/aGET /v1/customers/:id/usage + /margin
n/aPOST /v1/webhooks (13 event types)
n/aPOST /v1/customers/:id/client_tokens (frontend-safe JWTs)
n/aBudget caps: PATCH /v1/customers/:id {monthly_budget_usd: N}
Streaming SSEIdentical chunk shape (byte-exact across providers)
Idempotency-Key headerSupported, 24-hour per-dev dedup window

Gotchas

  • user must be a stable opaque ID, not PII. Use an internal customer UUID or tenant ID. Don't pass emails. Ringside stores user as external_id on the Customer row; you do not want PII there. Use FC-Property-Email: <email> if you want email-level tags.
  • Model prefix is required. model: "gpt-4o" returns 400 invalid_request_error. Always use fc:openai/gpt-4o (or any other fc:<provider>/<model>, or a slot:<alias>). Bare model names are reserved for a possible OpenAI-only passthrough mode in v2.
  • Error envelope shape. Ringside wraps all errors as {error: {code, message, type, param}}, compatible with the OpenAI SDK's error parser. Upstream provider errors are normalized to Ringside codes (upstream_unavailable, upstream_quota_exceeded, upstream_auth_failed, upstream_invalid_request, upstream_context_length_exceeded, upstream_content_filter) so you can write one error handler.
  • Rate limits. Per-dev, per-Customer (RPM/TPM you configure), and per-Client-Token RPM. 429s include a Retry-After header.
  • Moderations pricing. On the Developer tier, moderations pass through at cost + 10%. On Pro+, moderations run through Ringside's platform OpenAI key at cost with no markup, effectively free.
  • Request IDs. Every response carries an X-Request-Id header. Log it. When you file a support ticket, include the request ID and we'll find the trace instantly.

FAQ

Q: What about latency? A: Ringside adds roughly 10–30ms of edge routing + attribution overhead on top of the upstream provider. Streaming first-byte latency is unaffected (we proxy the upstream SSE directly).

Q: GDPR / CCPA hard-delete? A: DELETE /v1/customers/:id?hard=true purges the Customer row + anonymizes all associated UsageEvents (user_external_id nulled, metadata scrubbed). Returns 204.

Q: Failover if OpenAI is down? A: Use slot:<alias> refs. One admin-side PATCH /admin/ringside/slots/chat-cheap can re-point slot:chat-cheap from fc:openai/gpt-4o-mini to fc:anthropic/claude-haiku-4-5 with zero app redeploys.

Q: SLA? A: Pro tier: 99.9% monthly uptime. Enterprise: 99.95% + runbook access. See /legal/sla.

Q: How do I migrate existing data? A: Chat Completions is stateless, so there is nothing to migrate. Your first call with user="cus_42" creates the Customer; subsequent calls attach to it. If you want to pre-create Customers (e.g. to set a budget), use POST /v1/customers.


Next steps

Related migrations


Corrections, feedback: chka@stratus5.com.