Ringside · Migration guide

Migrating from OpenAI Assistants API to Ringside

The OpenAI Assistants API shuts down on 2026-08-26. Ringside is a wire-compatible drop-in replacement that adds per-end-customer billing, budget caps, webhooks, and 19 LLM providers behind one SDK. Migration is two lines of code.

Status: Draft v0.1, written against the v1 API design spec. Publishes when Ringside v1 ships.

See also: Migration library index with five more step-by-step guides for migrating off OpenAI Chat, Anthropic, OpenRouter, and Chatbase.


Change two lines.

Python

python
# Before from openai import OpenAI client = OpenAI(api_key="sk-...") # After from openai import OpenAI client = OpenAI( api_key="ko_...", # <- your Knockout token base_url="https://api.fightclub.pro/v1", # <- Knockout endpoint ) # Everything else stays the same assistant = client.beta.assistants.create(name="Support Bot", model="fc:openai/gpt-4o") thread = client.beta.threads.create() client.beta.threads.messages.create(thread_id=thread.id, role="user", content="Hello") run = client.beta.threads.runs.create_and_poll(thread_id=thread.id, assistant_id=assistant.id)

Node.js / TypeScript

javascript
// Before const openai = new OpenAI({ apiKey: "sk-..." }); // After const openai = new OpenAI({ apiKey: "ko_...", baseURL: "https://api.fightclub.pro/v1", }); // Everything else stays the same const assistant = await openai.beta.assistants.create({ name: "Support Bot", model: "fc:openai/gpt-4o" }); const thread = await openai.beta.threads.create(); await openai.beta.threads.messages.create(thread.id, { role: "user", content: "Hello" }); const run = await openai.beta.threads.runs.createAndPoll(thread.id, { assistant_id: assistant.id });

That's the migration. Every call through client.beta.threads.* continues to work. Every call through client.chat.completions.* continues to work. Ringside resolves each request against your configured model, routes to the underlying provider, bills your Ringside wallet, and logs an attributable UsageEvent.

What changes at the wire (and what doesn't): your SDK still creates thread_<hex> IDs; internally Ringside maps each one 1:1 to a conv_<hex> conversation ID via api_thread_aliases. You never see the internal ID. The SDK receives the OpenAI-shaped thread_<hex> it expects. If you stored OpenAI thread IDs in your own database, pass them in metadata.source_thread_id on threads.create for a migration audit trail.


Why you need to migrate

OpenAI announced the Assistants API deprecation with a firm cutover date: August 26, 2026. As of that date, api.openai.com/v1/assistants, /v1/threads, /v1/threads/{id}/runs, and related endpoints return 410 Gone. Your existing Assistants, Threads, and Runs are fully deleted.

OpenAI's official successor is the Responses API paired with the new Conversations API. That's a legitimate path, but it is not wire-compatible with Assistants. Migration means either:

  • (a) Rewrite your app against the Responses API shape (different primitives, different state machine, different streaming format), or
  • (b) Migrate to a drop-in wire-compatible alternative.

Ringside is option (b). Your existing code keeps working. You get to use the next four months for product work instead of plumbing.


Feature-by-feature support

Fully supported in Ringside v1

Assistants featureRingside v1
client.beta.assistants.create / retrieve / update / delete / list✅ Full
client.beta.threads.create / retrieve / update / delete✅ Full
client.beta.threads.messages.create / list✅ Full
client.beta.threads.runs.create / retrieve / cancel / list✅ Full
client.beta.threads.runs.create_and_poll✅ Full
client.beta.threads.create_and_run✅ Full
Streaming runs (stream=True, OpenAI SDK async iteration)✅ Full
Function-calling tools✅ Full passthrough
tool_choice: "auto" / "none" / "required"✅ Full
tool_choice: {type: "function", function: {name: ...}}✅ Full
response_format: {type: "json_object"}✅ Full
response_format: {type: "json_schema", json_schema: {...}}Enforced on every provider (see note below)
temperature, top_p, max_tokens, seed parameters✅ Full
metadata on assistants, threads, messages, runs✅ Full
additional_messages on run create✅ Full
additional_instructions on run create✅ Full

Not supported in v1 (target v2, Q4 2026)

Assistants featureRingside v2 (planned)Workaround today
file_search toolQ4 2026Use function-calling tools pointed at your own RAG backend (Supabase, Pinecone, etc.). Ringside /embeddings endpoint helps.
code_interpreter toolQ4 2026Use function-calling tools pointed at your own sandbox (e.g., E2B, Replit API).
Attachments on messages (attachments: [{file_id: ...}])Q4 2026Reference files via URLs in the message content; Ringside v1 passes image URLs through to vision-capable models.
Vector StoresQ4 2026Use your own vector DB with Ringside's /embeddings endpoint.
Parallel tool calls in a single RunQ4 2026Sequential tool calling works; most apps don't need parallel.

If your app uses file_search or code_interpreter: three options:

  1. Wait for v2 (Q4 2026, RAG primitives, file storage, vector store).
  2. Replace with function-calling tools that call your own RAG or sandbox backend. Ringside's /embeddings endpoint gives you the vector generation; you wire the storage.
  3. Stay on OpenAI for file-search flows as a stop-gap and use Ringside for everything else. OpenAI's Responses API continues to support file_search past the Assistants deprecation; you can run both side by side temporarily.

Most apps won't hit these. If yours does, book a migration call with us and we'll help you plan the bridge.


What you get beyond Assistants parity

Ringside's reason to exist isn't to be a drop-in replacement. It adds the primitives OpenAI doesn't:

1. Per-end-customer billing attribution

OpenAI Assistants bills your project. Ringside lets you tag every call with your end-customer:

python
# Same Assistants SDK call, with a customer attached run = client.beta.threads.runs.create( thread_id=thread_id, assistant_id=assistant_id, extra_body={"user": "customer_42"}, # <- attributed to your customer )

Now query spend per customer:

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

Returns per-day, per-model spend for that end-customer. Use it to bill your customer however you like: subscription, usage-based, flat fee. No database on your side.

2. Budget caps that actually enforce

bash
curl -X PATCH -H "Authorization: Bearer ko_..." \ -H "Content-Type: application/json" \ -d '{"monthly_budget_usd": 50}' \ "https://api.fightclub.pro/v1/customers/customer_42"

Next run that would push customer_42 over $50 for the month returns 402 customer_budget_exceeded. Your dashboard reacts; the customer's usage caps. No more runaway-loop horror stories.

3. Webhooks on operational events

OpenAI Assistants forces you to poll for run completion. Ringside pushes events:

bash
curl -X POST -H "Authorization: Bearer ko_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://mycompany.com/webhooks/ringside", "events": ["run.completed", "run.failed", "customer.budget_exceeded", "wallet.low"] }' \ "https://api.fightclub.pro/v1/webhooks"

Every event is HMAC-signed (X-FC-Signature header). Retries exponentially for up to 24 hours. Your app reacts instantly to run completions, budget hits, and wallet signals with no polling loops.

4. 19 providers behind one SDK

Ringside routes to 19 LLM providers (OpenAI, Anthropic, Google, Mistral, Cohere, Groq, Together, Fireworks, Perplexity, DeepSeek, xAI, Cerebras, SambaNova, OpenRouter, Ollama, Azure OpenAI, Bedrock, HuggingFace, Replicate). Same OpenAI SDK call, different model:

python
# Claude 3.5 Sonnet through the OpenAI SDK run = client.beta.threads.runs.create( thread_id=thread_id, assistant_id=assistant_id, model="fc:anthropic/claude-3.5-sonnet", ) # Groq Llama 3.1 405B through the OpenAI SDK run = client.beta.threads.runs.create( thread_id=thread_id, assistant_id=assistant_id, model="fc:groq/llama-3.1-405b-reasoning", )

Or use a slot, a named alias you can re-point without redeploying:

python
run = client.beta.threads.runs.create( thread_id=thread_id, assistant_id=assistant_id, model="slot:fast", # we resolve "fast" to whichever model you configured )

5. Active-run locking (prevents thread-corruption races)

OpenAI Assistants locks threads during active runs, a subtle but important behavior. Ringside replicates this invariant: while a run is in-flight on a thread, a second concurrent run-create returns 409 conflict_error with code=conversation_locked. The lock auto-releases on completion, client disconnect, or 10-minute timeout. Messages stay in the order you sent them, not a race-condition-scrambled order.

6. Canonical token accounting across providers

Every provider reports tokens differently (OpenAI caches them, Anthropic has thinking tokens, Gemini has context-cached tokens). Ringside normalizes: your /usage reports have consistent input_tokens, output_tokens, cached_input_tokens, and reasoning_tokens fields regardless of underlying provider.

7. Run expiry + 5,000-message thread cap

Two new guardrails absent from OpenAI Assistants:

  • 10-minute default run expiry. A run that stays in_progress or requires_action beyond expires_at (default 10 min, configurable per run) is flipped to expired by the background sweeper, which also emits a run.failed webhook with reason='expired'. Prevents orphaned runs from blocking the thread forever when your tool-output handler crashes.
  • 5,000-message hard cap per thread. Ringside enforces a 5,000-row ordinal ceiling per conversation. Past that, POST /threads/:id/messages returns 409 conversation_full; start a new thread. Protects billing and latency. Most production chat threads should rotate long before this anyway.

Pricing comparison

Typical chatbot workload: 50 end-customers, avg 500 messages/day each ≈ 750,000 messages/month. Using GPT-4o-mini ($0.15 input / $0.60 output per 1M tokens) at ~1,000 input + 500 output tokens per message.

Line itemOpenAI directRingside (Pro tier)
LLM tokens~$338/mo~$338/mo
Ringside platform fee$0$99/mo (Pro base)
Ringside markup on tokens (6%)$0~$20/mo
Per-customer budget capsbuild yourself✅ Included
Conversation storagebuild yourself (Postgres + schema)✅ Included
Webhooks on eventsbuild yourself✅ Included
Per-customer usage reportsbuild yourself✅ Included
Active-run lockingbuild yourself✅ Included
19 provider optionsone19
Total$338 + the cost of building the rest~$457/mo

If the "build yourself" pieces take a full-time engineer for a month (and they will), Ringside pays for itself in the first billing cycle and every month after that is pure margin improvement.

Developer tier: $0/month + 10% markup. Good for prototypes. Every signup gets $10 one-time credit to burn through. No card required.


Cross-provider behavioral contract

When you route to a non-OpenAI provider via fc:*, Ringside guarantees the semantics, not just the wire format:

  • response_format: json_schema: Ringside validates the response against your schema server-side if the provider doesn't enforce natively. Up to 2 auto-retries with a reinforcement prompt on mismatch (configurable). Persistent failure returns 422 response_schema_validation_failed with the offending content.
  • Tool calling: tool_choice: required, forced-function, parallel declarations. All semantics preserved per OpenAI's docs across every provider. Compatibility matrix published at ringside.fightclub.pro/docs/compatibility.
  • Streaming: all 19 adapters emit OpenAI's exact streaming chunk shape. Your existing streaming code doesn't change.
  • Prompt caching: Anthropic cache_control blocks pass through verbatim. OpenAI automatic prompt caching works as-is. Cost calculator respects cache-hit pricing on providers that report it.
  • Reasoning / thinking modes: provider-specific knobs (reasoning_effort on o-series, thinking on Claude, thinking_budget on Gemini 2.5) expose canonical fields. Filter /models?supports=reasoning to find which ones.

If a provider silently breaks a guarantee, it's our bug. File an issue at github.com/fightclub/ringside-issues.


Migrating your data

If you have existing Assistants, Threads, and Messages on OpenAI you want to preserve:

1. Assistants

python
import os from openai import OpenAI old = OpenAI(api_key=os.environ["OPENAI_KEY"]) new = OpenAI(api_key=os.environ["RINGSIDE_KEY"], base_url="https://api.fightclub.pro/v1") for asst in old.beta.assistants.list(): new.beta.assistants.create( name=asst.name, instructions=asst.instructions, model=f"fc:openai/{asst.model}", # or remap to any other Ringside provider tools=[t for t in asst.tools if t.type == "function"], # skip file_search, code_interpreter metadata=asst.metadata, )

2. Threads + Messages

Iterate your existing threads, create matching threads on Ringside, replay messages:

python
for thread in your_saved_thread_list: # you may need a DB of thread IDs — OpenAI doesn't list threads new_thread = new.beta.threads.create() messages = old.beta.threads.messages.list(thread_id=thread.id, order="asc") for msg in messages.data: new.beta.threads.messages.create( thread_id=new_thread.id, role=msg.role, content=msg.content[0].text.value, # text-only in v1 )

3. Tag each thread with an end-customer ID

While migrating, capture the end-customer → thread mapping you know about. Pass extra_body={"user": "customer_42"} on future run creates so Ringside attributes correctly. Unknown users auto-create as stub Customers, low-friction, no pre-registration required.

4. End-to-end migration script

A battle-tested migration script template is maintained at ringside.fightclub.pro/migrate/script. Fork, adapt to your schema, run.


Common gotchas

  • Thread IDs change. OpenAI thread_abc ≠ Ringside thread_xyz. If you store thread IDs in your own DB (most apps do), you need a migration table: (old_openai_thread_id, new_ringside_thread_id). Alternatively, pass the old ID in metadata.source_thread_id and query by that.
  • No listing of your existing threads via OpenAI's API. OpenAI never exposed a "list threads" endpoint at the project level. If you didn't track thread IDs yourself, you can't bulk-enumerate them after the Aug 26 shutdown. Act now.
  • Streaming event names. Ringside emits OpenAI's exact thread.message.delta, thread.run.completed, thread.run.failed event taxonomy. If your streaming code works against OpenAI, it works against us.
  • Model naming. Ringside prefixes model IDs with fc:<provider>/<model> or slot:<alias>. A bare gpt-4o returns 400 invalid_request_error; use fc:openai/gpt-4o.
  • Rate limits. Ringside has per-dev, per-customer, and per-client-token rate limits. If you're migrating a high-volume app, email support@fightclub.pro to lift limits for the migration window.
  • Idempotency during migration. Use Idempotency-Key header on run creates during the backfill script. Prevents double-charges if your migration script crashes and you re-run.
  • Run expiry default. Runs auto-expire at 10 minutes. Long-running tool loops should submit tool outputs promptly; stalled runs get swept to expired + emit run.failed.
  • Tool type restrictions. Only function tools are supported in v1. Passing code_interpreter or file_search to assistants.create returns 400 unsupported_tool_type (see the workarounds table above).

Getting help

  • Migration Q&A: Discord, #migration channel. Founders answer personally for the first 6 months.
  • White-glove migration (first 10 customers, free): book a 1-hour call. We migrate your app on Zoom. Produces a working migrated app, a case study, and your permanent founding-customer pricing.
  • General support: support@fightclub.pro, 4-business-hour response.
  • Bugs: Open an issue at github.com/fightclub/ringside-issues (public issue-tracker repo; source is closed).

Next steps

  1. Sign up for Ringside → ringside.fightclub.pro/signup. $10 one-time credit, no card required.
  2. Test the 2-line migration in the no-signup playground with one of your existing prompts. Should work unchanged.
  3. Migrate one assistant + one thread first. Verify the full lifecycle (create → message → run → response) against what OpenAI returned.
  4. Backfill-migrate the rest over a weekend. Use the migration-script template.
  5. Delete your OpenAI Assistants before the deadline: August 26, 2026.

Assistants shutdown countdown

You have [COUNTDOWN_DAYS] days left.

Embed this countdown on your internal Slack: ringside.fightclub.pro/assistants


Related migrations


Questions, corrections, feature requests on this guide? Open an issue or ping the founder directly: chka@stratus5.com.