Embedded chatbot in the browser
You want to drop a chat widget onto your marketing site, docs, or in-app help flow. Shipping your OpenAI key to the frontend is a mistake. Routing every chat call through your own server means running a streaming proxy, handling SSE backpressure and eating the latency hit for a feature that should be boring enough that you forget it's there.
Why Ringside
- • Client Tokens. Short-lived (15 min default, 1 h max) signed JWTs minted from your server, usable from the browser without your Bearer key ever touching the frontend.
- • Customer-pinned and Origin-bound. Every Client Token is scoped to one Customer and one Origin; a token stolen from device A is useless on anyone else's page.
- • Allowlisted endpoints. Client Tokens can only hit chat/embeddings/moderations/threads/messages; the minting, listing and revoke endpoints stay Bearer-only.
- • Prepaid wallet. Give each chatbot user a credit balance via
POST /v1/customers/:id/wallet/topup. Ringside debits every call automatically and returns402 customer_wallet_emptywhen funds run out, no custom spend-tracking code required.
Architecture
In code
# Your server: mint a short-lived Client Token on page load
@app.post("/api/chat-token")
def mint_chat_token(request):
user = request.user
token = client.customers.client_tokens.create(
customer_id=user.ringside_customer_id,
scope=["chat"],
ttl_seconds=900, # 15 min
origin_allowlist=["https://app.example.com"],
)
return {"token": token.jwt, "expires_at": token.expires_at}
# Browser: call Ringside directly with the Client Token
# (This is JS, shown here for illustration.)
# const r = await fetch("https://api.fightclub.pro/v1/chat/completions", {
# method: "POST",
# headers: {
# "Authorization": `Client ${jwt}`,
# "Content-Type": "application/json",
# },
# body: JSON.stringify({ model: "fc:openai/gpt-4o", messages, stream: true }),
# });Cross-links
Used by
[TODO: real customers]