Case Study - From Checkout to a Clinician’s Patient List, With No Human in Between
Replacing a manually-maintained spreadsheet of who paid, who’s onboarded, and who’s assigned to which clinician with a webhook-driven pipeline spanning billing, identity, and the EHR — including the gift-purchase edge case that could otherwise misassign a patient’s own record.
- Client
- Metabolic Health Platform
- Year
- Service
- 0→1 Platform Engineering
A single Stripe payment now reliably produces a working patient — a login, an EHR chart assigned to the right clinician, a CRM record — without a human touching any of it, and without a duplicate chart or a misrouted care team.
Overview
A single Stripe payment event needed to reliably produce a working patient: a login, an EHR chart assigned to the right clinician, and a CRM record — without a human touching any of it, and without creating duplicate patients, duplicate charts, or a patient with no way to log in.
The problem
The system distinguishes an assessment-only patient, routed to a dedicated assessment clinician, from a full membership patient, routed to the primary provider, based on which Stripe price ID was purchased — get that mapping wrong and a patient lands with the wrong clinical team from day one. The product also has a genuinely awkward edge case baked in: gift purchases. A purchaser and the actual patient are sometimes different people — a family member buying the program for someone else. Registration has to detect this from checkout's custom fields, distinguish it from the case where someone simply fills in their own name in those same fields, and route identity correctly in either direction.
Why it took a physician-engineer
The clinician-assignment logic is a one-line lookup in the code, but the judgment behind it is entirely clinical: which product tier warrants a dedicated assessment-only clinician versus the primary provider, and why that distinction matters for a company built around physician-led interpretation of results. A generalist engineer implementing "create a patient record on payment" defaults every patient to the same provider, because nothing in a payment event says otherwise. The patient-vs-purchaser distinction looks like a data-modeling exercise — which email goes in which field — but the actual requirement is downstream of a real clinical-operations question: whose consent, whose intake, and whose lab results does this record represent, when the person paying and the person being treated aren't the same.
Approach
Registration was deliberately split from the Stripe webhook rather than done entirely inside it. The webhook creates a CRM record and a pending registration entry immediately on payment; account creation itself — login, EHR patient, lab-ordering identity — happens when the patient completes a registration form, not synchronously inside the webhook handler. That trades a small amount of latency for a webhook handler that can't time out or partially fail on a slow downstream call, and for patients who set their own password from the first login instead of one mailed to them.
Each downstream system call was scoped by whether its failure should block the patient from getting an account at all.
Login and EHR account creation are blocking — without them, there's no patient. The lab-ordering identity, needed later for scheduling a blood draw, is explicitly non-blocking: if it fails, registration still succeeds, because a patient shouldn't be locked out of their own account by a third-party outage in a system they haven't reached yet.
Under the hood
Lambdas behind API Gateway orchestrate four external systems: Stripe for checkout session validation and line-item data, Cognito for identity with a permanent password set at registration rather than a temporary one, the EHR's GraphQL API for patient and provider assignment keyed on product tier, and the lab-ordering vendor for scheduling identity, keyed on the Cognito subject rather than PII, with duplicate-user responses treated as a recoverable success. The EHR patient-creation step checks for an existing record by email first — idempotent against the case where the webhook's earlier, best-effort attempt already created one — and updates rather than duplicates. The product-tier-to-patient-type mapping that drives clinician routing is kept in sync across the two Lambdas that need it via a shared constant, with the drift risk flagged explicitly in code. A CRM sync (contact, deal, line items, membership objects, keyed by customer ID, EHR ID, and email) closes the loop so the spreadsheet is no longer the source of truth for anything.
- 0→1 execution
- AWS serverless (Lambda, Cognito, DynamoDB, Secrets Manager)
- EHR integration
- Billing / CRM orchestration
- Idempotent distributed systems design
- Clinical operations translated into routing logic
Outcome
The sequence that used to mean someone manually creating a login, an EHR chart, and a CRM entry for every new patient — with the attendant risk of a step being missed — now completes without staff involvement for the standard path, with the harder edge cases (gift purchases, existing EHR profiles from an earlier partial attempt, third-party outages) handled as first-class cases rather than left to break in production.