Questionnaire Embed Integration Guide
Version: 0.2 (Stable)
Date: May 13, 2026
Package: @wearestealthhealth/questionnaire-embed
CDN (production): https://embed.stealth.health | CDN (beta): https://embed-beta.stealth.health
API base (production): https://app.stealth.health | API base (beta): https://beta.stealth.health
Status: Generally available
What this is. A drop-in iframe component that renders any Stealth Health questionnaire on your marketing site, partner portal, or in-app intake. Submissions are written directly to Stealth Health — your code never sees PHI. After submit, the patient is auto-redirected to the matching
/enroll/<slug>page (or shown a thank-you screen) and yourreferral.*webhooks start flowing exactly as documented in the Referral Tier guide.Who this is for. Any partner who wants the patient intake experience to live on their own domain rather than redirecting to
app.stealth.health. Works alongside any integration tier — referral, clinical, or prescriber-partner — because the embed is a delivery mechanism, not a separate API surface.
1. Overview
The questionnaire embed renders a Stealth Health intake form inside an iframe sourced from app.stealth.health (or beta.stealth.health) and embedded on a host page you control. Two integration paths are supported:
- Vanilla JS — paste a
<script>tag and a one-liner. No build tooling, no npm token, no React knowledge required. Best for Webflow, WordPress, Framer, or hand-written landing pages. - React / Next.js (npm) — install
@wearestealthhealth/questionnaire-embedfrom GitHub Packages and render<QuestionnaireEmbed />like any other component. Best for partners with a modern stack who want type definitions and tree-shaking.
Both paths render the same iframe with identical behaviour — the npm package is a thin React wrapper around the iframe URL plus the postMessage listener.
After the patient submits, the embed:
- Writes the response inside the iframe (so prescribers see it in real time in the doctor portal).
- Posts a
questionnaire:submittedmessage to the parent window with the newresponseId. - Redirects the iframe to the configured post-submit destination — usually the matching
/enroll/<slug>catalog page onapp.stealth.healthor a thank-you screen. - Triggers the
referral.enrolledwebhook to your registered endpoint (see Referral Tier § 7.3).
2. Architecture & PHI Boundaries
PHI handling. Every byte of patient data — answers, demographics, clinical history — flows from the patient's browser directly into the iframe and from the iframe to Stealth Health. Your parent page only ever sees:
responseId— a 20-character opaque document ID (no PHI).error.message— strings from our API like"reCAPTCHA verification failed"(no PHI).
This is the same PHI boundary as the Referral Tier: your stack is a referral source, not a HIPAA business associate handling PHI. Do not capture or persist anything from the iframe via DOM scraping or browser extensions — that would re-expose PHI to your stack and would require a full BAA.
3. Environments
You will integrate against beta first, validate end-to-end, then flip to production.
| Channel | npm dist-tag | API base URL (baseUrl option) | CDN base URL (<script src>) |
|---|---|---|---|
| Beta | beta | https://beta.stealth.health | https://embed-beta.stealth.health |
| Production | latest | https://app.stealth.health | https://embed.stealth.health |
Each environment is a fully isolated deployment with its own database and white-label configuration. Never send production traffic to beta URLs — the data lands in a separate environment and never reaches production prescribers.
Cutover. When you flip to production, change exactly two strings:
- <script src="https://embed-beta.stealth.health/questionnaire-embed/latest/umd.js"></script>
+ <script src="https://embed.stealth.health/questionnaire-embed/latest/umd.js"></script>
- baseUrl: "https://beta.stealth.health",
+ baseUrl: "https://app.stealth.health",
4. Option A — Vanilla JS (drop-in script)
Minimum viable integration — paste this anywhere on your page.
<!-- Anywhere in your page body -->
<div id="sh-questionnaire"></div>
<!-- 1. React runtime (any modern version; cached aggressively by unpkg) -->
<script crossorigin src="https://unpkg.com/react@19/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@19/umd/react-dom.production.min.js"></script>
<!-- 2. Stealth Health embed loader (UMD). Use the BETA CDN during integration. -->
<script src="https://embed-beta.stealth.health/questionnaire-embed/latest/umd.js"></script>
<script>
StealthHealthQuestionnaire.render('#sh-questionnaire', {
// From the catalog in §10 below.
questionnaireId: 'global-weight-loss-consultation',
// Beta during integration; flip to https://app.stealth.health for prod.
baseUrl: 'https://beta.stealth.health',
height: 720,
// Forwarded to our API. Use this for attribution + white-label routing.
queryParams: {
// Your registered white-label hostname (so emails/SMS use YOUR brand).
wl: 'partner.com',
utm_source: 'partner',
utm_medium: 'embed',
},
onSubmit: function (data) {
// data.responseId is a 20-char opaque doc ID. Stealth Health
// already has the submission — this callback is for your CRM /
// analytics only.
console.log('Stealth Health responseId:', data.responseId);
},
onError: function (err) {
console.error('Stealth Health embed error:', err);
},
});
</script>
That's it. The global StealthHealthQuestionnaire.render(selector, config) is the only public API exposed by the UMD bundle.
5. Option B — React / Next.js (npm)
Best for partners with a React stack — gives you TypeScript definitions, tree-shaking, and a component you can compose normally.
5.1 Configure npm to read from GitHub Packages
The package is published to GitHub Packages under the @wearestealthhealth scope. Create (or merge into) an .npmrc at your project root:
# .npmrc
@wearestealthhealth:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
Export the personal access token (PAT) we issued you with read:packages scope:
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In CI, set GITHUB_TOKEN (or NPM_AUTH_TOKEN) as a secret and reference it the same way. Do not commit the token.
5.2 Install
# During integration
npm install @wearestealthhealth/questionnaire-embed@beta
# Once you ship to production users
npm install @wearestealthhealth/questionnaire-embed@latest
5.3 Render
import { QuestionnaireEmbed } from "@wearestealthhealth/questionnaire-embed";
export default function ConsultationPage() {
return (
<QuestionnaireEmbed
questionnaireId="global-weight-loss-consultation"
baseUrl="https://beta.stealth.health"
height={720}
queryParams={{
wl: "partner.com",
utm_source: "partner",
utm_medium: "embed",
}}
onSubmit={({ responseId }) => {
// Forward to your own analytics / CRM. Stealth Health already has it.
console.log("submitted:", responseId);
}}
onError={(error) => {
console.error("questionnaire error:", error);
}}
/>
);
}
The component is iframe-only — it does not depend on any global state, theme provider, or context. Render as many instances as you want on the same page (each gets its own iframe and responseId).
5.4 Server-side rendering
The component reads window.addEventListener('message', ...) in a useEffect, so it is SSR-safe (Next.js App Router and Pages Router both supported). The iframe src is computed during render and is deterministic for a given prop set, so hydration does not flicker.
6. Configuration Reference
6.1 Props / options
The Vanilla JS StealthHealthQuestionnaire.render(selector, config) and the React <QuestionnaireEmbed {...props} /> accept the same shape.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
questionnaireId | string | yes | — | One of the IDs from the catalog in § 10. |
baseUrl | string | yes (recommended) | https://app.stealth.health | Must be HTTPS, or http://localhost / http://127.0.0.1 for local dev. Insecure URLs are rejected with a console error and the embed renders nothing. |
height | number | string | no | 700 | CSS pixel value (number) or CSS length (string, e.g. "100vh"). |
width | number | string | no | "100%" | Same conventions as height. The embed is responsive by default. |
queryParams | Record<string, string> | no | — | Forwarded to the iframe URL as ?key=value pairs. See § 6.2. |
recaptchaSiteKey | string | no | (Stealth Health default) | Override the reCAPTCHA v3 site key. Only set this if Stealth Health has provisioned a custom site key for your domain. |
onSubmit | (data: { responseId: string }) => void | no | — | Called when the iframe posts questionnaire:submitted. Use it to fire your own analytics/CRM events; do not call your own backend with PHI from this callback (the callback only contains responseId, which is fine). |
onError | (error: Error) => void | no | — | Called when the iframe posts questionnaire:error. The error message is a short string (e.g. "Submission failed"); the iframe surfaces full validation UX to the patient. |
className / style | — | no | — | Applied to the <iframe> element. Useful for fitting the embed into your design system. The iframe ships with border: none baked in via style. |
6.2 Forwarded query parameters
Anything you put in queryParams is appended to the iframe URL: https://app.stealth.health/q/<questionnaireId>?<your-params>&embed=1. Stealth Health honors the following keys explicitly; everything else is captured on the response document for your own attribution.
| Key | Honored by Stealth Health | Purpose |
|---|---|---|
wl | yes | White-label hostname. Drives per-request branding lookup so the post-submit emails/SMS to the patient use your brand. The value must exactly match a hostname registered in our white-label configuration (see § 8). |
utm_source / utm_medium / utm_campaign / utm_content / utm_term | yes | Persisted on the response doc and on any downstream referral, appointment, and transaction records for your reporting joins. |
partner_reference | yes | If present, used as the idempotency key on the resulting referral. Equivalent to passing partner_reference to POST /partner/referrals (see Referral Tier § 6.1). Recommended: pass your own customer ID here so your webhook handler can join on it without an extra API call. |
| Anything else | no (passed through) | Captured verbatim on the response doc under metadata and echoed back in webhook payloads under metadata.<key>. |
6.3 postMessage event protocol
The iframe communicates with the parent page via window.postMessage. The component verifies that the message origin includes the baseUrl hostname before invoking your callbacks — you do not need to filter origins yourself.
event.data.type | Direction | Payload | Triggered when |
|---|---|---|---|
questionnaire:submitted | iframe → parent | { type: "questionnaire:submitted", responseId: string } | Submission persisted. responseId is the new response document ID. |
questionnaire:error | iframe → parent | { type: "questionnaire:error", message?: string } | Submission failed (network error, reCAPTCHA failure, validation rejection from server). The patient sees an inline error UI inside the iframe; the parent callback fires so you can log it. |
If you want to listen for these events without using the npm package or UMD bundle (e.g. you embed the iframe yourself), the contract is:
window.addEventListener('message', (event) => {
// Always validate the origin before trusting the payload.
if (!event.origin.endsWith('.stealth.health')) return;
if (event.data?.type === 'questionnaire:submitted') {
// event.data.responseId
}
});
6.4 Cleanup / unmount
The Vanilla JS render() returns a handle for tear-down when the parent navigates away without a full page reload (single-page apps).
const handle = StealthHealthQuestionnaire.render('#sh-questionnaire', config);
// Later, e.g. on route change:
handle.unmount();
The React component cleans up its message listener on unmount automatically — no manual call required.
7. Content Security Policy
If your page ships a strict CSP, allowlist the following directives for whichever environment you're on. Beta and production use distinct hosts so you can keep them separate in your headers.
| Directive | Production | Beta |
|---|---|---|
script-src | https://embed.stealth.health https://unpkg.com | https://embed-beta.stealth.health https://unpkg.com |
connect-src | https://app.stealth.health https://*.googleapis.com | https://beta.stealth.health https://*.googleapis.com |
frame-src | https://app.stealth.health | https://beta.stealth.health |
img-src | https://*.stealth.health data: | https://*.stealth.health data: |
style-src | 'self' 'unsafe-inline' | same |
Why each entry:
script-srcallows the UMD loader and the React UMD runtime from unpkg.connect-srcallows the iframe to talk back to our API and to Google's reCAPTCHA verification endpoint.frame-srcallows the iframe itself.img-srcallows our brand assets and inlinedata:previews.style-src 'unsafe-inline'is required because the embed injects a<style>tag for layout. If your CSP forbids inline styles globally, setstyle-src 'self' https://*.stealth.healthand emailpartners@stealth.health— we can ship a hashed-style build on request.
8. White-label branding
Submission emails and SMS to the patient use your brand only if your public hostname is registered in our white-label configuration. This is a one-time onboarding step on our side. Email your Stealth Health contact:
- The public hostname(s) where the embed will appear (e.g.
consult.partner.com). - Your brand name, sender email, support email, logo URL (PNG/SVG, transparent background), and favicon.
- Whether navigation links should override our defaults.
We will register your branding against beta first, then production after you sign off. Verify by submitting a test response on beta and confirming the test inbox receives a message branded as you (not Stealth Health).
Important. Pass your registered hostname in
queryParams.wlon every render. The lookup happens per-request inside the iframe — ifwlis missing or doesn't match a registered host, branding silently falls back to Stealth Health.
Security note. Every registered white-label hostname is automatically considered a trusted SSO redirect target by our central identity provider at
app.stealth.health/login. Treat the white-label registration step with the same scrutiny you'd give a manual addition to an OAuth redirect allowlist.
9. Version pinning & release channels
/latest/umd.js follows the current channel's tip — that's usually what you want, because patch and minor releases ship via this URL with no integration work.
If you require a byte-for-byte reproducible build (e.g. to satisfy a change-management policy or a SOC 2 control), pin to an immutable URL:
<script src="https://embed.stealth.health/questionnaire-embed/v0.2.0/umd.js"></script>
Discover the currently deployed version on a channel:
curl -s https://embed.stealth.health/questionnaire-embed/version.json
# { "name": "@wearestealthhealth/questionnaire-embed", "version": "0.2.0", "channel": "latest", "builtAt": "…" }
Cache headers:
| URL pattern | Cache-Control |
|---|---|
/v<x.y.z>/umd.js (pinned) | public, max-age=31536000, immutable |
/latest/umd.js (rolling) | public, max-age=300, stale-while-revalidate=86400 |
Channel mapping:
mainbranch →npm @latestandembed.stealth.health/.../latest/(production).stagingbranch →npm @betaprereleases (e.g.0.2.1-beta.3) andembed-beta.stealth.health/.../latest/(beta).
If a regression slips out, roll back by pinning the previous immutable version:
<script src="https://embed.stealth.health/questionnaire-embed/v0.1.4/umd.js"></script>
…or for the React package:
npm install @wearestealthhealth/questionnaire-embed@0.1.4
We follow semantic versioning. Breaking changes bump the major version and are announced in the changelog ≥ 90 days in advance.
10. Questionnaire catalog
For each questionnaireId below, the patient submits the questionnaire, sees the thank-you screen (or our redirect into the matching enroll page), and is then offered the medications listed in the right-hand column. Every medication ID below has been verified live in our production medication catalog.
Excluded: the multi-step Mold/CIRS workup (
global-mold-cirs-consultation-step-{1,2,5}) is intentionally omitted — those questionnaires gate a sync visit and do not auto-route to a medication catalog.US only. The catalogs below cover US patients. Canadian SKUs exist for some categories but are routed through different enroll pages and are not listed here.
questionnaireId | Post-submit enroll page | US medications offered |
|---|---|---|
global-erectile-dysfunction-consultation | /enroll/ed | strive-tadalafil-5mg-flextab — Tadalafil 5 mg flextab (daily sublingual)strive-tadalafil-20mg-flextab — Tadalafil 20 mg flextab (on-demand, 36 h window) |
global-weight-loss-consultation † | /enroll/weight-loss | California only: strive-semaglutide-b12-glycine-0.5ml, strive-semaglutide-b12-glycine-1ml, strive-semaglutide-b12-glycine-2ml — Semaglutide / B12 / Glycine 5 mg/mLEvery other US state: wells-tirzepatide-glycine-2ml (2 mL), wells-tirzepatide-glycine-4.5ml (4.5 mL) — Tirzepatide + Glycine 16.6/7.5 mg/mL |
global-male-trt-consultation | /enroll/trt | wells-enclomiphene-citrate-12-5mg-per-capsule — Enclomiphene Citrate 12.5 mg capwells-perigo-testosterone-200mg-10ml-vial — Perigo brand T 200 mg/mL (10 mL)wells-testosterone-cypionate-bpi-labs-200mg-ml-5ml-vial — T-cypionate (BPI Labs) 200 mg/mL (5 mL) |
global-progesterone-consultation | /enroll/female-hrt | strive-progesterone-100mg-sr — Progesterone 100 mg SRstrive-progesterone-200mg-sr — Progesterone 200 mg SR |
global-thyroid-consultation | /enroll/thyroid | strive-liothyronine-t3-5mcg — Liothyronine (T3) 5 mcgstrive-armour-thyroid-15mg — Armour Thyroid 15 mgstrive-np-thyroid-15mg — NP Thyroid 15 mg |
global-scream-cream-consultation (female libido) | /enroll/hyposexuality-female | wells-testosterone-cypionate-in-grapes-20-mg-ml-low-d-5ml-vial — Low-dose T in grapeseed oil 20 mg/mL (5 mL)wells-testosterone-cypionate-in-grapes-50mg-ml-1ml-vial — T in grapeseed oil 50 mg/mL (1 mL)wells-testosterone-cypionate-in-grapes-50mg-ml-1-5ml-vial — T in grapeseed oil 50 mg/mL (1.5 mL) |
global-synapsyn-nad-consultation | /enroll/brain-health | wells-synapsin-0-2-mg-0-1ml-15-ml-bottle — Synapsin 0.2 mg / 0.1 mL (15 mL)wells-synapsin-b12-m-alpha-gpc-10-mg-0-2-mg-3-15-ml-bottle — Synapsin / B12 / Alpha-GPC stack (15 mL) |
global-bpc-157-peptide-consultation | /enroll/growth-hormone-support | wells-sermorelin-ship-cold-3-mg-ml-2ml-vial — Sermorelin 3 mg/mL (2 mL)wells-sermorelin-ship-cold-3-mg-ml-5ml-vial — Sermorelin 3 mg/mL (5 mL) |
global-b12-niagen-glutathione-consultation | /enroll/vitamin-nad | strive-nad-200mg-10ml, strive-nad-200mg-6ml — NAD+ 200 mg/mLwells-niagen-plus-kit-500mg — Niagen+ Kit 500 mgwells-glutathione-l-200mg-10ml, strive-glutathione-200mg-30ml — Glutathionewells-cyanocobalamin-b12-commercial-pr-1-000-mcg-ml-10ml-vial — B12 1,000 mcg/mL |
global-low-dose-naltrexone-ldn-consultation | /enroll/ldn | wells-naltrexone-ldn-1-5-mg-per-capsule — LDN 1.5 mg capwells-naltrexone-ldn-3-mg-per-capsule — LDN 3 mg capwells-naltrexone-ldn-4-5-mg-per-capsule — LDN 4.5 mg cap |
global-hair-growth-consultation | /enroll/hair-loss | wells-latanoprost-minoxidil-latanostim-0-06-2-2-30ml — LatanoSTIM-X 0.06 % / 2.2 % (30 mL)wells-latanoprost-minoxidil-latanostim-0-06-10-30ml — LatanoSTIM-X 0.06 % / 10 % (30 mL)wells-hydrocortisone-latanoprost-minox-1-0-05-6-5-0-0-30ml — HLMT — Vit E 5-in-1 (30 mL)wells-zinc-thymulin-ghk-cu-0-001-0-5-ml-25-ml-bottle — Zinc-Thymulin / GHK-Cu (25 mL) |
global-acne-consultation (Skin Care path) | /enroll/skin-care | wells-ghk-cu-hyaluronic-acid-minimum-b-0-055-0-5-30ml — GHK-Cu + Hyaluronic Acid (30 mL)wells-niacinamide-tretinoin-navra-gel-mild — Navra-Gel Mild (Niacinamide 5 % / Tretinoin 0.025 %)wells-niacinamide-tretinoin-navra-gel-moderate — Navra-Gel Moderate (0.05 %)wells-niacinamide-tretinoin-navra-gel-max — Navra-Gel Max (0.1 %) |
global-acne-consultation (Acne enroll path) ‡ | /enroll/acne | acne-chronic-us — Stealth Health Chronic Acne §acne-brightening-hydroquinone-us — Stealth Health Brightening §acne-scars-scarbase-us — Stealth Health Scars (ScarBase) §acne-multiprofen-cc-abx-us — Stealth Health Acne (Multiprofen-CC ABX) § |
global-pain-management-consultation | /enroll/pain-management (no-payment intake) | pain-management-pain-cream-us — Stealth Health Pain Cream §pain-management-pain-cream-plus-us — Stealth Health Pain Cream Plus § |
global-pain-management-consultation (paid path) | /enroll/pain-cream | Same SKUs as above. § |
global-metabolic-code-assessment | /enroll/metabolic-code (one-time / monthly / yearly) | metabolic-code-silver-us — Metabolic Code Silvermetabolic-code-gold-us — Metabolic Code Goldmetabolic-code-platinum-us — Metabolic Code Platinum |
global-regen-quantum-consultation | /enroll/regen-quantum | regen-quantum-black — Quantum Black (Deep Reset)regen-quantum-blue — Quantum Blue (Daily Optimization)regen-quantum-red — Quantum Red (Baseline Support) |
global-vcpr-multivisk-equine ¶ | /enroll/multivisk | ha-multivisk-us — Multivisk (HA injection) § |
Direct-enroll catalogs (no questionnaire required)
These enroll pages don't have a screening questionnaire — patients land directly on the catalog. Link to them straight from your site if you'd rather skip the embed for these categories.
| Enroll page | Medications |
|---|---|
/enroll/peptides | BPC-157 Inj. 2,000 mcg/mL · BPC-157 Capsules 500 mcg · Sermorelin Troche 500 mcg · Sermorelin Inj. 9 mg/vial · BPC-157 / TB-500 / GHK-Cu Inj. · Ipamorelin Inj. 2 mg/mL · NAD+ 200 mg/mL · GHK-Cu Cream · Thymosin-α1 (TA-1) Inj. · Bremelanotide (PT-141) Inj. · Elamipretide (SS-31) Inj. · Pentadecapeptide Arginate (PDA) Inj. |
/enroll/scream-cream | Now to WOW |
/enroll/multivisk | Multivisk |
Footnotes
† Weight-loss state split. The Weight Loss enroll page lists both groups, but checkout fulfilment is per-state: California ships the Strive semaglutide line, every other US state ships the Wells tirzepatide line. The split is enforced server-side by jurisdiction overrides on the weight-loss-program doc — the embed does not need to know which state the patient is in.
‡ global-acne-consultation is shared by two enroll pages. /enroll/skin-care exposes the Navra-Gel + GHK-Cu protocols; /enroll/acne exposes the direct-pay Hydroquinone / ScarBase / Multiprofen line. Pick the destination that matches your landing page's intent — the questionnaire ID is the same.
§ Limited US state coverage. The acne-*-us, pain-management-*-us, and ha-multivisk-us SKUs currently fulfil to AZ, CO, CT, DE, FL, GA, IL, IN, MD, MO, NJ, NY, NC, OH, PA, UT, WA, WY only. Every other SKU in the table fulfils to all US states (subdivisions: ['*']). Patients outside the supported states see an "out of network" message on the enroll page rather than a checkout button.
¶ Veterinary. global-vcpr-multivisk-equine is a veterinary client-patient relationship intake for Multivisk Equine HA. Skip this row if you do not serve veterinary customers.
11. Pre-production testing checklist
Run these against beta first; repeat against production on cutover day.
- The questionnaire renders inside the embed with no console errors.
- You can complete every required field and hit Submit.
- Either the page redirects to
/enroll/<slug>on the iframe (beta.stealth.healthorapp.stealth.health) or you see the thank-you screen — depending on the questionnaire. - Your
onSubmitcallback fires with aresponseIdmatching^[A-Za-z0-9]{20}$. - The patient email and SMS the test address receives are branded with your brand (logo, sender, support footer), not Stealth Health's. If they're branded as Stealth Health, the
wlparam doesn't match a seeded host — re-check thequeryParams.wlvalue and confirm the white-label doc is seeded for that exact hostname. - Stealth Health confirms the submission landed in our doctor portal under the correct questionnaire with the right answers.
- (Clinical-tier and prescriber-partner tiers only) Your webhook receiver at the registered URL receives the matching event (
referral.enrolled, plusappointment.intake_completedfor clinical tier) and verifies theX-Stealth-Signatureheader. See Referral Tier § 7.4 for the verification snippet.
If any check fails on beta, fix it before touching production — the production iframe and production webhook secret are not interchangeable with their beta counterparts.
12. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Iframe renders blank, console says Insecure baseUrl rejected | baseUrl is http:// (not localhost) | Use https://app.stealth.health (prod) or https://beta.stealth.health (beta). |
onSubmit never fires after a successful submission | Origin mismatch — your baseUrl doesn't match the iframe's event.origin | Make sure baseUrl exactly matches the host serving the iframe. Don't pass app.stealth.health while loading from beta.stealth.health. |
| reCAPTCHA error in the iframe | CSP blocks *.googleapis.com on connect-src | Add https://*.googleapis.com to connect-src (see § 7). |
| Patient receives a Stealth-Health-branded email instead of yours | queryParams.wl is missing or doesn't match a registered host | Pass wl: '<your-host>' in queryParams; confirm the host is registered in our white-label configuration (see § 8). |
| Webhook never arrives | Your webhook URL isn't set, or the receiver returned non-2xx for all 6 retries | Check delivery status via GET /partner/events?delivery_status=failed. To update the URL, contact your Stealth Health account manager. |
| Webhook receiver gets the same event multiple times | Working as intended — events are at-least-once | Dedupe on event.event_id (treat as the idempotency key). See Referral Tier § 7.5. |
npm install fails with 401 Unauthorized | GITHUB_TOKEN env var missing or PAT lacks read:packages scope | Re-issue a PAT with read:packages and re-export. Verify .npmrc is at the project root, not inside node_modules/. |
/latest/umd.js is serving an old version | Browser/CDN cache | Hard-refresh, or pin to an immutable /v<x.y.z>/ URL (§ 9). |
If you're still stuck, drop the network HAR for the iframe load and the parent page, plus the browser console output, into an email to partners@stealth.health.
13. Cross-references & support
- Referral Tier guide — webhook contract, signature verification, retry policy, and the dead-letter event replay endpoint that fires after the embed submission: Partner API Integration Guide.
- Clinical Tier guide — full PHI access, intake response API, partner-submitted prescriptions: Clinical Partner API Integration Guide.
- Prescriber-Partner guide — alternate flow where you collect intake in your own UI and submit it server-to-server: Prescriber-Partner Integration Guide.
- Package source & examples — the
questionnaire-embedpackage ships a working React example under itsexamples/directory. - Support channels:
- Technical bugs: email
partners@stealth.healthwith the details from § 12. - Branding rotations, new questionnaire IDs, white-label registration, webhook URL changes: contact your Stealth Health account manager (or
partners@stealth.health). - Security disclosures:
security@stealth.health— do not file a public issue.
- Technical bugs: email