Quick test
Create an API key at Organization Settings → API → Generate Key — it is shown once. Mint yourself a token with it:email is the only required field; name and ttl_seconds are in Step 1.
Take the token from the response and open:
How it works
Your user logs into your app → your frontend asks your backend for a token → your backend calls ClarityQ with your API key → your page loads the iframe, and hands it a fresh token whenever it asks for one.Identity
The email you send is the identity. Same address next visit, same conversation history. It becomes an ordinary ClarityQ user — so if that address already uses ClarityQ, it is that person, with the history and remembered preferences they already have.Step 1 — Mint tokens on your server
Using the same API key, expose one endpoint behind your own login:Step 2 — Render the iframe and answer one message
The embed manages its own lifetime. At about 80% of its token’s lifetime it postsclarityq:token-expiring to your page. You reply with a fresh token and it swaps it in
place: no reload, no timer, nothing lost. An answer that is streaming keeps streaming;
text your user has typed stays typed.
clarityq:token-expired, and the same listener answers it when your endpoint recovers.
The request that failed in between is lost; the conversation is not.
Step 3 — Add a history panel (optional)
The iframe is chat only — one conversation at a time, no list, no new-chat button — so it drops into your product without bringing a second navigation with it. Skip this step and every visit starts a fresh chat. Nothing is lost: ClarityQ stores each conversation either way, and they are all still there under that user’s identity. Your users just have no route back to them. If you want that route, the split is: ClarityQ stores the conversations, you render the list. Two things to wire. 1. List them — List conversations, authenticated with the token you already hold (Authorization: Bearer, not the API key). It returns only
that user’s conversations, scoped server-side. Sort on last_updated; description is the
title — a new conversation starts as New Chat and gets its real title a few seconds into
the first answer, announced by clarityq:title so you never re-fetch for it.
2. Open them, and keep them named — switching is a postMessage into the running iframe: internal navigation, so it is instant and needs no token. A brand-new chat announces its id the same way — catch it or that conversation is orphaned — and its title arrives moments later: