Skip to content

Fullscript Integration — Clinic Portal

Overview

Practitioners connect their own Fullscript account (per-practitioner OAuth). Treatment plans created or updated in Fullscript are pulled into the patient's master protocol in Onset Clinic. Manual/non-Fullscript supplements are preserved. The unified master protocol is delivered to the patient iOS app with configurable timing.

Default SOP (pull-first): Build supplement plans in Fullscript → link or import patients on the Patients page → pull the latest active treatment plan → send master protocol to the patient app.

Two separate systems (do not conflate)

System Who uses it Purpose
Fullscript (us-snd.fullscript.io sandbox) Practitioner Dispensary, catalog, treatment plans — native Fullscript UI
Fullscript developer portal (fullscript.dev) Integrator (you) OAuth app, webhooks, Create test account
Onset Clinic (staging / production) Practitioner Patient charts, master protocols, Patients → Connect / pull

Practitioners log into Fullscript separately from Onset. Onset Connect links an existing Fullscript login to the Onset practitioner profile via OAuth.

Sandbox setup order

  1. Developer portal — Create OAuth app, webhooks; use Create test account to mint a sandbox practitioner login.
  2. Fullscript sandbox — Sign in at https://us-snd.fullscript.io with that test practitioner. Build treatment plans here.
  3. Onset Clinic — Sign into staging clinic portal → Patients → Connect Fullscript (or Settings → Integrations → Connect). Authorize with the same Fullscript login.

After Connect succeeds, use the Patients page to import Fullscript patients, link charts, and pull protocols. Open in Fullscript opens the native Fullscript UI in a new tab.

Clinic workflow (Patients hub)

Step Where Action
1 Patients Connect Fullscript (banner when not connected)
2 Fullscript patients table Import FS-only patients, Link + pull email matches, Pull protocol for linked charts
3 Patient Directory Fullscript column shows link status; Pull from Fullscript per row
4 Patient profile Link chart, Pull latest protocol, or Create or link Fullscript chart (push patient identity only)
5 Patient profile Send master protocol to patient app (works for invited patients — queues until app install)

Bulk Sync all matched by email matches Fullscript patients to Onset charts by email and pulls each latest active plan.

Auto-sync: webhooks + hourly cron re-pull when Fullscript plans change (if auto-update enabled on the patient).

Push vs pull (phase 1)

Direction Supported Notes
Pull patient + protocol (FS → Onset) Yes Default SOP; product names resolved via catalog variant lookup
Push patient identity (Onset → FS) Yes Creates/links Fullscript chart without pulling a plan
Push protocol (Onset → FS) Phase 2 Requires Fullscript variant IDs; deferred

Availability flags

Variable Default Purpose
FULLSCRIPT_INTEGRATION_ENABLED true (set false on production) Kill-switch for all Fullscript routes
FULLSCRIPT_CUSTOMER_AVAILABLE false OAuth, sync, and patient UI
FULLSCRIPT_BILLING_ENABLED false Stripe Solo add-on line items at checkout
FULLSCRIPT_EMBED_UI_ENABLED unset (false) Opt-in in-portal iframe embed (not used in default workflow)

Production (until SOC 2 + Sub-BAA): FULLSCRIPT_INTEGRATION_ENABLED=false and FULLSCRIPT_CUSTOMER_AVAILABLE=false.

Staging sandbox POC: FULLSCRIPT_INTEGRATION_ENABLED=true, FULLSCRIPT_CUSTOMER_AVAILABLE=true, FULLSCRIPT_BILLING_ENABLED=false.

Environment variables

Variable Purpose
FULLSCRIPT_CLIENT_ID OAuth app client id from Fullscript API Dashboard
FULLSCRIPT_CLIENT_SECRET OAuth app secret
FULLSCRIPT_API_BASE Sandbox: https://api-us-snd.fullscript.io
FULLSCRIPT_OAUTH_AUTHORIZE_URL Default: https://us-snd.fullscript.com/oauth/authorize
FULLSCRIPT_OAUTH_TOKEN_URL Default: {FULLSCRIPT_API_BASE}/api/oauth/token
FULLSCRIPT_OAUTH_SCOPES Space-separated scopes (see dashboard)
FULLSCRIPT_WEBHOOK_CHALLENGE_TOKEN Secret challenge token from Fullscript Webhooks tab
FULLSCRIPT_WEBHOOK_SECRET Fullscript-signature HMAC secret key from Webhooks tab
FULLSCRIPT_TOKEN_ENCRYPTION_KEY Base64url 32-byte AES key (falls back to PATIENT_PII_ENCRYPTION_KEY)
FULLSCRIPT_EMBED_PUBLIC_KEY Optional; only if FULLSCRIPT_EMBED_UI_ENABLED=true
FULLSCRIPT_EMBED_ENV Embed environment: us-snd (sandbox), us (production)

Register OAuth redirect URI: {NEXT_PUBLIC_APP_URL}/api/integrations/fullscript/callback

Register webhook URL: {NEXT_PUBLIC_APP_URL}/api/webhooks/fullscript

Patient isolation invariant (HIPAA-critical)

A Fullscript treatment plan may only be merged into the Onset chart when all of the following are true:

  1. The chart belongs to the same practitioner as the connected Fullscript OAuth account.
  2. The chart's verified Onset email exactly matches the Fullscript patient email.
  3. The fullscript_patient_links row is scoped to that practitioner (practitioner_id + fullscript_patient_id unique).
  4. Manual pulls pass an explicit practitionerPatientId — stale Fullscript metadata never overrides a link row.

Sync is rejected (logged to fullscript_sync_log) when email mismatch, link mismatch, or practitioner mismatch is detected. Auto-sync (webhooks + cron) uses the same guards.

Manual SOP

  • Never link a Fullscript patient to an Onset chart unless emails match.
  • Use Import for Fullscript-only patients instead of linking the wrong chart.
  • If roster shows Incorrect link, use Remove link before pulling again.
  • After any suspected crossover incident, run the link audit script before re-syncing.

Incident response

  1. Stop manual pulls until audit completes.
  2. Run node --env-file=.env.local scripts/audit-fullscript-links.mjs (report only).
  3. Remediate mismatches: node --env-file=.env.local scripts/audit-fullscript-links.mjs --apply
  4. Re-pull protocols manually per patient from the patient profile.
  5. Review fullscript_sync_log for email_mismatch, link_mismatch, or practitioner_mismatch entries.

API routes (clinic)

Route Method Purpose
/api/integrations/fullscript/connect GET Start OAuth
/api/integrations/fullscript/roster GET Fullscript patient roster with link status + latest active plan
/api/integrations/fullscript/import-patient POST Create/link Onset chart + pull latest active plan
/api/integrations/fullscript/match-patients POST Bulk email match + pull
/api/clinic/patients/[id]/fullscript/link POST Manual link + optional pull
/api/clinic/patients/[id]/fullscript/sync-latest POST Pull latest active plan for linked chart
/api/clinic/patients/[id]/fullscript/push-patient POST Create/link Fullscript chart (no plan pull)
/api/clinic/patients/[id]/fullscript/ensure-patient POST Match by email + link + pull
/api/integrations/fullscript/dynamic-link POST Open Fullscript in new tab

Cron jobs

Route Schedule Purpose
POST /api/cron/protocol-deliveries Hourly (0 * * * *) Promote scheduled assignments → sent + notify
POST /api/cron/fullscript-sync Hourly (15 * * * *) Poll Fullscript Events API for missed webhooks

Sandbox E2E acceptance checklist

  1. Apply migration 20260624120000_fullscript_integration.sql to Supabase (if not already applied).
  2. Create a sandbox practitioner via Fullscript developer portal; sign in at https://us-snd.fullscript.io.
  3. Onset staging: Patients → Connect Fullscript — authorize with that Fullscript login.
  4. Patients page shows Fullscript roster; import or link a patient and pull protocol.
  5. Patient profile: no iframe embed; Pull latest protocol from Fullscript works.
  6. Pull protocol → confirm amber Protocol synced banner and assignment status (scheduled or sent).
  7. Send to patient app works for invited patients without the Onset app (queues assignment).
  8. Webhook or hourly cron → master protocol updates when plan changes in Fullscript.
  9. Hourly cron promotes scheduled assignments when delay elapses.
  10. Patient app E2E (Onset Staging TestFlight):
    • Portal: send master protocol with Apply immediately (status = sent). Scheduled/delayed assignments are invisible to the app until cron runs.
    • Copy connection code from patient profile (16 hex — not the clinic org code).
    • iOS: More → My Practitioner (or onboarding practitioner code) → Sign in with Apple → accept assignment sheet.
    • Verify supplement names on Protocol/Today; portal patient status → active.
    • Readiness check: node --env-file=.env.local scripts/verify-staging-patient-e2e.mjs [patient_id]

Production gates

  • SOC 2 Type 2 + Sub-BAA with Fullscript
  • Partner approval via api@fullscript.com
  • Production OAuth app + webhook secret
  • Set FULLSCRIPT_CUSTOMER_AVAILABLE=true, then FULLSCRIPT_BILLING_ENABLED=true when ready to sell Solo add-on

Partner evaluation brief (PDF attachment) and outreach email template:

Delivery modes

Mode Patient visibility Routine activation
delayed_push Hidden until delay elapses On accept after visible
immediate_preview Immediate Manual "Add to my routine"
immediate_apply Immediate On accept

Open in Fullscript

Dynamic link opens Fullscript in a new tab via POST /api/integrations/fullscript/dynamic-link. This is the supported way to manage supplement catalogs and plans in the native Fullscript UI.