Migrating from Chatbase to Ringside
Chatbase ships UI + agent. Ringside ships primitives. When your no-code chatbot outgrows Chatbase's framework (provider choice, SDK-level prompt control, your own UI, per-end-customer billing, or integrations Chatbase doesn't support), Ringside is the next step. This guide walks you through migrating without losing what you already had working.
Status: v1 (2026-04-20).
See also: Migration library index.
Should you migrate?
Chatbase is excellent at what it does: a no-code SaaS that gives you a trainable chatbot, a chat widget, and a dashboard, in an afternoon. If that's enough, stay on Chatbase.
Migrate to Ringside when any of these apply:
- You need per-customer billing. Chatbase bills you for total message volume. You can't cleanly pass that cost through to your end-customers.
- You need provider choice. Chatbase is OpenAI-first; Ringside gives you 19 providers behind one SDK, and you can swap models per-request or via
slot:<alias>. - You need SDK-level control. Custom tools, structured output via JSON schemas, streaming to your own UI, retrieval pipelines you own. All of this is API-shaped, not widget-shaped.
- You need your own UI. Chatbase's widget is fine for simple FAQ bots; if you want a brand-native chat experience, you have to own the front-end anyway.
- You need webhooks on operational events.
customer.budget_exceeded,wallet.low,run.failed,moderation.flagged. Ringside ships 13 event types, HMAC-signed, retried.
If those boxes are unchecked, Chatbase is still the right call. Bookmark this guide for later.
What you keep. What you lose. What you gain.
You keep
- Your training documents. Export from Chatbase, re-index with any RAG stack (Supabase, Pinecone, etc.) + Ringside's
/v1/embeddings. - Your conversation history. Ringside's
/v1/customers/:id/conversationsreplaces Chatbase's thread storage 1:1. - Your chatbot persona / system prompt. Goes straight into a Ringside Assistant via
POST /v1/assistants {instructions: "..."}.
You lose
- The widget. You have to build or buy your own chat UI. Good news: shadcn/ui, Vercel's
aipackage, or 20+ OSS React components all do this in a day. - Chatbase's analytics dashboard. Replaced by Ringside's
/ringside/app/customers/[id]/usage+ the request log at/ringside/app/logs. - Built-in lead capture form. Trivial to re-implement in your UI: post to your own backend, then call
POST /v1/customersto create the Ringside Customer.
You gain
- 19 LLM providers, one SDK. Swap Claude, Llama 3, Gemini, Mistral on the same client code.
- Per-end-customer billing + budgets. Bill each of your customers independently; hard-cap their monthly spend.
- Webhooks on 13 operational events.
- Client Tokens. Short-lived JWTs your browser can use directly (after your backend mints them), scoped to a single Customer with optional Origin allowlist.
- Margin reporting. Track what you charge vs. what Ringside costs you, per Customer, per model, per day.
- Assistants API wire-compat. If you ever want OpenAI-SDK Assistants behaviour, that's built-in.
- GDPR hard-delete.
DELETE /v1/customers/:id?hard=true.
Step-by-step migration
Step 1: Sign up + create a server token (5 minutes)
ringside.fightclub.pro/register?intent=ringside → $10 credit, no card. Create a server token at /ringside/app/api-keys/new.
Step 2: Export your Chatbase chatbot config
In the Chatbase dashboard, export the chatbot's instructions + the training documents. We'll call these instructions.txt and docs/*.md.
Step 3: Map each Chatbase "chatbot" to a Ringside Assistant
pythonfrom openai import OpenAI client = OpenAI(api_key="ko_...", base_url="https://api.fightclub.pro/v1") assistant = client.beta.assistants.create( name="Support Bot", instructions=open("instructions.txt").read(), model="fc:openai/gpt-4o-mini", # or fc:anthropic/claude-haiku-4-5, etc. tools=[{ "type": "function", "function": { "name": "search_docs", "description": "Retrieve relevant training docs for a query", "parameters": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"], }, }, }], ) print(assistant.id) # asst_...
Step 4: Index your training documents
Chatbase's built-in RAG is replaced with your own. Simplest stack: Supabase + pgvector + Ringside's /v1/embeddings:
pythonemb = client.embeddings.create( model="fc:openai/text-embedding-3-small", input=[doc.read() for doc in training_docs], ) # Store emb.data[i].embedding alongside the doc chunks in Supabase / Pinecone / etc.
Your search_docs function-tool becomes: compute query embedding → top-k vector search → return matched passages.
Step 5: Map each Chatbase end-user to a Ringside Customer
Ringside auto-creates Customers on first user=<id> pass, but if you want to pre-populate budgets + metadata:
pythonfor cbase_user in chatbase_users_export: client.post( # POST /v1/customers (raw HTTP here; no SDK method yet) "/customers", json={ "external_id": cbase_user["id"], "name": cbase_user["name"], "monthly_budget_usd": 10, # hard cap per Customer "metadata": {"source": "chatbase_migration"}, }, )
Step 6: Build your own chat UI
Minimal React snippet using Client Tokens for browser-safe calls + SSE streaming:
jsx// Backend mints a Client Token for this Customer (keep server token server-side!) // POST /v1/customers/cus_42/client_tokens → { token: "eyJ...", expires_at: ... } // Frontend (React) — uses the Client Token directly: import { useState } from "react"; export function Chat({ clientToken, conversationId }) { const [messages, setMessages] = useState([]); async function send(text) { const resp = await fetch( `https://api.fightclub.pro/v1/customers/cus_42/conversations/${conversationId}/messages`, { method: "POST", headers: { Authorization: `Client ${clientToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ role: "user", content: text, stream: true, }), } ); const reader = resp.body.getReader(); const decoder = new TextDecoder(); let assistant = ""; while (true) { const { value, done } = await reader.read(); if (done) break; // Parse OpenAI-shape SSE chunks const chunk = decoder.decode(value); for (const line of chunk.split("\n")) { if (!line.startsWith("data: ")) continue; const payload = line.slice(6).trim(); if (payload === "[DONE]") return; const delta = JSON.parse(payload).choices?.[0]?.delta?.content ?? ""; assistant += delta; setMessages((m) => [...m.slice(0, -1), { role: "assistant", content: assistant }]); } } } return ( <div> {messages.map((m, i) => <p key={i}>{m.role}: {m.content}</p>)} <input onKeyDown={(e) => e.key === "Enter" && send(e.currentTarget.value)} /> </div> ); }
Your backend mints a new Client Token every 15 minutes (default TTL) and serves it to the authenticated user. That's ~20 lines of server code.
Step 7: Migrate conversations (optional)
If you want to preserve chat history: export Chatbase conversations → create Ringside Conversations → replay messages:
pythonfor cbase_conv in chatbase_conversations_export: conv = client.post( f"/customers/{cbase_conv['user_id']}/conversations", json={"metadata": {"source_chatbase_id": cbase_conv["id"]}}, ) for msg in cbase_conv["messages"]: client.post( f"/customers/{cbase_conv['user_id']}/conversations/{conv['id']}/messages", json={"role": msg["role"], "content": msg["content"], "skip_llm": True}, )
(skip_llm: true appends the message without triggering a new LLM run, useful for replay. Available on v1.1+.)
Feature mapping
| Chatbase | Ringside equivalent |
|---|---|
| "Chatbot" (template + training docs + model) | POST /v1/assistants + your own RAG stack (Supabase/Pinecone + /v1/embeddings) |
| Training documents | Embed via /v1/embeddings, store in your vector DB |
| End-users ("chat users") | POST /v1/customers (or auto-create via user: "<id>") |
| Conversations (thread history) | POST /v1/customers/:id/conversations + /messages |
| Chat widget | Build your own (shadcn/ui, custom React, whatever) |
| Lead capture forms | Your own form; call POST /v1/customers on submit |
| Usage dashboard | /ringside/app/customers/[id] + /ringside/app/usage |
| Webhook on new message | POST /v1/webhooks {events: ["run.completed", ...]} |
| Per-chatbot model selection | model: "fc:<provider>/<model>" or slot:<alias> on each call |
| Message rate limits | Per-Customer RPM/TPM (PATCH /v1/customers/:id {rpm: 60, tpm: 10000}) |
| n/a | Per-Customer monthly budget caps (monthly_budget_usd) |
| n/a | Margin reporting (/v1/customers/:id/margin) |
| n/a | 19 provider choices |
| n/a | Client Tokens (browser-safe Ed25519 JWTs) |
| n/a | GDPR hard-delete |
Gotchas
- UI is all you. Chatbase's theming panel, widget embed, analytics overlay: none of it exists in Ringside. Budget 1-3 days to build or buy a chat UI (shadcn/ui +
aipackage is fastest). - Your backend must mint Client Tokens. Never ship your
ko_...server token to the browser. Your server mints short-lived Client Tokens (POST /v1/customers/:id/client_tokens), browser uses them to callchat/completions+conversations/*/messagesdirectly. Auto-refresh before expiry. - No built-in RAG. You own the vector store. Supabase + pgvector is 50 lines of setup. Pinecone is one
pip install. Ringside's/v1/embeddingsgives you the vectors. - Lazy Customer auto-creation. Passing
user: "cus_xyz"on a first call creates the Customer with no budget, no metadata. If you care about budgets, pre-create viaPOST /v1/customers. - Conversation lock. Only one LLM call can run against a conversation at a time. If your UI fires a second message while the first is still streaming, you'll get
409 conversation_locked. Debounce, or create a second conversation. - 5,000-message cap per conversation. After 5k messages,
POST /messagesreturns409 conversation_full. Long-running FAQ bots should rotate conversations (one per session, or one per calendar week).
FAQ
Q: What if I want Chatbase-like simplicity but more flexibility? A: Ringside's Assistants API (/v1/assistants, /v1/threads, /v1/threads/:id/runs) is a closer 1:1 mental model to Chatbase's "chatbot" concept: a named agent with instructions that maintains thread state. See openai-assistants-to-ringside.md for the full OpenAI-SDK pattern (works the same way here).
Q: Does Ringside do content moderation like Chatbase? A: Yes. POST /v1/moderations (OpenAI-compat, free at-cost on Pro+). Call it on inbound user messages before passing to the LLM. moderation.flagged webhook fires on flagged content.
Q: Do I need a separate billing system to charge my end-customers? A: Stripe + Ringside is the standard combo. Ringside tells you what each Customer cost you; Stripe charges your Customer. Use GET /v1/customers/:id/usage nightly to sync usage → Stripe meters (or your own invoicing).
Q: How much does this cost vs. Chatbase? A: Chatbase's Pro plan is $40/mo for 2,000 messages on GPT-4o-mini = ~$0.02/message. Ringside is $99/mo base (Pro) + ~$0.0003/message on gpt-4o-mini + 6% markup = ~$0.00032/message. For 10k+ messages/mo, Ringside is cheaper. For under 2k, Chatbase is simpler + cheaper.
Q: Support? A: support@fightclub.pro (4-business-hour response) or Discord #migration. White-glove migration (first 10 customers, free): book a 1-hour call.
Next steps
- Pre-built chat UI examples:
ringside.fightclub.pro/docs/recipes/chat-ui. - RAG quickstart:
ringside.fightclub.pro/docs/recipes/rag. - Playground:
ringside.fightclub.pro/try. - Support:
support@fightclub.proor Discord#migration.
Related migrations
- OpenAI Chat Completions → Ringside
- OpenAI Assistants → Ringside
- Anthropic Messages → Ringside
- OpenRouter → Ringside
- Index
Corrections, feedback: chka@stratus5.com.