Ringside · Migration guide

Persona · Solo-Dev Sam → Team Lead Tanya

Updated 2026-07-18

Migrating from Chatbase to Ringside

Chatbase ships a finished chatbot. Ringside ships the pieces, retrieval included. When your no-code bot runs into the edge of the dashboard (provider choice, prompt control in your repo, your own UI, per-end-customer billing), Ringside is the next step. Managed RAG means you don't give up your training documents to get there.

Status: v1 (2026-04-20).

See also: Migration library index.


Should you move

Someone edited the system prompt in the Chatbase dashboard on Thursday afternoon. By Friday the bot was quoting a 14-day refund window instead of 30, and support was fielding the fallout. There's no diff to read, no commit to revert and nobody who reviewed the change, because the prompt lives in a textarea in a web app rather than in your repository next to everything else you ship. That's the week most teams start looking for an API.

The dashboard is the whole product, which is exactly why it's good for the first six months and awkward after that. A prompt you can't code-review is one symptom. The others show up on the invoice.

Chatbase bills you for total message volume across every one of your customers. One number. If the agency that resells your bot to 40 of their own clients wants a per-client breakdown, you cannot produce one, because Chatbase does not model your customers at all. Ringside does, and that single difference is what most of this guide is about.

Move when any of these is true.

  1. One bill covering all your customers, with no way to split it. Ringside gives every end-customer a Customer object. Pass user: "cus_42" on a call and GET /v1/customers/cus_42/usage tells you what that account cost you, with GET /v1/customers/cus_42/margin?group_by=day sitting next to it for what you made on them.
  2. No hard cap when something runs away overnight. PATCH /v1/customers/cus_42 {"monthly_budget_usd": 25} stops the next call that would cross $25 with 402 customer_budget_exceeded, before it reaches a provider. Per customer, not per account.
  3. No answer when a customer disputes usage. Every response carries X-Request-Id and every charge is an attributed event with a model, token counts and a timestamp.
  4. A prompt and a document set you cannot get under version control. Assistant instructions go in your repo and ship through your normal pipeline. Vector stores are created and populated by API calls you can put in a migration script.
  5. You're locked to one provider's models. 19 providers behind one SDK, swapped per request with a fc:<provider>/<model> ref or repointed across every call site at once with slot:<alias>.

If none of those bite yet, stay on Chatbase. It does the job it's built for and this guide will still be here.


The part people expect to lose, and don't

The old version of this guide told you to go and buy a vector database. That advice is dead. Ringside ships managed RAG, so the training documents that were the reason to stay on Chatbase come across as a first-class object.

You create a store with POST /v1/vector_stores, upload files to /v1/files with purpose="attachments", attach them, and the platform chunks, embeds and indexes them for you. Then you hand the store to an Assistant with the file_search tool and it retrieves at answer time, with file_citation annotations pointing back at the source file. Ingestion takes 25+ MIME types, PDFs included, so a folder of product manuals goes in the way you'd hope.

Two things sit on top of that, and they're the reasons to prefer it over rolling your own index. graphrag_enabled: true on a store builds a fact graph over your documents, so a question whose answer is split across three files gets joined instead of shrugged at. And a store created with encryption: "managed" seals chunk text and vectors at rest under a per-store key, which matters if the documents are contracts rather than FAQs.

One caveat on sealing, and it's a real one. Sealed stores accept text only today: text, Markdown, plain text, JSON, XML and YAML, up to 25 MB per file. Push a PDF or a DOCX at a sealed store and ingest fails. If your documents are PDFs and you want them sealed, convert to text or Markdown first. Plaintext stores take the full MIME range.

If you already run your own index, keep it. /v1/embeddings is there and the bring-your-own path is documented below. Managed RAG is the default because it's less code, not because the other option went away.


How risky is this

You can run both at once. Chatbase keeps serving your live widget while you stand up the Ringside path behind a flag and send it a fraction of traffic, which is the honest way to find out whether your retrieval quality holds up on real questions rather than on the six you thought to test.

Nothing about the move is one-way. Your training documents stay in whatever folder you exported them to. Your prompt is a text file. Ringside's chat endpoint is OpenAI-shaped, so if you decide six weeks in that you want a different platform underneath, you point base_url somewhere else and keep the same SDK call.

The genuinely new work is the UI, and that's the part to schedule properly. Budget a couple of days.


What you keep. What you lose. What you gain.

You keep

  • Your training documents. Export from Chatbase, upload to a Ringside vector store, attach, done. No third-party vector database in the path.
  • Your conversation history. /v1/customers/:id/conversations replaces Chatbase's thread storage 1:1.
  • Your chatbot persona / system prompt. Straight into a Ringside Assistant via POST /v1/assistants {instructions: "..."}, and this time it lives in git.

You lose

  • The widget. You build or buy a chat UI. shadcn/ui, Vercel's ai package or any of the OSS React chat components get you there in a day.
  • Chatbase's analytics dashboard. Replaced by /ringside/app/customers/[id]/usage plus the request log at /ringside/app/logs.
  • Built-in lead capture form. Re-implement in your UI, post to your own backend, then call POST /v1/customers.

You gain

  • 19 LLM providers, one SDK. Swap Claude, Llama 3, Gemini or Mistral on the same client code.
  • Per-end-customer billing and hard budget caps.
  • Managed RAG with file_search, GraphRAG and sealed stores.
  • Webhooks on 34 registered event types, HMAC-signed and retried.
  • Client Tokens. Short-lived JWTs your browser uses directly after your backend mints them, scoped to a single Customer, with an optional Origin allowlist.
  • Margin reporting, per Customer, per model, per day.
  • Assistants API wire-compat, if you want OpenAI-SDK Assistants behaviour.
  • GDPR hard-delete. DELETE /v1/customers/:id?hard=true.

Step-by-step migration

Step 1: Sign up and create a server token (5 minutes)

ringside.fightclub.pro/register?intent=ringside gets you $10 credit, no card. Create a server token at /ringside/app/api-keys/new. It's ko_ followed by 64 hex characters, shown once.

Step 2: Export your Chatbase chatbot config

From the Chatbase dashboard, export the chatbot's instructions and its training documents. We'll call them instructions.txt and docs/*.md. Commit both.

Step 3: Create a vector store and load your documents

python
from openai import OpenAI client = OpenAI(api_key="ko_...", base_url="https://api.fightclub.pro/v1") store = client.vector_stores.create( name="support-docs", metadata={"source": "chatbase_migration"}, ) for path in training_docs: f = client.files.create(file=open(path, "rb"), purpose="attachments") client.vector_stores.files.create(vector_store_id=store.id, file_id=f.id)

Attach returns pending. Ingest runs asynchronously, so poll each file until it reaches completed or failed, and read last_error on a failure. Only failed and cancelled files are retryable. If you'd rather not poll, subscribe to the vector_store.file.completed webhook.

Turning on the fact graph is one field, at create time or later via PATCH:

python
store = client.vector_stores.create(name="support-docs", graphrag_enabled=True)

For a store that has to hold sensitive text, add encryption: "managed" at create. Remember the text-only limit above before you point it at a folder of PDFs.

Step 4: Map each Chatbase "chatbot" to a Ringside Assistant

python
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": "file_search"}], tool_resources={"file_search": {"vector_store_ids": [store.id]}}, ) print(assistant.id) # asst_...

Retrieval now happens inside the run. Answers come back with file_citation annotations, so your UI can show which document a claim came from, which is something the Chatbase widget never gave you.

Already running your own index? Skip the vector store. POST /v1/embeddings gives you the vectors, you keep pgvector or whatever you have, and you expose retrieval to the model as an ordinary function tool:

python
emb = client.embeddings.create( model="fc:openai/text-embedding-3-small", input=[doc.read() for doc in training_docs], ) # Store emb.data[i].embedding alongside your chunks, then declare a # search_docs function tool that does top-k lookup and returns passages.

Step 5: Map each Chatbase end-user to a Ringside Customer

Ringside auto-creates Customers on first user=<id> pass. Pre-populate if you want budgets and metadata set from the start:

python
for 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 "rpm_limit": 60, "metadata": {"source": "chatbase_migration"}, }, )

Step 6: Build your own chat UI

Minimal React using Client Tokens for browser-safe calls plus SSE streaming:

jsx
// Backend mints a Client Token for this Customer (keep the server token server-side) // POST /v1/customers/cus_42/client_tokens → { token: "eyJ...", expires_at: ... } // Frontend (React), using 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 (the default TTL) and serves it to the authenticated user. That's about 20 lines of server code.

Step 7: Migrate conversations (optional)

To preserve chat history, export Chatbase conversations, create Ringside Conversations, replay the messages:

python
for 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 a message without triggering a new LLM run, which is what you want for replay. Not shipped yet, available on v1.1+.)


Feature mapping

ChatbaseRingside equivalent
"Chatbot" (template + training docs + model)POST /v1/assistants with the file_search tool + a vector store
Training documentsPOST /v1/vector_stores, POST /v1/files with purpose="attachments", then attach
Bring-your-own index (alternative)POST /v1/embeddings + your own store, exposed as a function tool
Multi-hop questions across documentsgraphrag_enabled: true on the store
Sensitive documentsencryption: "managed" on the store (text/Markdown/JSON/XML/YAML only, 25 MB cap)
End-users ("chat users")POST /v1/customers (or auto-create via user: "<id>")
Conversations (thread history)POST /v1/customers/:id/conversations + /messages
Chat widgetBuild your own (shadcn/ui, custom React, whatever)
Lead capture formsYour own form, then POST /v1/customers on submit
Usage dashboard/ringside/app/customers/[id] + /ringside/app/usage
Webhook on new messagePOST /v1/webhooks {events: ["run.completed", ...]}, 34 registered event types
Per-chatbot model selectionmodel: "fc:<provider>/<model>" or slot:<alias> on each call
Message rate limitsPer-Customer rpm_limit / tpm_limit via PATCH /v1/customers/:id
Bulk offline processingPOST /v1/batches (JSONL in, 24h completion window, batch-rate billing)
n/aPer-Customer monthly budget caps (monthly_budget_usd)
n/aMargin reporting (/v1/customers/:id/margin)
n/a19 provider choices
n/aClient Tokens (browser-safe Ed25519 JWTs)
n/aGDPR hard-delete

Gotchas

  • UI is all you. Chatbase's theming panel, widget embed and analytics overlay have no equivalent. Budget 1-3 days to build or buy a chat UI.
  • Your backend must mint Client Tokens. Never ship a ko_ server token to the browser. Your server mints short-lived Client Tokens via POST /v1/customers/:id/client_tokens, the browser uses those to call chat/completions and conversations/*/messages directly, and you refresh before expiry.
  • File upload purpose matters. purpose="assistants" returns 400. The allowed values are attachments, batch and vision. Vector store files want attachments.
  • Ingest is asynchronous. Attach returns pending and moves through in_progress to completed, failed or cancelled. A file sitting at pending is not an error yet. Read last_error on failed.
  • Sealed stores are text-only today. Text, Markdown, plain text, JSON, XML, YAML, 25 MB per file. A sealed PDF fails ingest. Plaintext stores take PDFs and 25+ MIME types.
  • Lazy Customer auto-creation is on by default. Passing user: "cus_xyz" on a first call creates the Customer with no budget and no metadata. Pre-create via POST /v1/customers if the budget matters.
  • Customer ids are cus_ plus hex. Not cust_.
  • Conversation lock. Only one LLM call runs against a conversation at a time. If your UI fires a second message while the first is still streaming you get 409 conversation_locked. Debounce, or open a second conversation.
  • 5,000-message cap per conversation. After 5k messages POST /messages returns 409 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 with more flexibility? A: Ringside's Assistants API (/v1/assistants, /v1/threads, /v1/threads/:id/runs) is the closest 1:1 to Chatbase's "chatbot" concept, a named agent with instructions that keeps thread state, and with file_search attached it also keeps the training documents. See openai-assistants-to-ringside.md for the full OpenAI-SDK pattern.

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 the LLM. The moderation.flagged webhook fires on flagged content.

Q: Do I need a separate billing system to charge my end-customers? A: Stripe plus Ringside is the standard combo. Ringside tells you what each Customer cost you and Stripe charges them. Sync GET /v1/customers/:id/usage nightly into 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, about $0.02 a message. Ringside is $99/mo base on Pro plus roughly $0.0003 a message on gpt-4o-mini plus 6% markup, about $0.00032 a message, with vector storage billed separately per GB-day. Above 10k messages a month Ringside is cheaper. Under 2k, Chatbase is simpler and cheaper.

Q: Support? A: Open a ticket and a human picks it up. For hands-on help moving a live bot over, book a migration call.


Next steps

Related migrations


Corrections or feedback: open an issue.