Skip to main content
Every brand you operate has a brand configuration held on the platform, which you can read back with your own key at any time. Three parts of it are yours to write directly with your own API key: the display identity (the name, logo, and theme tokens the patient-facing surfaces wear), your legal document links (your terms of service and privacy policy, and your telehealth informed consent when you hold your consent documents, which patients are shown before they consent), and who holds your consent documents (consent_holder). The rest of the configuration (the questionnaire theme overlay, the redirect allowlist, and your therapy and commercial references) stays platform-managed; you confirm it through the same readback before you go live.

Your display identity

The display-identity block is what the platform-rendered surfaces — the patient portal, the hosted checkout door, and the sign-in screen — read to wear your brand instead of the default. It carries four things and nothing else: The brand you are writing is resolved from your key context, never from the request body. You present the brand in the X-Brand-Id header, and the brand_id in the path must match it. A brand your key does not own — foreign, unknown, or mismatched — answers 404, never 403: there is no way to probe whether another tenant’s brand exists.

Set it with your key

Send the display block to PUT /v1/account/brands/{brand_id}/config. The body carries any of display, legal_documents (see your terms and privacy links) and consent_holder (see who holds your consent documents). Those are the only fields read here.
The response is your stored configuration — the same shape the readback returns:
A few things this write guarantees:
  • The version is server-minted. You never send version; the platform mints a new monotone version on each write and returns it — so every write, including a repeat, advances the version. The stored write is durable before it takes effect, so a restart re-serves exactly what you last wrote.
  • The block is merged, not replaced wholesale. Your display block is merged onto the existing configuration, so a display write never disturbs the platform-managed redirect allowlist or theme.
  • A stale write is refused, not merged. If the saved configuration changed since you loaded it, the write is refused with 409 version-mismatch and nothing is written: read it again and retry. A 503 means the outcome is unknown: read the configuration back before retrying.
Patients are shown your terms of service and your privacy policy before they consent, and, when you hold your consent documents, your telehealth informed consent. Set the links with the same call, in a legal_documents block:
  • Which links you can send depends on who holds your consent documents. When you hold them (consent_holder client), legal_documents takes terms, privacy and telehealth (your telehealth informed consent), so your brand can become ready for patients on its own. Otherwise it takes terms and privacy only, and the telehealth informed consent link stays platform-managed. If the same call sends consent_holder, the value it sends decides. Any other key is refused with 422 and the issue brand.legal_documents.slot, and nothing is stored.
  • Absolute https only. Each link must be an absolute https URL to the document as your patients should read it. A relative path, an http: URL, an empty string or a non-string value is refused with 422 and the issue brand.legal_documents.url, and nothing is stored.
  • One link at a time is fine. A link you send replaces that link. A link you leave out keeps the one already stored, so you can update your privacy policy without resending your terms.
  • Send it with your display block or on its own. A body with only legal_documents leaves your display identity untouched, and the reverse is true too.
Each brand records who holds its patients’ consent documents and identity: the platform, or you. There is no default. Until it is chosen, your brand is not ready for patients. Set it with the same call, in consent_holder:
  • client: you hold them. Patients see your own registered links, so every document your consents link to must be registered by you.
  • The platform’s value (the other value of the consent_holder enum in the API reference): the platform holds them. The platform’s own documents apply by default, and your own legal links are not needed or used. Until the platform’s legal pages are published, patients see a “not available” notice in place of those links, and your readiness report reads readiness_unknown.
  • Any other value is refused with 422 and the issue brand.consent_holder.
Your welcome pack shows the choice (“Consent and identity held by the platform”).

Read it back

GET /v1/account/brands/{brand_id}/config returns the configuration as the platform holds it. It is key-scoped the same way: the path brand_id must match your X-Brand-Id, and any other brand answers 404. A brand with no configuration set yet also answers 404. The readback carries your theme, your enabled therapies and your redirect allowlist. It does not list your offerings: read those with GET /v1/account/brands/{brand_id}/offerings, which serves each offering’s name, sku_id, therapy, entry mode, checkout link, price and readiness.

Is your brand ready for patients?

Every intake resolve (GET /v1/instrument/resolve) carries a patient_readiness report for your brand. It is a report, not a wall: the questionnaire is served either way. Read it while you set up, and before your first patient arrives.
The same report rides your configuration readback, GET /v1/account/brands/{brand_id}/config, beside data, whenever the platform’s readiness check is available.
  • status is patient_ready or not_patient_ready. missing lists every required item still unset, and is empty exactly when status is patient_ready.
  • consent_holder is who holds your consent documents, as chosen, and not_chosen until someone chooses.
  • required_documents lists the documents your brand’s served consents link to, and only those: a document no served consent links to is never required. It is worked out from the questionnaires actually served for your brand, and it is empty when that cannot be told yet. The documents the platform can name today are listed in the API reference (for example terms, privacy and telehealth); the list can grow.
  • missing[].item is one of:
    • display_name: your display name is not set;
    • consent_holder: nobody has chosen who holds your consent documents;
    • legal_document: a required document is not set from the chosen holder’s source. document names which one, for example privacy. When you hold them, set your own link. When the platform holds them, the platform’s link must be set up; ask your onboarding contact;
    • readiness_unknown: the platform cannot tell yet. That happens when the documents your served questionnaires need can’t be worked out yet, when the platform holds your consent documents and its legal pages aren’t in hand yet, or when a page served for your brand links to a legal document the platform can’t fill in there (patients see a “not available” notice in its place). Every page served for your brand is checked, not only consents.
  • missing[].detail says in plain words what is missing and how to set it.
  • When the platform can’t tell, the answer is not_patient_ready with readiness_unknown, never patient_ready. Try again shortly, or ask your onboarding contact.

Where it renders

You do not call anything to make the display identity show up. The platform-rendered surfaces resolve it per request from your brand context and wear it automatically: the patient portal, the hosted checkout door, and the sign-in screen. A brand that has no display block does not error — those surfaces fall back to the platform’s default look, and the fallback is always explicit: the default is never silently presented as your brand’s own identity. How each part renders on the patient portal and the hosted checkout:
  • When changes show. The surfaces read your display identity on every page load, with no cache. A write is visible on the next page load.
  • Light and dark mode. The surfaces follow the patient’s device setting: a device set to dark shows the dark mode, otherwise the light mode. colors.light and colors.dark theme each mode.
  • Logos. logo.light shows in light mode and logo.dark in dark mode. With no logo.dark, the light logo shows in both modes, unchanged, so supply a dark asset if your light logo is dark on transparent. The logo is drawn by height, 24 or 40 CSS pixels depending on the header, and its width follows the image’s own proportions. Use an image a browser displays directly, such as SVG or PNG.
  • Root-relative logo paths. A path such as /brand/logo.svg loads from the address of the page showing it, which is your member portal address. Host the file there, or use an absolute https URL.
  • Radius. radius is a base value, not a single literal used everywhere. Larger and smaller corners are derived from it: small is the base minus 4px, medium the base minus 2px, large the base itself, and extra-large the base plus 4px.

The token reference

tokens is a partial theme set. You supply any subset of the slots below; anything you omit falls back to the platform default when the theme is assembled. Every leaf value is a string. An unknown group, mode, or slot is refused (see the wording wall). Your brand surface is colors, corner radius and your logo (set in logo, beside tokens). Typography is not supported: platform pages load no custom fonts, so a font family named in typography is not loaded. Leave typography out. Within colors.light and colors.dark, the slot names are the semantic color slots of the platform design-system brand-token contract. They are the exact vocabulary the theme stylesheet is generated from, so they can never drift from what the surfaces actually render. Grouped by the surface each one themes: Partial override. You may set as few as one slot in one mode. The write is merged onto your existing configuration and, at render time, onto the platform default — so a single-slot write re-themes only that slot and leaves the rest of your look intact. Unknown slots are refused. A slot name outside the vocabulary above (a typo, or a slot the platform does not define) fails the whole write with 422 and a slot-level issue — nothing is stored. A color value must be a non-empty string; an empty or non-string value is refused the same way.

The wording wall

The display block is theming only — name, logo, and tokens. It is not a copy channel and can never carry patient-facing wording. A copy key inside display is refused outright with 422:
The wall is structural, not a review step: the refusal is built into the validator, so there is no configuration in which display copy could slip through. Any display key outside display_name, logo, tokens, and support_email is refused the same way with brand.display.unknown_key. The client write reads only display, legal_documents and consent_holder. Anything else you place in the body — a theme, a redirect allowlist, a brand id, a charge setting — has no effect here: those stay platform-managed and cannot be set through your key.

The operator override

The same display block can also be written from the platform’s operator surface, as an override or as a starting look seeded when your brand is first created. That is a platform-side path, not yours; it writes the one shared configuration your readback returns, and it runs through the identical validator and the same wording wall. From your side there is one display identity, whichever path last wrote it — and the readback always shows you exactly what is stored.

The rest of your configuration

Everything else in your brand configuration is platform-managed — set through your onboarding contact and confirmed through the same readback. Each member has a fixed meaning:
  • theme — the presentation overlay for the intake questionnaire: theme tokens the renderer applies to each already-sequenced question. It is a separate token system from your display identity. The display tokens above dress the platform-rendered surfaces (portal, checkout, sign-in); theme dresses the questionnaire your own renderer draws. The two are disjoint — the same brand may set both, and neither overrides the other. See how intake serving works.
  • copy — brand copy slots for your non-clinical surfaces, keyed by slot. A copy slot changes wording on those surfaces only; it can never supply or override a question’s headline, which comes from the reviewed questionnaire. (This is separate from the display block, which carries no copy at all.)
  • enabled_therapies — references to the therapies your brand offers. References, never definitions: the therapy, its questionnaire, and its clinical rules live on the platform.
  • commercial — identifiers tying the brand to its storefront and pricing. References only.
  • redirect_allowlist — the absolute https URL prefixes a patient may be returned to after completion. Matching is exact origin plus path prefix; an entry that matches nothing is dropped, and an empty list refuses every redirect. Fail-closed by design — an open redirect is unrepresentable.

The clinical wall

The whole brand configuration — display identity included — is clinically inert by construction. Two checks run on every write, before anything is stored:
  1. A closed allowlist. A configuration, and the display block within it, may carry only their permitted keys. Any other key is rejected.
  2. A clinical-shape scan. The entire configuration is scanned at every depth for any key that could carry clinical meaning — facts, options, controls, rules, sequencing, visibility, and their relatives. One such key anywhere fails the whole write.
So a brand configuration can change how a surface looks, but it can never change what a patient is asked, the choices offered, what an answer records, or which jurisdiction’s requirements apply. The presentation grammar is themeable, never clinically alterable.