Embedding

Intake Relay ships a framework-agnostic JavaScript SDK (intake.js) that renders multi-step intake flows directly in your DOM. No iframe. Works with React, Vue, Svelte, static HTML, and embedded SaaS widgets.

Script tag (fastest)

<div id="onboarding"></div>
<script
  src="https://willowriverautomation.com/relay/v1/intake.js"
  data-flow-id="YOUR_FLOW_ID"
  data-version="1"
  data-embed-key="YOUR_EMBED_KEY"
  data-target="#onboarding">
</script>

Attributes

Attribute Required Description
data-flow-id Yes Intake flow UUID
data-version Yes Pinned published version number
data-embed-key Yes Per-flow public key
data-target No CSS selector (default: #intake-flow)
data-api-base No API origin (defaults to script origin)

Programmatic mount (callbacks)

Use this in React or when you need lifecycle hooks:

<script src="https://willowriverautomation.com/relay/v1/intake.js"></script>
<script>
  IntakeRelay.mount({
    target: "#onboarding",
    flowId: "YOUR_FLOW_ID",
    version: 1,
    embedKey: "YOUR_EMBED_KEY",
    apiBase: "https://willowriverautomation.com/relay/v1",
    onStepChange: ({ stepIndex, stepId, totalSteps, data }) => {
      analytics.track("onboarding_step", { step: stepId });
    },
    onSubmit: (result) => {
      window.location.href = "/dashboard";
    },
    onError: (err) => {
      console.error(err);
    },
  });
</script>

React integration

Use the headless client or mount into a ref:

import { useEffect, useRef } from "react";

export function OnboardingIntake({ flowId, version, embedKey }) {
  const ref = useRef(null);

  useEffect(() => {
    if (!ref.current || !window.IntakeRelay) return;
    window.IntakeRelay.mount({
      target: ref.current,
      flowId,
      version,
      embedKey,
      apiBase: process.env.REACT_APP_INTAKE_API ?? "https://willowriverautomation.com/relay/v1",
      onSubmit: () => navigate("/dashboard"),
    });
  }, [flowId, version, embedKey]);

  return <div ref={ref} />;
}

See embed/react/ for the @intakerelay/react scaffold.

Version pinning

Always pin data-version in production. When you publish v2, existing embeds on v1 keep working.

Styling

The SDK renders semantic class names:

Style these in your app's CSS. The SDK ships unstyled by design.

CORS

The hosted API allows cross-origin requests to /v1/* from browser embeds. If you load intake.js from your own domain and call the Intake Relay API directly, use the production base URL (https://willowriverautomation.com/relay/v1) or the same-origin path your app already proxies.

vs iframe embeds

Intake Relay SDK iframe
Custom styling Full control Limited
Parent page integration Callbacks, analytics postMessage only
Performance Single DOM Extra document
CSP complexity Script allowlist frame-src required

Intake Relay is optimized as an embedded form API for React and other frameworks without iframe dependency.

Compliance checklist (for embed customers)

When you embed Intake Relay in a product that collects personal data, you are the controller toward your end users. You should:

  1. Show a privacy notice before or alongside the form explaining what you collect and why.
  2. Disclose Intake Relay as a processor/subprocessor where required.
  3. Obtain consent or another lawful basis where applicable (GDPR, CPRA, etc.).
  4. Tell users that submission metadata (IP address, User-Agent) is collected automatically for abuse prevention.
  5. Pin ?version= on intake and presign requests so submissions match the schema you tested.
  6. Send Idempotency-Key on intake POST requests to avoid duplicate submissions on retries.

See our DPA and Subprocessors pages for business customers.