Embedding
Embed your Ragtime assistant into any website or application using a simple iframe. Pass data via query parameters or POST body, and optionally secure requests with HMAC signatures.
Basic Iframe
Section titled “Basic Iframe”The simplest way to embed your assistant is a standard iframe. Replace the URL with your assistant’s live URL (found in Dashboard → Assistant → Deployment):
<iframe src="https://your-ragtime-instance.com/chat/org-slug/assistant-slug" width="100%" height="700" style="border: none; border-radius: 12px;" allow="microphone; geolocation"></iframe>If the assistant has Require GPS location enabled in the dashboard, the embedded chat will request browser location access and will not proceed without the user’s permission. In that mode, make sure the host page allows geolocation in the iframe.
Location-aware assistants
Section titled “Location-aware assistants”When geospatial retrieval is enabled for an assistant, the chat client includes the user’s current location in each request automatically. The behavior is controlled by the assistant’s dashboard settings, so location-aware parameters are ignored unless Require GPS location is turned on.
If you are rendering Ragtime inside your own iframe wrapper, include geolocation in the iframe permissions and expect the browser to prompt the end user for access.
Passing Data via Query Parameters
Section titled “Passing Data via Query Parameters”If you’ve configured External Variables (Dashboard → Assistant → Deployment → External Variables) with URL Query source, append them as query parameters:
<!-- Pass data via URL query parameters --><iframe src="https://your-ragtime-instance.com/chat/org-slug/assistant-slug?user_name=John&locale=en" width="100%" height="700" style="border: none; border-radius: 12px;" allow="microphone" name="ragtime-chat"></iframe>Passing Data via POST Body
Section titled “Passing Data via POST Body”To keep values out of the URL, use POST Body variables. Create a hidden form that targets the iframe by name. (A POST hides values from the address bar; it does not prove they came from you. For that, turn on signing.)
<!-- When using POST body parameters, submit via a form targeting the iframe --><iframe name="ragtime-chat" width="100%" height="700" style="border: none; border-radius: 12px;" allow="microphone"></iframe>
<form method="POST" action="https://your-ragtime-instance.com/chat/org-slug/assistant-slug" target="ragtime-chat" style="display:none;"> <input type="hidden" name="user_id" value="abc123" /> <input type="hidden" name="session_token" value="..." /></form>Variable types
Section titled “Variable types”Each external variable has a type, set in the Type column next to it:
| Type | Accepts |
|---|---|
| Text | Up to 200 characters (or a lower limit you set), on one line |
| Number | A number, optionally within a minimum and maximum |
| Yes / no | true or false |
| Choice list | One of the values you list; the assistant reads your label for it |
| ID | Letters, digits, _ and -, up to 64 characters |
A value that does not fit its type is dropped, never cut short: a shortened value can say something you did not mean. Variables declared before types existed are treated as Text, with the 200-character limit.
HMAC Signature (Security)
Section titled “HMAC Signature (Security)”When Security → Require signature is on, Ragtime only accepts values it can prove came from your server. That means signing:
- every chat page link that carries values (external variables or injection),
- every form POST to the chat page, and
- every direct API call from your backend (send the signature as an
X-Ragtime-Signatureheader).
A plain link with no values (or only ?lang=) needs no signature. A signature is valid for 5
minutes, so generate the URL when your page is rendered. The signature is an HMAC-SHA256 over the
sorted query string (including timestamp, excluding signature) followed by the raw request body,
if any. You cannot use a static HTML snippet; generate the URL on your server:
import crypto from 'crypto';
/** * Generate an HMAC-SHA256 signature for request verification. * The signing secret is found in Dashboard → Assistant → Deployment → Security. */function generateHmacSignature(secret: string, payload: string): string { return crypto.createHmac('sha256', secret).update(payload).digest('hex');}
async function generateSignedUrl() { const baseUrl = 'https://your-ragtime-instance.com/chat/org-slug/assistant-slug'; const params = new URLSearchParams();
// Add any query parameters params.append('user_name', 'John');
// Generate signature const timestamp = Date.now().toString(); params.append('timestamp', timestamp); params.sort();
const payload = params.toString(); const signature = generateHmacSignature(process.env.RAGTIME_SIGNING_SECRET!, payload); params.append('signature', signature);
return `${baseUrl}?${params.toString()}`;}The signing secret is found in Dashboard → Deployment → Embedding → Security. Keep it server-side; never expose it to the client.
A link that carries values but is not signed (or whose signature is wrong or older than 5 minutes) is answered with 401 Invalid signature instead of the chat. If signing is on but no secret is set, the page answers with an error rather than accepting unchecked values.
Which sites may embed: Allowed Domains
Section titled “Which sites may embed: Allowed Domains”Only the sites listed under Dashboard → Deployment → Embedding → Allowed Domains may show the assistant in a frame (subdomains included). With none listed, it cannot be embedded anywhere. Opening the chat link directly always works.
A site that is not listed sees a short message in the frame naming itself and this setting. If the browser does not say which site is embedding, it blocks the frame on its own. An LMS must be listed too, including the domain it serves content from.
Event Listener
Section titled “Event Listener”The embedded assistant communicates with your page via postMessage. Add this listener to capture lifecycle events and AI-triggered actions:
// Listen for Ragtime chat events and actionswindow.addEventListener('message', (event) => { const { data } = event;
// Lifecycle events: // chat_started, thinking, response_start, response_given, emotion, // session_ended_idle, session_ended_natural, session_restarted, listening, // stage_changed, // session_ended_user, session_ended_remote, session_ended_max_duration, session_ended_error, // idle_timeout_start, idle_timeout_tick, idle_timeout_stop, // mic_timeout_start, mic_timeout_tick, mic_timeout_stop, mic_max_duration // // Transcript events (opt-in via Deployment settings): // transcript_user, transcript_assistant if (data?.type === 'ragtime-event') { console.log('[Ragtime Event]', data.event, data.detail); }
// Page events sent by an action (see Actions → Steps → Page event) if (data?.type === 'ragtime-action') { console.log('[Ragtime Action]', data.action, data.args); }});
// Optional: let the host application drive a conversation stage.// Requires conversation stages on the assistant.const frame = document.querySelector('iframe[name="ragtime-chat"]');frame?.contentWindow?.postMessage( { type: 'ragtime-host', action: 'set-stage', stageId: 'stage-3' }, 'https://your-ragtime-instance.com');Available Events
Section titled “Available Events”| Category | Events |
|---|---|
| Chat lifecycle | chat_started, thinking, response_start, response_given, emotion |
| Session end | session_ended_idle, session_ended_natural, session_ended_user, session_ended_remote, session_ended_max_duration, session_ended_error |
| Session control | session_restarted, listening |
| Conversation stages | stage_changed |
| Idle timeout | idle_timeout_start, idle_timeout_tick, idle_timeout_stop |
| Mic timeout | mic_timeout_start, mic_timeout_tick, mic_timeout_stop, mic_max_duration |
| Transcript (opt-in) | transcript_user, transcript_assistant |
| Page events | host_event_accepted, host_event_dropped (see Page events) |
Page events
Section titled “Page events”Your page can tell a running conversation what the visitor is doing: the room they tapped on a map,
the product they are looking at, the quiz they just failed. Declare the events under Dashboard →
Actions → Page events, turn Accept page events on for the channel (Deployment → Channels),
and send them with postMessage:
const frame = document.querySelector('iframe');frame.contentWindow.postMessage( { type: 'ragtime-host', action: 'event', name: 'place_selected', args: { place: 'spa' } }, 'https://your-ragtime-instance.com');Each event has one of three effects:
| Effect | What it does |
|---|---|
| Keep as state | The latest value is read on every turn, silently. Send args: null to clear it. |
| Shape next reply | Read once by the next reply, then forgotten. |
| Speak now | The assistant responds straight away. At most 6 per session, at least 20 seconds apart. |
Only declared events with values that fit their declared types are accepted, and the assistant never
reads what your page sent: it reads the sentence you wrote for the event, with the values filled in.
Every event is answered with host_event_accepted, or with host_event_dropped carrying a reason
and a plain-language message:
window.addEventListener('message', (e) => { if (e.data?.type === 'ragtime-event' && e.data.event === 'host_event_dropped') { console.warn(e.data.detail.name, e.data.detail.reason, e.data.detail.message); }});The starter HTML on Deployment → Custom template includes ready-to-paste code for the events you declared, and the dashboard preview has a Page events button for trying them before you have a page. The Page Events guide covers declaring events, their types and limits.
Starter Page
Section titled “Starter Page”For a quick start, use the Starter Page download button in Dashboard → Deployment → Embedding. It generates a ready-to-open HTML file with your assistant embedded and an event log — perfect for testing your integration locally.
Custom embed template
Section titled “Custom embed template”Customers can ship a fully custom HTML “shell” around the chat (header, branding, per-channel JS, etc.) and have Ragtime serve it as a real page. Configure it in Dashboard → Deployment → Custom template and toggle individual channels to use the template under Channels. Once a template is published, the channel’s public URL switches from /chat/<org>/<project>/<device> to /embed/<org>/<project>/<device>.
Everything above on this page works identically — just swap the URL. Query parameters, POST body variables, HMAC signatures, and the postMessage event stream are all forwarded between the outer /embed/... page and the chat iframe it wraps, so an external host that iframes /embed/... sees the same protocol as one that iframes /chat/... directly.
Inside the template itself, customer JS gets two extra conveniences:
window.addEventListener('ragtime:event', e => …)— chat events re-dispatched as DOMCustomEvents.window.ragtime.send(type, detail)— post a message into the chat iframe.
The {{RAGTIME_IFRAME}} token in the template is replaced with the chat iframe at render time. Two extra tokens are substituted per-request so a single template can branch on which channel is hosting it:
{{CHANNEL_SLUG}}— URL-safe channel identifier (e.g.kiosk-a). HTML-escaped; safe to use as text content or inside double-quoted attribute values.{{CHANNEL_NAME}}— human-readable channel label, HTML-escaped under the same rules.
The legacy {{DEVICE_SLUG}} / {{DEVICE_NAME}} tokens are still accepted for templates saved before this rename.
On the preview route (/embed/<projectId>) both tokens resolve to preview / Preview since the preview isn’t bound to a specific channel.
External Variables (declared under Deployment → Embedding → External Variables) work the same way for /embed/... as they do for /chat/... — values arrive via query string or POST body and are injected into the system prompt only if the variable is declared in the dashboard.