Ringside · Migration guide

Persona · Solo-Dev Sam

Updated 2026-04-20

Migrating from OpenRouter to Ringside

OpenRouter sells tokens. Ringside sells Customers. Same base_url shape, same 100+ models behind one key. The difference: per-end-customer billing attribution, budget caps, webhooks, Client Tokens, margin reporting, and a proper conversation persistence layer. None of which OpenRouter ships. Switch in 60 seconds.

Status: v1 (2026-04-20).

See also: Migration library index.


TL;DR: one-character diff (plus a prefix)

Python

python
# Before (OpenRouter) from openai import OpenAI client = OpenAI( api_key="sk-or-v1-...", base_url="https://openrouter.ai/api/v1", ) resp = client.chat.completions.create( model="anthropic/claude-3.5-sonnet", messages=[{"role": "user", "content": "Hello"}], ) # After (Ringside) client = OpenAI( api_key="ko_...", base_url="https://api.fightclub.pro/v1", ) resp = client.chat.completions.create( model="fc:anthropic/claude-sonnet-4-5", # add the fc: prefix messages=[{"role": "user", "content": "Hello"}], user="cus_42", # <- attribution you didn't have )

One URL swap, one fc: prefix, one user field. Done.

Node.js / TypeScript

javascript
// Before const openai = new OpenAI({ apiKey: process.env.OPENROUTER_API_KEY, baseURL: "https://openrouter.ai/api/v1", }); // 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:anthropic/claude-sonnet-4-5", messages: [{ role: "user", content: "Hello" }], user: "cus_42", });

Why migrate

OpenRouter is a great multi-provider aggregator. It's missing one category of feature, and it's the one that matters when you're building a real product:

  1. Per-end-customer billing attribution. OpenRouter shows you a single total. It has no concept of "which of my end-customers caused this spend." You have to build that yourself: every call, correlate with your own user ID, store usage events, aggregate. That's a database schema, a pipeline, and a dashboard you're maintaining. Ringside's user: "cus_42" does all of it, server-side, zero code from you.
  2. Budget caps. OpenRouter has credit caps at the account level. Ringside has budget caps per Customer: PATCH /v1/customers/cus_42 {monthly_budget_usd: 50}. Hit $50, Ringside returns 402 customer_budget_exceeded. Your runaway-loop horror story dies in its sleep.
  3. Webhooks. 13 event types (customer.budget_exceeded, wallet.low, run.failed, moderation.flagged, etc.). HMAC-signed, retried. OpenRouter has none.
  4. Client Tokens. Short-lived Ed25519-signed JWTs pinned to a Customer, optional Origin allowlist, optional IP hash. Let browsers call chat/completions directly without going through your backend. OpenRouter can't safely do this.
  5. Conversation persistence. Ringside stores threads + messages + runs server-side. You don't need a Postgres schema for chat history.
  6. Margin reporting. X-FC-Billable-Amount: 0.25 on the call → GET /v1/margin shows what you charged vs. what it cost. Run your pricing experiments with real data.

Step-by-step (60 seconds)

  1. Sign up at ringside.fightclub.pro/register?intent=ringside. $10 credit, no card.
  2. Create a server token at /ringside/app/api-keys/new.
  3. Update your SDK init: base_url = "https://api.fightclub.pro/v1", api_key = "ko_...".
  4. Rewrite your model string: anthropic/claude-3.5-sonnetfc:anthropic/claude-sonnet-4-5. (Ringside uses the provider's canonical current model name; our /v1/models endpoint lists all 250+.)
  5. Add user: "<your-end-customer-id>" to every call.

Drop-in code diffs

Streaming

python
stream = client.chat.completions.create( model="fc:openai/gpt-4o-mini", messages=[{"role": "user", "content": "Stream a poem"}], stream=True, user="cus_42", ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="", flush=True)

Ringside emits OpenAI chat-completion-chunk format byte-exact across all 19 providers (C.6 canonical SSE layer). Same shape OpenRouter emits today, no parser change.

Tool calling with multi-provider fallback

python
# Ringside slot alias — re-pointable without code changes resp = client.chat.completions.create( model="slot:chat-fast", 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", )

Slot aliases (slot:<name>) resolve via platformSettings.slotAliases. You can swap slot:chat-fast from fc:openai/gpt-4o-mini to fc:groq/llama-3.1-70b in the dashboard without redeploying.

Attribution via headers (recommended for multi-tag setups)

python
client.chat.completions.create( model="fc:openai/gpt-4o-mini", messages=[{"role": "user", "content": "Hello"}], user="cus_42", extra_headers={ "FC-Tag": "support-bot", "FC-Property-Plan": "enterprise", "X-FC-Billable-Amount": "0.50", # what you charged your customer for this call }, )

Then query margin:

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

Feature mapping

OpenRouterRingside equivalent
openrouter.ai/api/v1/chat/completionsapi.fightclub.pro/v1/chat/completions
model: "anthropic/claude-3.5-sonnet"model: "fc:anthropic/claude-sonnet-4-5" (add fc: prefix)
HTTP-Referer / X-Title headersOptional FC-Tag / FC-Property-* headers for richer metadata
Account-level credit capPer-Customer + global budget caps
Transforms (transforms: ["middle-out"])Not v1, most use cases solved by max_tokens + model choice
Auto-routing (model: "openrouter/auto")Not v1 (v2 target, slot: aliases give you manual failover today)
provider: {order: [...]} preferenceUse explicit fc:<provider>/<model> per-call
Usage dashboardGET /v1/usage, /margin, /customers/:id/usage + /ringside/app/usage UI
n/aCustomer Object + per-Customer budgets
n/aWebhooks (13 events)
n/aClient Tokens (frontend-safe JWTs)
n/aConversation persistence (/v1/customers/:id/conversations)
n/aAssistants API wire-compat (for migrating from OpenAI Assistants)
n/aMargin reporting (your charge vs. Ringside cost)

Gotchas

  • Pricing shape differs. OpenRouter's markup is roughly ~5% flat. Ringside's is 6% (Pro) or 10% (Developer), plus the Pro tier's $99/mo base. Compare your monthly volume + how much you value the features OpenRouter doesn't ship. Rough break-even: if you'd spend an engineer-week building Customer attribution, Ringside Pro is cheaper in year one.
  • Model naming. Ringside uses fc:<provider>/<current_name>. OpenRouter sometimes lags the provider's current naming (e.g. claude-3.5-sonnet vs. claude-sonnet-4-5). Check GET /v1/models for the canonical Ringside name.
  • No auto-routing in v1. OpenRouter's openrouter/auto routes to cheapest-matching. Ringside v2 will add this (§22 in the v1 API spec). For now, use slot:<alias> indirection. You pick the target; we make it re-pointable.
  • No provider-order preference in v1. OpenRouter lets you prefer Azure OpenAI over OpenAI direct, etc. Ringside v1 pins each fc:<provider>/<model> to a single upstream. v2 will add preference lists.
  • Transforms (middle-out, etc.) not supported. The common use case (long-context truncation) is handled by Ringside's conversation-context truncation on /v1/conversations/*/messages + explicit max_tokens.
  • HTTP-Referer and X-Title headers. OpenRouter uses these for its public leaderboard. Ringside ignores them; we don't have a public leaderboard (privacy-by-default). Use FC-Property-* if you want the metadata attached to UsageEvents.

FAQ

Q: Can I use all 250+ models OpenRouter has? A: Ringside covers 19 providers, roughly 100+ models today, with the rest coming in v1.1. Hit GET /v1/models for the current list. If a model you need isn't there, email support@fightclub.pro and we'll add it.

Q: What about prompt caching across providers? A: OpenAI automatic caching + Anthropic cache_control blocks both work. Ringside's C.6 canonical usage layer reports cached_input_tokens uniformly.

Q: Is Ringside faster than OpenRouter? A: Comparable. Both add roughly 10-30ms edge overhead on top of the upstream provider. Streaming first-byte is unaffected.

Q: Do I get a Customer-level dashboard? A: Yes. /ringside/app/customers/[id] shows per-Customer conversations, usage, budget status, webhooks, and Client Tokens in one view.

Q: Can I run OpenRouter and Ringside side-by-side? A: Yes. Route a percentage of traffic to each, compare. Since both speak OpenAI wire format, your client code branches on one base_url variable.


Next steps

Related migrations


Corrections, feedback: chka@stratus5.com.