Skip to content

Resumable Sessions

By default every conversation is a clean slate. Close the tab and it’s gone — nothing about the chat is stored against the visitor.

Resumable sessions change that for a device you choose. The conversation is kept on Ragtime’s side and handed back when the visitor returns, so they continue mid-thread instead of starting over. It works for anonymous visitors — there is no Ragtime login involved.

Resumable sessions require Store conversations to be on for the project (Dashboard → Analytics → Conversation storage). Resuming means handing a stored conversation back to the visitor, so it can’t be switched on for a project that has opted out of keeping conversations — the device toggle stays unavailable and tells you why.

Turning conversation storage off later stops resume everywhere in that project immediately, including for conversations already saved.

Dashboard → Deployment → Devices → edit a device → Resumable sessions.

Setting What it does
Resumable sessions The master switch. On its own, this gives you same-browser resume (see below).
Keep sessions for (days of inactivity) How long an untouched conversation survives before it’s deleted. 1–365, default 30. The clock restarts on every message.
Continue across devices Option A — resume by your user ID. Requires request signing.
Let the host page manage the session Option B — your page holds the session token.

Level 1 — same browser (nothing to build)

Section titled “Level 1 — same browser (nothing to build)”

Flip Resumable sessions on and you’re done. Ragtime keeps a private token in the visitor’s browser storage and reuses it when they come back to the same device on the same browser.

This covers the common case — someone reads half an answer, closes the tab, and returns that evening on the same laptop.

What it can’t do: follow the visitor to their phone, survive a cleared browser, or work in a private window. Those need one of the two options below.

A conversation that reached a natural ending is not resumed — the visitor gets a fresh start next time, which is usually what you want.

Level 2 — Option A: continue across devices

Section titled “Level 2 — Option A: continue across devices”

Use this when your site already has logged-in users. You tell Ragtime who the visitor is, using your own user ID, and Ragtime hands back that person’s last conversation on whatever device they open next.

  • Request signing enabled: Dashboard → Deployment → Security → Require signature, which gives you a signing secret.
  • A server-side place to build the URL. This will not work from browser JavaScript, by design — see the warning below.

Add three query parameters to the iframe URL:

Parameter Value
ragtimeUserId Your own stable ID for the logged-in visitor.
timestamp Current time in milliseconds. Valid for 5 minutes.
signature HMAC-SHA256 over the sorted parameters (below).
import crypto from 'crypto';
function buildResumableChatUrl(baseUrl: string, secret: string, userId: string) {
const params = new URLSearchParams();
params.set('ragtimeUserId', userId);
params.set('timestamp', Date.now().toString());
// Sort, then sign the resulting query string. `timestamp` is just one of the
// sorted parameters — it is not prefixed separately.
params.sort();
const signature = crypto
.createHmac('sha256', secret)
.update(params.toString())
.digest('hex');
params.set('signature', signature);
return `${baseUrl}?${params.toString()}`;
}
// Render this server-side, per page load — the timestamp expires after 5 minutes.
const src = buildResumableChatUrl(
'https://<host>/chat/<orgSlug>/<projectSlug>/<deviceSlug>',
process.env.RAGTIME_SIGNING_SECRET!,
currentUser.id
);

This is the same signing scheme used elsewhere in these docs — if you already sign injection parameters, reuse that helper and add ragtimeUserId to it.

Not your user ID. Ragtime stores a one-way fingerprint of it, so the ID itself is never at rest on our side. You keep the mapping; we only ever match a fingerprint we’re handed against one we’ve seen before.

Their previous conversation, on any device, as long as your page identifies them. First visit for a given ID is an ordinary fresh chat.

Level 3 — Option B: your page holds the token

Section titled “Level 3 — Option B: your page holds the token”

Use this when you want portability but don’t have logged-in users — or when you’d rather own where the conversation token lives (your own cookie, your own account record, your own app storage).

The widget hands you an opaque token; you keep it; you hand it back later.

Three messages, over postMessage between the iframe and your page:

Direction Message When
Widget → your page { type: 'ragtime-session', status: 'ready' } On load. Your cue to reply if you have a stored token.
Widget → your page { type: 'ragtime-session', status: 'active', sessionId, expiresAt } After each save. Store sessionId.
Your page → widget { type: 'ragtime-host', action: 'resume', sessionId } Your reply to ready.
<iframe
id="ragtime"
src="https://<host>/chat/<orgSlug>/<projectSlug>/<deviceSlug>"
width="100%" height="600px" frameborder="0"
allow="microphone; camera; display-capture; autoplay"
></iframe>
<script>
const RAGTIME_ORIGIN = 'https://<host>';
const frame = document.getElementById('ragtime');
window.addEventListener('message', async (event) => {
if (event.origin !== RAGTIME_ORIGIN) return;
if (event.data?.type !== 'ragtime-session') return;
if (event.data.status === 'ready') {
// Hand back whatever you stored for this visitor, if anything.
const stored = await yourBackend.getRagtimeSession();
if (stored) {
frame.contentWindow.postMessage(
{ type: 'ragtime-host', action: 'resume', sessionId: stored },
RAGTIME_ORIGIN
);
}
}
if (event.data.status === 'active') {
// Persist for next time. Server-side storage is strongly preferred.
await yourBackend.saveRagtimeSession(event.data.sessionId, event.data.expiresAt);
}
});
</script>

Answer ready quickly. The widget waits about a second, then gets on with greeting the visitor. If your lookup is slow, do it before the iframe loads and have the answer ready. Missing the window isn’t fatal — you’ll still receive the token on the first save — but that visit won’t resume.

Never put the token in a URL. It is a read-access key to that conversation: anyone holding it can read the transcript. URLs leak through referrer headers, browser history, server logs and copy-paste. Keep it in your backend, or at minimum an HttpOnly cookie.

Your situation Use
Public site, no accounts, “good enough” continuity Level 1 alone
Visitors log in to your site Option A
No accounts, but you want continuity across devices or your own app Option B
You have accounts and a native app Both — Option A wins when both are available

Options A and B layer on top of Level 1 rather than replacing it: if Option A has no signed user, or Option B’s handshake goes unanswered, the visitor’s own browser token is still tried before starting fresh.

What carries over — and what deliberately doesn’t

Section titled “What carries over — and what deliberately doesn’t”

Carries over: the conversation itself, the selected language, the point the assistant had reached in a multi-step persona, and the visitor’s position in a guided form (including answers already given).

Deliberately does not: anything that did something. If a step in your guided form triggered a webhook, sent an email, or called your API the first time through, resuming does not fire it again. Ragtime restores where the visitor was standing, never replays what happened. A returning visitor mid-form picks up at the next unanswered question.

If you change a guided form’s design while someone has a conversation paused inside it, their saved position may no longer fit the new flow. That’s handled: they get a normal fresh greeting rather than a broken form.

Not supported: resuming mid-flow in live voice/avatar conversations. Those run on a different pipeline. Resumable sessions target text and text-with-avatar chat.

  • The stored conversation is a live record, kept as written — not anonymised, because it has to be shown back to the visitor verbatim. It is separate from the anonymised transcripts behind analytics and insights.
  • It is deleted automatically once the retention window passes with no activity. Shorter windows mean less data at rest — 30 days suits most sites; pick lower if your conversations touch sensitive ground.
  • Turning the device setting off — or conversation storage off project-wide — makes existing conversations immediately unresumable.
  • Anyone holding the session token can read that conversation. That is what makes resume work without a login, and it is why Option B insists you keep the token out of URLs.
  • If your privacy notice enumerates what you store about visitors, enabling this is a change worth reflecting there.

Devices → edit the device → Stored conversations → Delete stored conversations. It shows how many are held and erases them immediately. Available to org admins and project owners, and recorded in your audit log.

Unresumable is not the same as deleted: switching the feature off stops conversations being served back, but they remain until they expire. Use the delete button when you want the data gone now.

Because sessions are anonymous, there is no way to look one up by name — so an erasure request for a specific visitor is served by deleting the device’s stored conversations. Shorter retention windows reduce how often this comes up.

Symptom Likely cause
The toggle is greyed out Store conversations is off for the project — turn it on under Analytics → Conversation storage first.
Nothing ever resumes Resumable sessions is off for that specific device, the device is disabled, or conversation storage was switched off project-wide.
Resumes on desktop, not on mobile That’s Level 1 working as designed — browser storage doesn’t travel. You need Option A or B.
Option A always starts fresh The signature isn’t validating: the secret differs, the parameters weren’t sorted before signing, or the timestamp is older than 5 minutes. An unsigned ragtimeUserId is ignored silently.
Option B never receives ready Your message listener is checking the wrong origin, or it’s registered after the iframe has already loaded.
A very long conversation resumes part-way in Past the platform’s message ceiling only the most recent turns are kept, so resume lands near the end rather than the very beginning.
A conversation you expected to resume starts fresh It probably ended naturally — a farewelled conversation is not resumed by design.