Getting started
The survey helper is the SDK for embedding the Apex Telemed qualification survey (survey v2) on your own site. It composes the survey for the drugs you sell, walks the patient through it one step at a time, hides questions whose conditions are not met, checks qualification between steps, keeps a draft so a refresh resumes where the patient left off, and submits the response to Apex. You supply every element of the UI. The SDK ships no markup and no CSS.
Packages
Section titled “Packages”| Package | What it is | Install |
|---|---|---|
@apextelemed/survey-core |
Framework-agnostic engine and API client. Zero runtime dependencies. | npm install @apextelemed/survey-core |
@apextelemed/survey-react |
React hook (useSurveyV2Flow) over the core. |
npm install @apextelemed/survey-core @apextelemed/survey-react |
@apextelemed/survey-solid |
Solid hook (useSurveyV2Flow) over the core. |
npm install @apextelemed/survey-core @apextelemed/survey-solid |
All three packages are published to npm under the MIT license and ship ESM and CommonJS builds with TypeScript declarations. The React adapter needs react 18 or later. The Solid adapter needs solid-js 1.9 or later. The three packages are versioned together, so install matching versions.
The source, runnable examples, and release notes live in the public repository at github.com/thefortifiedgrp/apex-survey-helper.
Prerequisites
Section titled “Prerequisites”You need three things from Apex before the survey will load:
- A publishable key (
pk_followed by 32 hex characters). It is safe to ship in your client bundle. See Authentication. - Your site’s origin on your allow-list, for example
https://app.yourco.com. Browser requests from any other origin are rejected. - One or more drug IDs to compose the survey for, or a returning-member token for a refill flow.
Keys and allow-lists are separate per environment. Point apiBaseUrl at https://apextelemed.com/api for production or https://dev.apextelemed.com/api for the sandbox, and use the key issued for that environment. See Environments.
Install
Section titled “Install”# Reactnpm install @apextelemed/survey-core @apextelemed/survey-react
# Solidnpm install @apextelemed/survey-core @apextelemed/survey-solid
# No frameworknpm install @apextelemed/survey-coreRender the flow with React
Section titled “Render the flow with React”import { useSurveyV2Flow } from '@apextelemed/survey-react';
export function Intake() { const flow = useSurveyV2Flow({ apiBaseUrl: 'https://apextelemed.com/api', publishableKey: import.meta.env.VITE_APEX_PUBLISHABLE_KEY, drugIds: ['drug-A'], onComplete: (r) => console.log('response', r.responseId), });
switch (flow.outcome.kind) { case 'loading': return <p>Loading…</p>; case 'load_failed': return <p role="alert">Something went wrong: {flow.outcome.error}</p>; case 'submitting': return <p>Submitting…</p>; case 'disqualified': return <p>Unfortunately you're not eligible at this time.</p>; case 'complete': return <p>All set. We'll be in touch shortly.</p>; }
if (flow.outcome.kind === 'patient_info') { return ( <form onSubmit={(e) => { e.preventDefault(); void flow.submit(); }}> <input placeholder="First name" value={flow.patientInfo.firstName ?? ''} onChange={(e) => flow.setPatientInfo({ firstName: e.target.value })} /> <input placeholder="Last name" value={flow.patientInfo.lastName ?? ''} onChange={(e) => flow.setPatientInfo({ lastName: e.target.value })} /> <input placeholder="Email" type="email" value={flow.patientInfo.email ?? ''} onChange={(e) => flow.setPatientInfo({ email: e.target.value })} /> <input placeholder="Date of birth" type="date" value={flow.patientInfo.dob ?? ''} onChange={(e) => flow.setPatientInfo({ dob: e.target.value })} /> <input placeholder="State (2-letter)" value={flow.patientInfo.state ?? ''} onChange={(e) => flow.setPatientInfo({ state: e.target.value.toUpperCase() })} /> {flow.validationError && <p role="alert">{flow.validationError}</p>} <button type="button" onClick={flow.back}>Back</button> <button type="submit" disabled={flow.submitting}>Submit</button> </form> ); }
// questions phase return ( <div> <p>Step {flow.stepIndex + 1} of {flow.totalSteps}</p> <h2>{flow.currentStep?.stepTitle ?? flow.currentStep?.sectionTitle}</h2> {flow.visibleQuestions.map((q) => ( <label key={q.questionId}> <span>{q.text}</span> {q.options?.length ? ( <select value={(flow.answers[q.questionId] as string) ?? ''} onChange={(e) => flow.setAnswer(q.questionId, e.target.value)} > <option value="" disabled>Select…</option> {q.options.map((o) => ( <option key={o.value} value={o.value}>{o.text ?? o.value}</option> ))} </select> ) : ( <input value={(flow.answers[q.questionId] as string) ?? ''} onChange={(e) => flow.setAnswer(q.questionId, e.target.value)} /> )} </label> ))} {flow.validationError && <p role="alert">{flow.validationError}</p>} <button onClick={flow.back} disabled={flow.stepIndex === 0}>Back</button> <button onClick={flow.next} disabled={flow.submitting}>Next</button> </div> );}That is the whole integration. The hook constructs the engine once when the component mounts and tears it down on unmount. See the React adapter for every option and field.
Render the flow with Solid
Section titled “Render the flow with Solid”The Solid API has the same shape. Every read is an accessor, so call it as a function.
import { useSurveyV2Flow } from '@apextelemed/survey-solid';import { Switch, Match, For, Show } from 'solid-js';
export function Intake() { const flow = useSurveyV2Flow({ apiBaseUrl: 'https://apextelemed.com/api', publishableKey: import.meta.env.VITE_APEX_PUBLISHABLE_KEY, drugIds: ['drug-A'], });
return ( <Switch fallback={<p>Loading…</p>}> <Match when={flow.outcome().kind === 'load_failed'}> <p role="alert">Something went wrong.</p> </Match> <Match when={flow.outcome().kind === 'questions'}> <h2>{flow.currentStep()?.stepTitle ?? flow.currentStep()?.sectionTitle}</h2> <For each={flow.visibleQuestions()}> {(q) => ( <label> {q.text} <input value={(flow.answers()[q.questionId] as string) ?? ''} onInput={(e) => flow.setAnswer(q.questionId, e.currentTarget.value)} /> </label> )} </For> <Show when={flow.validationError()}> <p role="alert">{flow.validationError()}</p> </Show> <button onClick={flow.back} disabled={flow.stepIndex() === 0}>Back</button> <button onClick={flow.next} disabled={flow.submitting()}>Next</button> </Match> <Match when={flow.outcome().kind === 'patient_info'}> {/* patient-info form: flow.patientInfo(), flow.setPatientInfo(), flow.submit() */} </Match> <Match when={flow.outcome().kind === 'disqualified'}><p>Not eligible.</p></Match> <Match when={flow.outcome().kind === 'complete'}><p>Thanks!</p></Match> </Switch> );}See the Solid adapter for the full reference, including the patient-info form.
What the SDK does for you
Section titled “What the SDK does for you”- Composes the survey for your
drugIds(ortoken) as soon as the engine is created. - Walks the patient through one step at a time and skips steps whose questions are all hidden by earlier answers.
- Shows only the questions whose visibility conditions match the current answers.
- Runs a qualification check on every Next. If every requested drug is disqualified, the flow ends in
disqualified. - Collects the patient’s details on a final form, validates the required fields, and submits the response.
- Saves a draft of the answers and current step to
localStorage, so a refresh resumes the survey. Drafts expire after 24 hours and can be moved to your own server. - Emits lifecycle events (
survey:loaded,step:shown,qualification:checked,submit:succeeded, and more) for your analytics.
Next steps
Section titled “Next steps”- Authentication and modes: browser-direct versus proxy through your backend, the security model, refills and returning customers, draft persistence.
- React adapter and Solid adapter: every option, returned field, and event.
- Headless core: drive the engine from vanilla JS, Vue, Svelte, or web components, and plug in a server-backed draft store.
- Theming: what you control, optional branding data, accessibility notes.
- API contract: the endpoints the engine calls, the headers it sends, the exported types, and what a proxy must satisfy.
- Runnable examples: examples/react-vite and examples/solid-vite in the repository. Copy the
.env.example, set your publishable key and drug IDs, and run the Vite dev server. - Release notes: the changelogs for survey-core, survey-react, and survey-solid.