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.