What your storefront owns, and what the platform provides
Your storefront never renders a card field, never holds a payment credential, and never creates a
patient account. It presents a price, and hands the patient to a platform-hosted page with a link.
The full contract for that hand-off is the checkout door —
read it alongside this page; nothing here replaces it.
The four values, and where each may live
The publishable key is brand-scoped and read-only. It is issued by your onboarding contact, per
brand, and shown once — the same handling as your API key, minus the secrecy. It reaches exactly
two operations and is refused everywhere else, writes included:
GET /v1/account/brands/{brand_id}/storefront— one offering’s price and your brand’s readiness to sell it, in one call.GET /v1/account/brands/{brand_id}/readiness— the readiness report on its own.
Permitted browser origins
A browser will not let your page read a cross-origin API response unless the platform grants it. The grant is a list of exact origins on your brand’s member domain row, and you manage it yourself:- Exact origins only — a wildcard entry is refused. Scheme, host and port, nothing else. You cannot register a pattern that covers every preview URL Lovable hands you; register the specific origins you intend to use. The response returns the entries it refused, by name, so a typo tells you which one rather than presenting as “the browser read is broken”.
- It is a replace. Removing an origin from the array revokes it.
- Your brand needs a member domain registered first. Without one there is no row to hold the list. See login URLs and domains.
Two switches, both yours to ask for. The browser read needs a publishable key issued for your
brand and your page’s origin on the list above. Until both are in place every cross-origin call
is refused, whatever credential it carries. Build against the server pattern below either way, and
switch the read to the browser once you have both.Both operations declare either credential in the reference — your server key or your
publishable key — so the interactive playground can exercise them with either one. The origin
allowlist is what the playground cannot reproduce: test the cross-origin path from your own page.
The architecture to build against
Lovable projects commonly pair a generated frontend with a server function (its Supabase integration, or any backend you already run). Use that server side. It is not optional styling — it is where your API key has to live.Development setup
Everything on this site targets the test-mode environment with test-scoped keys and synthetic data
only — see environments.
Path A — checkout first, then the clinical questions
This is the path to build first, and the only one you can ship without a server of your own.-
Present the offering. Read the price on your server with your API key and pass it to the
page, or read it in the browser with your publishable key once your origins are registered.
Either way send
X-Brand-Id— it is required, and with a publishable key it must equal the brand that key was minted for, or the read answers404. Readprice_statusbefore you show an amount —pendingmeans the terms are not fully set up and no amount is shown rather than a guessed one — and read the readiness block before you render a buy control. -
Hand off with a link. Send the patient to
/checkouton your brand’s member origin, carrying the offering:The parameters select what to present, never what anything costs. A link cannot name a price, a discount or a fee. - The patient signs in at the door. Checkout requires sign-in; the hosted page collects an email and runs the platform’s own login. Your storefront does not create the account.
- The patient pays on the hosted page. Card details travel from the patient’s browser to the payment provider. Nothing payment-shaped passes through your code.
- The patient continues inside the portal — to the clinical questionnaire, then to their plan. There is no redirect back to your storefront, so your storefront is not where the outcome arrives. See below.
Path B — qualification questions before checkout
Available, with one constraint that shapes the whole design: every call on this path is a server call. Your page can render questions and collect answers, but it can never talk to these endpoints itself. Plan the backend before you plan the pages, and confirm your brand’s configuration first — see before you rely on any of this. The questionnaire is served one question at a time, by the server, which owns sequence, branching and completion. See how questions work for the model.node_id. It keys the answer to the node it was collected for. With it, a stale
replay of an already-passed node resyncs cleanly — 200, current node re-served — and a submit for
a node the session has not reached fails closed with a renderer.node_mismatch issue (422).
Without it, an answer can misbind to whatever node follows a server-side skip, on a path that
carries a patient’s health answers.
Then carry the journey_id to checkout:
Splitting the questionnaire around the payment
GET /v1/instrument/resolve takes an optional phase parameter that serves one tier of the
questionnaire on its own:
400. Whether your brand can serve a phase therefore depends on its questionnaire configuration —
confirm that with your onboarding contact before you design a page around the split.
Four constraints to design around:
- Server-side only. These operations need the API key, they carry patient answers, and they are not on the browser-safe list — a Lovable page cannot call them directly, with any credential. Proxy them through your server function and never return more to the page than the question to render.
- Resume is a header. Send the enrollment id back as
X-Journey-Idon a return visit rather than starting a second questionnaire. - Which questionnaire is served follows your brand’s configured therapies. A brand with more than one enabled therapy must name an offering on the entry link so the therapy is unambiguous.
- Proof-of-identity capture is never part of this questionnaire. Questions collecting a government ID are withheld here and are not counted toward completion; the patient verifies identity in the member portal’s own identity step. See the identity handoff.
Never infer payment from the browser
There is no redirect back to your storefront after payment, and the outcome never rides the patient’s browser to reach you. A page that concludes “paid” because the patient returned, or because a timer elapsed, is guessing. Read the authoritative status from your server instead:GET /v1/payments/{order_ref}/status— the order’s own payment state. Fed by confirmed events from the payment provider, never by guesswork. Branch on the wire values exactly as the reference declares them —created,payment_authorized,paid,payment_failed,partially_refunded,refunded,disputed,cancelled. Prose names like “authorized” or “failed” are not what the field carries, and a storefront matching on those never matches.POST /v1/webhooks/registrations— register an endpoint and the response returns your signing secret once. Deliveries carry an event type and identifiers only, so a delivery prompts a read rather than carrying detail.GET /v1/journeys/{journey_id}/status— the patient-visible enrollment status, for your own progress copy. It never carries payment detail, and it may answer with a typed no status yet instead of a status value.
States your page must have
A generated app will happily render a price ofundefined. Ask for these explicitly.
The reusable prompt
Paste this into Lovable and fill the bracketed values. It contains no credentials and instructs the generated app never to hold one.Development walkthrough checklist
- Confirm with your onboarding contact which entry path your brand is configured for.
- Store the API key in your server environment. Confirm it appears in no client bundle.
- Read the offering on your server and confirm
price_statusand the readiness block render. - Build the checkout link and confirm it opens the hosted page wearing your brand.
- Complete a test-mode purchase and confirm your server — not the browser — observes the paid state through the payment status read or a webhook delivery.
- Confirm the patient lands in the member portal afterwards.
- Only then, if you want the browser read: register your origins, ask for your publishable key, and move the price read client-side.
Common integration errors
Before you rely on any of this
Four things to confirm for your brand before you plan a launch around this page. None of them are code you write — they are configuration and credentials, and your onboarding contact owns them.- Which entry path your program is configured for. A questions-first program shows a plain “health questions come first” stop instead of a payment form when a link carries no enrollment.
- Whether your publishable key is issued and your browser origins are registered. Until both are in place, the two browser reads are unavailable to your page and the server pattern above is your only route to a price.
- Whether your brand’s questionnaire is configured in tiers, if you want the split entry with
phase. Without tiers there is no qualification tier to serve on its own. - What your brand’s post-payment questionnaire currently serves. The platform owns that content; confirm its current shape before you design copy or a progress indicator around it.