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 feature | Ringside 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 feature | Ringside v2 (planned) | Workaround today |
|---|---|---|
file_search tool | Q4 2026 | Use function-calling tools pointed at your own RAG backend (Supabase, Pinecone, etc.). Ringside /embeddings endpoint helps. |
code_interpreter tool | Q4 2026 | Use function-calling tools pointed at your own sandbox (e.g., E2B, Replit API). |
Attachments on messages (attachments: [{file_id: ...}]) | Q4 2026 | Reference files via URLs in the message content; Ringside v1 passes image URLs through to vision-capable models. |
| Vector Stores | Q4 2026 | Use your own vector DB with Ringside's /embeddings endpoint. |
| Parallel tool calls in a single Run | Q4 2026 | Sequential tool calling works; most apps don't need parallel. |
If your app uses file_search or code_interpreter: three options:
- Wait for v2 (Q4 2026, RAG primitives, file storage, vector store).
- Replace with function-calling tools that call your own RAG or sandbox backend. Ringside's
/embeddingsendpoint gives you the vector generation; you wire the storage. - 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:
bashcurl -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
bashcurl -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:
bashcurl -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:
pythonrun = 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_progressorrequires_actionbeyondexpires_at(default 10 min, configurable per run) is flipped toexpiredby the background sweeper, which also emits arun.failedwebhook withreason='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/messagesreturns409 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 item | OpenAI direct | Ringside (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 caps | build yourself | ✅ Included |
| Conversation storage | build yourself (Postgres + schema) | ✅ Included |
| Webhooks on events | build yourself | ✅ Included |
| Per-customer usage reports | build yourself | ✅ Included |
| Active-run locking | build yourself | ✅ Included |
| 19 provider options | one | 19 |
| 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 returns422 response_schema_validation_failedwith 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 atringside.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_controlblocks 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_efforton o-series,thinkingon Claude,thinking_budgeton Gemini 2.5) expose canonical fields. Filter/models?supports=reasoningto 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
pythonimport 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:
pythonfor 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≠ Ringsidethread_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 inmetadata.source_thread_idand 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.failedevent taxonomy. If your streaming code works against OpenAI, it works against us. - Model naming. Ringside prefixes model IDs with
fc:<provider>/<model>orslot:<alias>. A baregpt-4oreturns400 invalid_request_error; usefc: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.proto lift limits for the migration window. - Idempotency during migration. Use
Idempotency-Keyheader 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+ emitrun.failed. - Tool type restrictions. Only
functiontools are supported in v1. Passingcode_interpreterorfile_searchtoassistants.createreturns400 unsupported_tool_type(see the workarounds table above).
Getting help
- Migration Q&A: Discord,
#migrationchannel. 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
- Sign up for Ringside →
ringside.fightclub.pro/signup. $10 one-time credit, no card required. - Test the 2-line migration in the no-signup playground with one of your existing prompts. Should work unchanged.
- Migrate one assistant + one thread first. Verify the full lifecycle (create → message → run → response) against what OpenAI returned.
- Backfill-migrate the rest over a weekend. Use the migration-script template.
- 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
- OpenAI Chat Completions → Ringside
- Anthropic Messages → Ringside
- OpenRouter → Ringside
- Chatbase → Ringside
- Index
Questions, corrections, feature requests on this guide? Open an issue or ping the founder directly: chka@stratus5.com.