Flows

Custom domains

Serve a published flow from your own hostname — start.yourclinic.com — over TLS issued by Cloudflare for SaaS, still from neolife's BAA-covered origin.

By default a published flow is reachable at a neolife-hosted URL — a hosted page at /f/:embedKey, served from the same BAA-covered API that runs the runtime. A custom domain puts that same flow on a hostname you own — start.yourclinic.com — so patients see your brand end to end, not neolife's. Nothing about where the flow runs changes: the page is still served from neolife's origin, the PHI boundary still holds, and the flow is never served from the static marketing host (the static frontends sit outside the BAA-covered plane).

Custom domains are a Growth+ capability, gated by the flow_custom_domains feature flag. Adding, verifying, and removing a domain requires the FLOWS_MANAGE permission.

How it works#

neolife uses Cloudflare for SaaS to attach your hostname. The mechanics are the standard custom-hostname handshake:

  1. You register the hostname you want (start.yourclinic.com) in the Funnels → Domains section of the console, or over the API.
  2. neolife registers it as a custom hostname with Cloudflare and returns two DNS records for you to add at your registrar: a CNAME pointing your hostname at the flow host, and a TXT record that proves you own the hostname and lets Cloudflare issue a TLS certificate for it.
  3. You add those records at your DNS provider.
  4. neolife polls Cloudflare until the hostname goes active — at which point the domain is live and any patient hitting start.yourclinic.com is routed to your published flow.

The CNAME target is the flow host — flows.neolife.health by default. TLS is issued and renewed by Cloudflare against the TXT record; you never handle a certificate.

The flow is served from neolife's origin, on your hostname. A custom domain is a routing and branding layer — it does not move the runtime onto your infrastructure and does not put your systems in PHI scope. The analytics and event beacons a flow emits are PHI-free by construction regardless of what host it runs on.

Domain status#

A domain moves through three states:

Status Meaning
pending The hostname is registered but Cloudflare issuance is not active yet — either the DNS records aren't in place, or Cloudflare credentials aren't configured on the environment. The CNAME target is still shown so you can set the record up manually.
verifying The custom hostname is registered with Cloudflare and awaiting DNS propagation and certificate issuance.
live Cloudflare reports the hostname as active. TLS is issued and the domain now serves your published flow.

Only a live domain routes traffic. A domain resolves to the flow explicitly bound to it (if you passed a flowId when adding it) or, if unbound, to your tenant's most recently published flow.

The LegitScript interstitial#

Serving a flow on your own root domain is a step neolife takes on your certification, not ours. A published flow on a custom domain is gated by a LegitScript interstitial: the domain routes on the clinic's own LegitScript certification and attestation. This is consistent with the rest of the publish gate — the clinical plane is provider-approved, the presentation plane passes the claims linter, and the domain-serving step carries your attestation. See Publishing a flow for the full gate.

Rule: neolife will not serve a clinic's flow from a clinic-branded root domain without that clinic's own certification on file. Custom domains do not bypass any of the publish gates — they add one.

Manage domains over the API#

All paths are under /v1/flows/domains. Every route requires the FLOWS_MANAGE permission; adding a domain additionally requires the flow_custom_domains entitlement.

Add a domain#

POST /v1/flows/domains. Register a hostname and get back the DNS records to set. Pass an optional flowId to bind the domain to a specific flow; omit it to serve your most recent published flow.

curl -X POST https://api.neolife.health/v1/flows/domains \
  -H "Authorization: Bearer $NEOLIFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "hostname": "start.yourclinic.com",
    "flowId": "flow_1a2b3c"
  }'
{
  "id": "fdm_9f2c",
  "hostname": "start.yourclinic.com",
  "status": "verifying",
  "flowId": "flow_1a2b3c",
  "cnameTarget": "flows.neolife.health",
  "txt": {
    "name": "_cf-custom-hostname.start.yourclinic.com",
    "value": "0a1b2c3d-…"
  },
  "verifiedAt": null
}

Point the CNAME for hostname at cnameTarget, and add the txt record exactly as returned. If Cloudflare credentials aren't configured on the environment, the domain is created with status pending and txt is null — the cnameTarget is still returned so you can wire up the CNAME by hand. An invalid hostname returns 400; a hostname that's already registered returns 400.

List domains#

GET /v1/flows/domains. Every domain registered for your tenant, newest first.

curl https://api.neolife.health/v1/flows/domains \
  -H "Authorization: Bearer $NEOLIFE_API_KEY"
[
  {
    "id": "fdm_9f2c",
    "hostname": "start.yourclinic.com",
    "status": "live",
    "flowId": "flow_1a2b3c",
    "cnameTarget": "flows.neolife.health",
    "txt": { "name": "_cf-custom-hostname.start.yourclinic.com", "value": "0a1b2c3d-…" },
    "verifiedAt": "2026-07-10T18:04:22.000Z"
  }
]

Verify a domain#

POST /v1/flows/domains/{domainId}/verify. Re-checks the hostname's status with Cloudflare and updates it. When Cloudflare reports the hostname as active, the domain flips to live and verifiedAt is stamped. Call this after you've added the DNS records; the console's status check calls the same route.

curl -X POST https://api.neolife.health/v1/flows/domains/fdm_9f2c/verify \
  -H "Authorization: Bearer $NEOLIFE_API_KEY"
{
  "id": "fdm_9f2c",
  "hostname": "start.yourclinic.com",
  "status": "live",
  "flowId": "flow_1a2b3c",
  "cnameTarget": "flows.neolife.health",
  "txt": { "name": "_cf-custom-hostname.start.yourclinic.com", "value": "0a1b2c3d-…" },
  "verifiedAt": "2026-07-10T18:04:22.000Z"
}

An unknown domainId returns 404.

Remove a domain#

DELETE /v1/flows/domains/{domainId}. Unregisters the hostname. Traffic to it stops routing to your flow immediately.

curl -X DELETE https://api.neolife.health/v1/flows/domains/fdm_9f2c \
  -H "Authorization: Bearer $NEOLIFE_API_KEY"
{ "ok": true }

An unknown domainId returns 404.

Bringing a domain live#

  1. Add the domain — over the API or in Funnels → Domains — and note the returned cnameTarget and txt record.
  2. Add the DNS records at your registrar: a CNAME from your hostname to cnameTarget, plus the TXT record exactly as returned.
  3. Verify — run the status check (POST …/verify) once DNS has propagated. The domain moves through verifying to live.
  4. Certify — a live domain serving a patient-facing flow is gated by the LegitScript interstitial; your certification and attestation must be on file.

Once the domain is live and the flow is published, patients reach your funnel at your own hostname — over Cloudflare-issued TLS, still served from neolife's BAA-covered origin.

Related#

  • Flows overview — the two-plane model and where hosted pages fit.
  • The flow runtime — the hosted page, the embed snippet, and the public runtime API.
  • Publishing a flow — the provider-approval and claims gates a flow clears before it can be served, including the custom-domain interstitial.
  • The PHI boundary — why serving on your own hostname keeps your infrastructure out of PHI scope.