Flows integrations & tracking
How a Flow fans a qualified lead out to ad platforms, CRMs, and Zapier — with a hard split between hashed ad signals and first-party PII, and no clinical detail ever leaving the platform.
A published Flow doesn't just route an order into the rail — it can also tell your marketing stack that a lead qualified. neolife does this with a two-tier destination model and one governing rule: outbound marketing signals never carry clinical detail. Ad platforms get a hashed identifier and a closed-vocabulary event; your own CRM gets the lead's contact details under your own BAA and a neutral funnel label — never a diagnosis, a product, a questionnaire, or a verdict. This is analytics that cannot leak PHI applied to the acquisition side of the funnel.
When a session qualifies, a dispatcher fans it out to every destination you've configured, handing each one only the context its tier is allowed to see. disqualified sessions never leave the platform.
The two tiers#
There are exactly two kinds of destination, and they see different things.
| Tier | Destinations | Receives | Runs under | Flag |
|---|---|---|---|---|
| Ad pixels | Meta Conversions API, GA4, TikTok | A hashed identifier (SHA-256 email) + a closed-vocabulary event. Never a raw email, never any health context. | neolife's server-side path | funnel_ad_conversions (counsel-gated) |
| First-party CRM/CDP | Segment, HubSpot | The lead's email / name + a neutral funnel label. Never clinical detail. | Your own BAA | flow_cdp_destinations (Growth+) |
The distinction is the whole design. An ad platform is a third party you have no BAA with, so it may only ever receive a hash and a generic conversion event — no product name, no category, no questionnaire key, no verdict. Your CRM is yours, covered by your BAA, so it may receive the lead's identity and a plain funnel name (e.g. "Metabolic Reset intake") so your team can follow up — but still nothing from the clinical plane.
Rule: the ad tier fires only on eligible sessions and never sees who the patient is; the CRM tier may see who the patient is but never sees what they answered. Neither ever receives a
disqualifiedevent.
Ad-pixel destinations#
Meta CAPI, GA4, and TikTok receive a server-side conversion when a session qualifies. The payload is a hashed identifier plus a fixed event name from a closed vocabulary — there is no field in which a product, dose, condition, or verdict can travel.
- Server-side, not client-side. Post-identity conversions go out over each platform's server API (Conversions API / Measurement Protocol / Events API), not a browser pixel, so the hashing happens on neolife's side and the raw email never touches the ad platform.
- Click-ID capture (
fbclid,gclid,ttclid) is collected on the landing step so a server-side conversion can be attributed back to the ad that drove it. - Eligible only. A
disqualifiedoutcome is a hard stop that never fires a conversion — the platform can't learn that a specific person failed a medical screen.
This tier is gated by the counsel-gated funnel_ad_conversions flag. Turning it on for a tenant is a compliance decision, not a config toggle: a signed tracking-tech memo and the tenant's marketing-authorization posture come first. See Flow flags & entitlements.
First-party CRM / CDP destinations#
Segment and HubSpot are different: they run under the clinic's own BAA, so they're allowed to receive the lead's actual contact details. A qualified session sends the lead's email and name plus a neutral funnel label, so your sales or care team can follow up — and nothing else. No clinical answers, no verdict, no certificate content.
This tier is gated by flow_cdp_destinations (Growth and above). It presumes you have a BAA in place with the destination; neolife does not extend its own BAA to your CRM.
Zapier & webhooks#
Everything else connects through the platform's existing signed flow.* webhooks — the same PHI-free, signature-verified event surface the rest of the rail uses. A Zapier app subscribes to these events, which lets a Flow reach thousands of downstream tools without neolife maintaining a connector farm.
The Flow-specific events are:
| Event | When |
|---|---|
flow.published |
A provider approved and published a flow version. |
flow.session.qualified |
A session cleared the clinical screen (verdict eligible). |
flow.session.completed |
A session finished the funnel. |
flow.checkout.completed |
The clinic's store reported checkout done for a handed-off session. |
Payloads carry ids, an embed key, and a verdict label — never patient identity or clinical answers, exactly like every other neolife event:
{
"type": "flow.session.qualified",
"id": "evt_1a2b3c",
"data": {
"flowId": "flow_123",
"embedKey": "fk_live_…",
"sessionId": "fs_9",
"verdict": "eligible"
}
}
Verify the signature before acting, dedupe on the event id, and treat the event as a signal to fetch rather than a data source — the same discipline described in Webhooks & events and The PHI boundary.
Contacts: funnel-to-fulfillment attribution#
Under the CRM tier, every submitted lead is also merged into a unified Contact inside neolife, keyed on a PHI-free emailHash. The lead's email and name are encrypted at rest; the hash is what stitches multiple sessions from the same person into one Contact.
The payoff is attribution nobody without the pharmacy rail can offer: once a provider approves the intake, the Contact links to the resulting Patient, which joins to the order and its shipment. That closes the loop from the ad that drove the click all the way to the medication that shipped — funnel-to-fulfillment, in one record.
You can browse this in the Contacts view in the console, with a per-contact journey. The identity graph is deliberately narrow — it stitches a lead to their own fulfillment, not a general-purpose audience-syncing CDP.
Custom domains#
A Flow's hosted page can run on your own subdomain — start.theirclinic.com — instead of a neolife URL, via Cloudflare for SaaS.
- Add the hostname in the console.
- neolife returns the CNAME and TXT records to add at your DNS provider.
- Verification polls Cloudflare until the records resolve; the domain then serves the flow.
Custom domains are gated by flow_custom_domains (Growth and above) and served from neolife's BAA-covered edge — never the static marketing host, which can't hold PHI. The runtime edge rewrite that serves a flow at the custom-domain root is the one piece that's activated with live Cloudflare credentials at deploy time.
Configuration#
Destinations are configured per tenant and toggled in the console's Integrations tab. A few properties hold across all of them:
- Secrets are write-only. API keys, access tokens, and dataset ids you enter are stored encrypted; the API never returns secret values on read — you see that a destination is configured, not its credentials.
- Per-tenant. One clinic's destinations and credentials are never visible to another.
- Feature-flagged. A destination only fires if its flag is on for the tenant (
funnel_ad_conversions,flow_cdp_destinations,flow_custom_domains), which also respects tier entitlements. See Flow flags & entitlements.
Recap#
- Two tiers, two contracts. Ad pixels get a hash + closed-vocabulary event and run under neolife's server-side path; CRMs get email/name + a neutral label and run under your BAA. Neither sees clinical detail; neither sees
disqualified. - Zapier and everything else ride the signed, PHI-free
flow.*webhooks — no connector farm. - Contacts stitch each lead on a PHI-free
emailHashand link to fulfillment post-approval, giving you funnel-to-fulfillment attribution. - Custom domains run on Cloudflare for SaaS off neolife's BAA-covered edge, never the static marketing host.
- Every marketing destination is per-tenant, flag-gated, and write-only on secrets — analytics that cannot leak PHI, by schema, not by policy.