Hosting & embedding a flow
The two ways a published flow reaches patients — a hosted page and a drop-in script — plus theming, the public runtime API, the checkout handoff, and why none of it can leak PHI.
A published flow renders in exactly two places: a hosted page neolife serves for you, and an embeddable widget you drop into your own site. Both are served from neolife's BAA-covered API — never from the static marketing host or your storefront's static host — because the flow's clinical plane runs where PHI is allowed to run. Everything below the surface is the same runtime: the same public definition, the same session lifecycle, the same certificate at the end.
What Publish gives you#
When a licensed provider approves and publishes a flow, the builder mints an embedKey — a per-flow, patient-safe public key (not your secret API key). Every hosted and embedded surface is keyed on it, and it's the only credential a patient-facing page ever carries. Publish hands you two things off that key:
- A hosted URL —
https://api.neolife.health/f/:embedKey— a complete, themed page you can link to directly. - An embed snippet — a one-line
<script>that mounts the same flow inside a page you control.
The embedKey maps to the flow's published clinical-plane hash. Re-publishing a flow (a copy or theme change on the presentation plane, or a re-approval of the clinical plane) keeps the same embedKey — the URL and snippet you shipped keep working. See Publishing a flow for what the approval gate records.
The hosted page#
The simplest surface. Point a patient at the hosted URL and neolife renders the full funnel — landing copy, marketing quiz, medical intake, outcome screen — on infrastructure that's already in PHI scope.
https://api.neolife.health/f/f_9k2m…
Use the hosted page when you want a link to drop into an ad, an email, or a QR code without touching your own site. If you're on Growth or above, you can front it with a custom domain so the patient sees start.yourclinic.com instead.
The embeddable widget#
To mount a flow inside a page you already run, load the self-contained renderer and give it a target and an embedKey:
<div id="neolife-flow"></div>
<script
src="https://api.neolife.health/embed/v1/flow.js"
data-embed-key="f_9k2m…"
data-target="#neolife-flow"></script>
flow.js is a single self-contained script — no framework, no build step, no external dependencies. It fetches the public flow definition, renders every step, manages the session, and posts answers back to the runtime API. It is origin-aware: a flow only renders on the domains you've registered for its tenant.
Rule: the embed script carries the
embedKey, never your secret API key. TheembedKeycan only read a patient-safe flow definition and drive a session — it can't reach the authenticated surface, list orders, or read PHI. Ship it in client-side HTML without a second thought.
Theming#
The presentation plane is yours to brand. flow.js and the hosted page both read a set of --nl-flow-* CSS custom properties, so a theme is just variables — set them once on a wrapping element and the whole flow inherits them:
<div id="neolife-flow" style="
--nl-flow-bg: #f7f3ea;
--nl-flow-fg: #1f3a2a;
--nl-flow-accent: #d98a2b;
--nl-flow-radius: 12px;
"></div>
The theme tokens saved on the flow in the builder are applied by default; overriding the variables at the mount point lets you match a specific page. Theming touches the presentation plane only — copy, color, type, spacing. It can't change a medical question, reorder a clinical step, or alter a disqualifier. Those live on the clinical plane, and the plane split is enforced by the runtime, not by convention. See The two-plane model for why that line is load-bearing.
The public runtime API#
Both surfaces speak the same public API, keyed on the embedKey. It's the contract you'd implement against if you build a fully custom renderer instead of using flow.js. Every route is patient-safe — no Bearer key, no PHI on the way out.
| Method & path | What it does |
|---|---|
GET /public/flows/:embedKey |
Returns the patient-safe flow definition — steps, copy, theme tokens, and the A/B assignment. Never the internal rule detail or provider packet. |
POST /public/flows/:embedKey/sessions |
Starts a session and returns a session id. Assignment to an A/B arm is deterministic and sticky from here. |
POST /public/flows/:embedKey/sessions/:id/steps |
Partial save — persists answers as the patient advances, so a drop-off can be measured and a return can resume. |
POST /public/flows/:embedKey/sessions/:id/submit |
Submits the completed intake. Delegates to the deterministic intake engine → issues the certificate → enqueues provider review → routes to the pharmacy. |
POST /public/flows/:embedKey/sessions/:id/events |
A PHI-free beacon — view, step-advance, and drop-off signals that feed analytics. Ids and closed-vocabulary event names only. |
submit is the seam where a flow rejoins the fulfillment rail. It does not run its own eligibility logic: it hands the collected answers to the same deterministic engine every other intake path uses, so a flow-sourced submission gets the same verdict, the same certificate, and the same provider gate as an order that came in through Shopify or the API. The clinical guarantee is identical no matter which surface collected the answers.
# Fetch the patient-safe definition — no auth, no PHI
curl https://api.neolife.health/public/flows/f_9k2m…
The checkout handoff#
A flow never takes the patient's money. Prescriptions can't settle on a generic card rail, so checkout is a handoff, not a step neolife owns. On a submit that clears the deterministic gate, the runtime issues the certificate and hands the patient off to the clinic's own store carrying an opaque certificate token — the same token the Shopify integration re-verifies server-side before any pharmacy order is armed.
- neolife collects answers, runs the gate, and issues the certificate.
- The patient is handed to your checkout with the certificate token attached.
- Your store re-verifies the token server-side; the fulfillment layer is the hard gate. Gate the prescription, not the cart.
neolife never holds patient funds and never becomes a merchant of record. The certificate is proof the patient cleared the clinical gate — it is not insurance and says nothing about coverage. What crosses onto your checkout is an opaque token and nothing else; the answers stay on neolife's covered infrastructure. PathwAI gets the patient to your door; neolife gets the medication to theirs.
Custom domains#
On Growth and above, a flow can be served from a subdomain you own — start.yourclinic.com instead of the neolife-hosted URL. Add the domain in the builder and neolife (via Cloudflare for SaaS) returns the CNAME and TXT records to set; verification polls Cloudflare until the records resolve, then the hosted page answers on your domain. This surface is gated by the flow_custom_domains flag and requires the Cloudflare edge rewrite that serves a flow at a custom-domain root — a deployment step, not a code one.
Why none of this leaks PHI#
Every patient-facing surface here is PHI-free by construction, not by policy:
- The
embedKeyreads a patient-safe definition and drives a session. It can't reach an authenticated read. - The events beacon carries ids and a closed vocabulary of event names — never an email, never an answer, never a clinical string.
- The checkout handoff carries an opaque certificate token — no answers, no diagnosis, no identity.
- The answers themselves post browser-direct to neolife's covered API and never enter your page's DOM, your storefront, or any third-party analytics tag.
This is the same discipline the rest of the platform runs on. The definitive statement of what crosses the wire lives in The PHI boundary: ids and status travel; clinical detail stays server-side and is hydrated only over authenticated reads. Analytics inherits the same property — analytics that cannot leak PHI, by schema, not by policy.
Where a flow stays gated#
Rendering a flow to a real patient is a clinical act, so the same guardrails as the rest of the platform apply. Patient-facing publish stays behind the platform's existing PHI-readiness gate; before that, you build and preview a flow with NEOLIFE_MODE=demo. And a flow can't be published — and therefore can't be hosted or embedded — until a licensed provider has approved its clinical plane. AI drafts, a licensed provider signs.
Next steps#
- Publishing a flow — the approval gate and the flow approval certificate that mint the
embedKey. - The two-plane model — why theming and A/B can't touch a clinical screen.
- Analytics — the PHI-free funnel stats the events beacon feeds.
- Intake & provider approval — the deterministic engine
submitdelegates to. - The PHI boundary — the platform-wide rule this surface inherits.