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.
<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>
| 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) |
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>
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.
Always pin data-version in production. When you publish v2, existing embeds on v1 keep working.
The SDK renders semantic class names:
.intake-flow — root form.intake-field — field wrapper.intake-step-header — step title.intake-error — field errorsStyle these in your app's CSS. The SDK ships unstyled by design.
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.
| 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.
When you embed Intake Relay in a product that collects personal data, you are the controller toward your end users. You should:
?version= on intake and presign requests so submissions match the schema you tested.Idempotency-Key on intake POST requests to avoid duplicate submissions on retries.See our DPA and Subprocessors pages for business customers.