Embeddable widget
Drop the Flow chat widget onto any page with a single <script> tag. The widget
mounts into an isolated Shadow DOM, so its styles never clash with your site.
Quick start
Copy the snippet from Settings → Widget (it already carries your workflow’s
public token) and paste it before </body>:
<script
src="https://app.example.com/widget/flow-widget.js"
data-token="<public_token>"
data-mode="bubble"
async
></script>
Colors, title, launcher, position and language default to the workflow’s server
config; any data-* attribute overrides the server value. Use
data-mode="inline" together with data-target="#some-id" to render the chat
inside an element instead of a floating bubble.
Identify your end-user
If your page already knows who the visitor is, hand the widget a JWT signed with your workspace’s embed secret (rotate it in Settings → Widget). The widget keeps the token in the browser and attaches it to every message so the backend can attribute the conversation to that user.
Either pass it statically via data-user-jwt:
<script
src="https://app.example.com/widget/flow-widget.js"
data-token="<public_token>"
data-user-jwt="<end-user JWT>"
async
></script>
…or call flowWidget.identify(jwt) at runtime. Use the command-queue shim so it
works even before the bundle finishes loading:
<script>
window.flowWidget = window.flowWidget || { _q: [] };
flowWidget.identify = flowWidget.identify
|| function () { (flowWidget._idq = flowWidget._idq || []).push(arguments); };
flowWidget.identify("<end-user JWT signed with the embed secret>");
</script>
Call browser JS from a workflow
A workflow can invoke JavaScript that runs on your page (read the cart, highlight
an element, open a modal). Declare functions with flowWidget.defineFunction; the
handler stays in the browser, only its {name, description, parameters} shape is
advertised to the workflow each turn.
<script>
window.flowWidget = window.flowWidget || { _q: [] };
flowWidget.defineFunction = flowWidget.defineFunction
|| function () { flowWidget._q.push(arguments); };
flowWidget.defineFunction({
name: "get_cart_total",
description: "Returns the current shopping-cart total",
parameters: { type: "object", properties: {} },
handler: async () => ({ total: window.cart.total }),
});
</script>
When the workflow calls get_cart_total, the widget runs your handler and sends
the result back — a synchronous round-trip your workflow can branch on.
Tip: every run is isolated and billed to the workflow owner’s plan.