Embeddable SDK (@flow/embed)

@flow/embed lets you add member sign-in, plus typed storefront and members clients, to your own website — with one script tag. Members type their password inside a sign-in widget served from our origin (an iframe), so your page's code never touches their credentials. On success your page receives a short-lived member token over an origin-pinned channel and can call the Members API as the signed-in member.


Include the SDK

Script tag (browser global)

<script src="https://embed.example.com/flow.js"></script>
<script>
  // window.Flow is now available
  Flow.configure({
    storefrontApiUrl: "https://storefront.example.com/v1",
    participantApiUrl: "https://api.example.com",
  });
</script>

Or as a module

import { Flow, configure, mountSignIn, storefront, members } from "@flow/embed";

configure(config) sets SDK-wide defaults once at boot so you don't repeat them on every call:

configure({
  embedOrigin: "https://embed.example.com",      // where the widget + script are served (defaults to the script's origin)
  storefrontApiUrl: "https://storefront.example.com/v1",
  participantApiUrl: "https://api.example.com",
});

Read public data — Flow.storefront(pk)

A typed, read-only client backed by your publishable key. Safe to run in the browser.

const shop = Flow.storefront("flow_pk_your_publishable_key");

await shop.listClasses();      // StorefrontClass[]
await shop.listInstructors();  // StorefrontInstructor[]
await shop.listPlans();        // StorefrontPlan[]
await shop.config();           // StorefrontConfig — { providerId, slug, name, allowedOrigins }

These map onto the Storefront API reads.


Sign a member in — Flow.mountSignIn(el, options)

Mount the sign-in widget into a container element. The member signs in inside the widget; your page is notified and gets a token getter.

const handle = Flow.mountSignIn(document.getElementById("signin"), {
  pk: "flow_pk_your_publishable_key",
  studio: "your-studio-slug",        // which studio the member belongs to
  theme: { "--flow-color-accent": "#7c5cff" }, // optional brand overrides
});

mountSignIn returns a SignInHandle:

Method Description
onSignIn(cb) Fires when a member signs in; cb(member) receives { id, email, name }. Returns an unsubscribe.
onSignOut(cb) Fires when the member signs out.
getMember() The current member, or null.
getToken() Resolves to a usable short-lived access token (silently re-minted).
members() A Members API client bound to this session (token + key attribution).
signOut() Signs the member out (clears the session on our origin).
destroy() Removes the widget and its listeners.

Register your origins first. Member tokens are only ever delivered to an origin you have registered for the studio (in your dashboard). An unregistered page can read public storefront data but can never receive a member token — embedding sign-in fails closed.


Act as the member — Flow.members(getToken)

A typed Members API client. Get one either from the sign-in handle (handle.members()) or directly from a token getter:

const me = handle.members();
// or: const me = Flow.members(() => handle.getToken(), { pk: "flow_pk_…" });

// reads
await me.me();           // Me — { id, email, name, activeProviderId }
await me.memberships();  // Membership[]
await me.bookings();     // Booking[]

// writes (each carries an Idempotency-Key automatically)
await me.book(occurrenceId, { channel: "online" });    // Booking
await me.cancel(bookingId);                            // void
await me.updateMe({ name: "Sam Lee", notifyByEmail: true });
await me.purchaseDropIn(occurrenceId);                 // CheckoutResult
await me.subscribe(planId, { couponCode: "WELCOME" }); // CheckoutResult

Every write generates a fresh Idempotency-Key per call (safe for network retries). To dedupe across separate calls — for example a double-clicked "Buy" — pass a stable one: me.book(id, { idempotencyKey: "stable-key" }).

A CheckoutResult is { orderId, status, redirectUrl? }. When status is "pending", send the member to redirectUrl to complete payment; "succeeded" means access was granted immediately.


The token model


A minimal working page

<!doctype html>
<html>
  <head><meta charset="utf-8" /><title>My studio</title></head>
  <body>
    <div id="signin" style="max-width: 360px"></div>
    <pre id="out">Sign in to load your bookings…</pre>

    <script src="https://embed.example.com/flow.js"></script>
    <script>
      Flow.configure({
        storefrontApiUrl: "https://storefront.example.com/v1",
        participantApiUrl: "https://api.example.com",
      });

      const handle = Flow.mountSignIn(document.getElementById("signin"), {
        pk: "flow_pk_your_publishable_key",
        studio: "your-studio-slug",
      });

      handle.onSignIn(async (member) => {
        const me = handle.members();
        const bookings = await me.bookings();
        document.getElementById("out").textContent =
          `Welcome ${member.name ?? member.email} — ${bookings.length} bookings`;
      });

      handle.onSignOut(() => {
        document.getElementById("out").textContent = "Signed out.";
      });
    </script>
  </body>
</html>

Next