YourEra Engineering Docs
Canvas-first microservice architecture. If you're onboarding, read this page top to bottom.
- organization-service — Wave A1, commit
900ef59, 510 tests (2026-04-20). - @yourera/canvas-client — Wave A2, commit
bd48cfd, 1065 tests (2026-04-21). - patient-service — Wave A3, commit
4e7dd28, 908 tests (2026-04-21). - webhook-receiver — Wave A4, commit
31c7367, 841 tests (2026-04-21). - intake-gateway — Wave A5, commit
3a8ee9c, 702 tests (2026-04-21). Ships@yourera/cryptoshared package (21 tests).
What we are
YourEra is a prescription platform for GLP-1's and adjacent medications. Two product surfaces share one Canvas FHIR backend and one set of microservices:
- YourEra DTC — consumer product at
yourera.com. Patient completes intake, a licensed physician reviews in Canvas Medical, and a 503A compounding pharmacy ships the Rx. Modeled as FHIROrganization.kind='dtc'. - GuideGLP (B2B) — partners like Biologics, LCMC, Sleep Corner, and others. Each is a
FHIR Organization.kind='b2b'inside the same Canvas instance. Their patients havePatient.managingOrganizationpointing to their org. They get white-labeled patient portals, branded notifications, and tenant-scoped admin views — but share infrastructure.
Canvas is the medical-record database for everyone. Nothing clinical lives in a sidecar. Our services handle what Canvas doesn't model: identifiers, branding, custom domains, payments, shipping logistics, notification delivery, and operational sagas. Without SureScripts, we do our own Rx choreography through prescription-orchestrator. Read the Canvas-first principles doc for what can and cannot live in a sidecar.
How a prescription flows
- Intake. Patient reaches their org's intake host (e.g.,
intake.biologics.com).intake-gatewayresolves the organization from the hostname viaorganization-service, creates anintake_sessionsrow, and captures the email as a lead viacontact.captured(consumed bynotification-service). - Atomic Canvas write. On final submit,
intake-gatewayatomically writes Canvas resources via@yourera/canvas-client: Patient (withmanagingOrganization), Observation, Condition, AllergyIntolerance, QuestionnaireResponse, Consent, DocumentReference. In the same transaction,patient-servicewrites thepatient_directoryrow + INSERT-onlyintake_snapshots. - Provider review in Canvas. A Canvas Task is routed to eligible physicians (via
physician-registry+ the Canvasprescription_queueplugin, now org-aware). Provider reviews, approves, signs the Rx. Canvas emitsMedicationRequest.status = active. - Orchestrator saga.
webhook-receiveringests the Canvas webhook and emitscanvas.medication_request.activated.prescription-orchestratorcreates anrx_fill_sagasrow and runs the state machine: preflight → payment (or skipped if prepaid plan) → pharmacy → shipping → notify. - Pharmacy routing (live per fill). Orchestrator asks
pharmacy-routerfor a route: Galleria (GMP) if licensed in the patient's state, else Strive. Routing is re-evaluated on every fill — a patient who went to Strive for fill #1 automatically routes to GMP for fill #2 if GMP adds a license in between. Pharmacy dispenses; router writes CanvasMedicationDispenseon callback. - Fulfillment + notification.
shipping-servicegenerates a FedEx 2Day Express label (fetching patient address live from Canvas, never cached).notification-servicesends templated patient updates (shipped → out-for-delivery → delivered) using the org's verified sender identity. - Refill cycle.
refill-schedulermaintains a materialized view of upcoming refills fed by Canvas webhooks. On due date, it firesrefill.due; orchestrator creates a new saga and runs the full pipeline again. Prepaid bundled plans (quarterly, 6-month) skip the charge stage — money was collected upfront.
Mental model: Canvas owns clinical truth; sidecars hold operational state
The one thing to internalize: Canvas FHIR is the medical-record database. Every clinical fact — demographics, vitals, conditions, allergies, prescriptions, dispenses, messages — lives in Canvas. Our services are sidecars that hold only what Canvas doesn't model.
The decision rule when designing anything new: Would a HIPAA auditor expect this data in an EMR? Then Canvas owns it. Would a Stripe auditor, FedEx tracking system, or CMS-style operational dashboard expect this data? Then a sidecar owns it.
Concrete consequences:
- No caching Canvas data "just in case." If a read is hot, build a materialized view fed by webhooks (like
refill-scheduler). Not an ad-hoc cache. - No clinical fields in sidecars. Shipping-service doesn't store patient names. Notification-service doesn't store Rx details. Everything references
canvas_patient_idand fetches live. - Cross-service joins are forbidden. Every service has its own Postgres. Foreign keys don't cross service boundaries.
- Bypassing the owner is forbidden. Need patient data? Call
patient-service, which calls Canvas. The only service that can talk Canvas directly is the one whose domain it is. - No
platform = 'yourera' | 'guideglp'discriminator. Tenants are FHIROrganizations.Organization.kinddistinguishes DTC from B2B where branching is needed.
Gotchas before you touch anything
- Production vs staging names are counterintuitive.
yourera.com= prod (Bask → Canvas migration complete as of 2026-03-05).hisera.com= staging (Canvas was trialed on hisera first). - One Canvas, many orgs. YourEra DTC and every GuideGLP partner (Biologics, LCMC, Sleep Corner, etc.) share the same Canvas instance. Tenant isolation is enforced in our service layer via
organization_id, not by Canvas. - Single-org-for-life. A patient belongs to exactly one Organization for their entire lifetime with us.
organization_idis set once atpatient_directorycreation and never reassigned through normal flows. - Pharmacy Router is the single pharmacy entry point. Galleria (GMP) primary where licensed, Strive fallback elsewhere. Boothwyn was dropped. Routing is re-evaluated per fill — never pin a pharmacy to a patient.
canvas-pioneer-integration-serviceis retired. Its Canvas auth moved to@yourera/canvas-client(library). Canvas webhooks go towebhook-receiver. Stripe glue moved topayment-service. Patient records moved to Canvas. Don't add anything to this service.- HubSpot is gone. Contacts, campaigns, drip sequences, and opt-outs all live in
notification-servicenow. Never write HubSpot sync code. - PioneerRx auth is SHA512 over UTF-16-LE. Not UTF-8. See
pharmacy-router/src/adapters/pioneerrx.tsand the known-vector regression test. We've been bitten by this.
Environments & addresses
| Domain | Role | Notes |
|---|---|---|
yourera.com |
Production marketing + landing | Post-Bask-migration (live as of 2026-03-05) |
{slug}.portal.yourera.com + custom domains |
White-label patient portal per org | Default + org-owned custom hostnames (e.g., patients.sleepcorner.com) |
admin.hisera.com |
Admin Portal (superadmin + tenant-scoped) | Canvas RxQueue, saga ops console, campaigns, refunds |
intake.{org-domain} |
Intake sites, org-branded per B2B client | Resolves to organization_id at the portal edge |
hisera.com |
Staging (DTC + B2B, Canvas dev sandbox) | Yes, staging is named differently from prod. Historical. |
api.hisera.com |
Internal service HTTP endpoints | ALB routes per service (path-based) |
docs.hisera.com |
These docs |
Where to go next
Depending on what you're working on:
intake_sessions for resume + lead capture. Event emission for notification-service.Full service index
patient_directory + INSERT-only intake_snapshots. Two tables. Zero clinical data. Patient clinical truth lives in Canvas. 908 tests.
__Host- cookie, encrypted answersSealed via @yourera/crypto, pre-Clerk email OTP, multi-step submit saga with partial-failure recovery, server-authoritative disqualifiers. 702 tests.
refill.due; never writes Canvas directly. Replaces the old daily refill cron.
MedicationDispense on dispense callback. Boothwyn dropped.
physician_org_memberships in organization-service so physicians can serve multiple orgs.
intake_sessions.