Embedding Workflows
A step-by-step guide to embed public Streamline workflows on your site (no authentication required).
Overview
This guide walks you through embedding a public Streamline workflow on your own website using an iframe. Embedded workflows run without requiring your visitors to log in; they are ideal for public facing forms.
You will:
- Build the embed URL for your workflow
- Add an iframe to your page
- Listen for postMessage events from the embed
- Handle resize events so the iframe height fits the content
- React to session completion or failure (optional)
Prerequisites
- A public workflow in Streamline (workflow must be configured to allow unauthenticated/embed access)
- Your workflow ID (from the Streamline app)
- Your site served over HTTPS in production (required for iframe and postMessage)
Workflow requirements for embedding:
The first step of the workflow must:
- Be either a Form or an Incoming Webhook.
- Not have an assignee.
Step 1: Build the embed URL
The embed URL has this form:
{BASE_URL}/start-session/{WORKFLOW_ID}?source=embed- BASE_URL: Your Streamline Production base URL (see below).
- WORKFLOW_ID: The workflow's unique ID from Streamline.
- source=embed: So that Streamline can treat the session as an embed.
Example:
https://us.streamline.intellistack.app/start-session/abc123-my-workflow-id?source=embedProduction base URL: https://us.streamline.intellistack.app (or your region's host if your organization uses a different domain).
Step 2: Add the iframe
Add an iframe and set its src to the URL from Step 1.
<iframe
id="streamline-embedded-workflow"
title="Streamline Workflow"
src="https://us.streamline.intellistack.app/start-session/YOUR_WORKFLOW_ID?source=embed"
width="600"
height="800"
style="border: none;"
></iframe>- Use a descriptive
titlefor accessibility. width="600"andheight="800"are just examples; change them or use CSS (e.g.width: 100%) to fit your layout.- Height can be updated dynamically in Step 4 when you handle
streamline:resizeevents.
Step 3: Listen for postMessage events
The workflow inside the iframe sends events to the parent window via postMessage. Always validate the message origin before handling data.
Allowed origin: Only accept messages from your Streamline Production origin, e.g. https://us.streamline.intellistack.app (or your region's host).
Event format: Messages are objects with type and payload. Streamline event types use the prefix streamline:.
Minimal listener with origin check:
const ALLOWED_ORIGINS = ['https://us.streamline.intellistack.app'];
function isOriginAllowed(origin) {
return ALLOWED_ORIGINS.some((allowed) => origin.startsWith(allowed));
}
window.addEventListener('message', (event) => {
if (!isOriginAllowed(event.origin)) {
return; // Ignore messages from other origins
}
const { type, payload } = event.data || {};
if (!type || !type.startsWith('streamline:')) {
return;
}
switch (type) {
case 'streamline:ready':
// Embed has loaded and is ready
// payload: { timestamp, sessionId }
break;
case 'streamline:resize':
// payload: { width, height } — both are emitted (see Step 4)
break;
case 'streamline:session:completed':
// User completed the workflow
break;
case 'streamline:session:failed':
// Session failed or expired
break;
default:
break;
}
});For security purposes, never process or trust event.data without checking event.origin against your allowlist.
Step 4: Handle resize events
The embed sends streamline:resize when its content size changes so your page can avoid scrollbars inside the iframe. Resize events are debounced (≈150 ms), so you may see a brief delay between content changes and the event.
Payload: { width, height } (numbers, in pixels). Both dimensions are included on every resize event.
Example: Update the iframe height when you receive a resize event:
const iframe = document.getElementById('streamline-embedded-workflow');
// Inside your message handler, for type === 'streamline:resize':
if (type === 'streamline:resize' && payload && payload.height > 0) {
iframe.style.height = `${payload.height}px`;
}You can also use payload.width if you need to constrain or match width.
Step 5: Handle session completion and failure (optional)
Use these events to show a thank-you message, redirect, or track analytics on your own website.
streamline:session:completed— User finished the workflow. Payload:{ sessionId }.streamline:session:failed— Session failed or expired. Payload:{ sessionId, error }(errormay beundefined).
Example:
case 'streamline:session:completed':
console.log('Workflow completed', payload?.sessionId);
// e.g. show thank-you message, redirect, or send to analytics
break;
case 'streamline:session:failed':
console.warn('Workflow failed or expired', payload?.sessionId, payload?.error);
break;Event reference
| Event | Description | Payload |
|---|---|---|
streamline:ready | Embed has loaded and is ready for interaction. | { timestamp, sessionId } |
streamline:resize | Content size changed. | { width, height } |
streamline:session:completed | User completed the workflow. | { sessionId } |
streamline:session:failed | Session failed or expired. | { sessionId, error? } |
All events are sent with type (string) and payload (object). Only process messages whose type starts with streamline: and whose event.origin is in your allowlist.
Security
- Origin validation: Always check
event.originagainst a fixed list of Streamline origins. Do not use wildcards or accept any origin. - HTTPS: In production, serve your page and use Streamline over HTTPS.
- Workflow visibility: Only workflows configured as public (embeddable without authentication) can be used with this URL.
Troubleshooting
- Embed shows "This page isn't available right now": The workflow is not configured correctly for embedding. Verify that the first step is a Form or Incoming Webhook, the first step has no assignee, and the workflow is public.
Tips
- Use
source=embedin the URL so Streamline can apply embed-specific behavior (e.g. styling, in-memory routing). - Give the iframe a sufficient initial
min-heightto avoid layout shift; resize events will refine it. - For debugging, log incoming messages and payloads; ensure you only act on allowed origins and
streamline:*types.
Updated about 5 hours ago
