Selected work
02

Tempo EMR

Building a serverless, multi-tenant practice management platform for pediatric therapy clinics from zero.

Tempo replaces paper records, spreadsheets, and disconnected legacy tools with one platform for patient intake, clinical documentation, scheduling, billing, and clinic operations. As a core engineer on a small founding team, I owned the configurable intake system across the stack, from the GraphQL schema and Python Lambda resolvers to the form renderer, along with role-aware workspaces and least-privilege navigation, and carried them from discovery through production rollout.

Healthtech · EMR Architecture · Data Isolation · Config-Driven Systems · Serverless AWS · Full-Stack

The problem

Clinics Run on Paper and Disconnected Tools

Pediatric therapy practices juggle intake paperwork, provider schedules, session documentation, and insurance billing across several disconnected systems. In practice that means paper intake packets, files handed around insecurely, and staff re-entering the same information in multiple places, with real risk to patient data along the way.

The challenge was to build one platform flexible enough to fit how different clinics actually work, without compromising on tenant isolation, access control, or performance as the data grows.

Architecture

A Serverless, Multi-Tenant Core

Tempo is an AWS-native serverless application. A React and TypeScript frontend talks to an AppSync GraphQL API, which runs pipeline resolvers and Python Lambdas over multi-tenant DynamoDB and S3 data stores, all deployed with CDK.

I co-designed the system topology with the founder and worked in every layer of it. I wrote resolvers and Lambdas for intake, RBAC, audit logging, and insurance integrations, and made sure the frontend enforced the same tenant isolation and least-privilege rules the backend does, so the two never drift apart.

React · TypeScript · Vite · AppSync (GraphQL) · Pipeline resolvers · Python Lambda · DynamoDB · S3 · AWS CDK · JWT + RBAC

Intake

A Form Configuration Engine Clinics Can Change Themselves

Clinics change their onboarding requirements often, and I did not want every change to require an engineer and a deployment. So I built the intake system around versioned, immutable form definitions stored in a multi-tenant DynamoDB table, keyed by organization and form so that version resolution and rollback stay cheap.

Form schemas are stored as structured attributes rather than opaque JSON strings, which keeps them queryable. On the frontend, a recursive React renderer walks the schema and handles conditional sections, field-level validation, and navigation in the family-facing portal, while the Python Lambda layer I wrote handles section saves, progress tracking, and final submission. A patient record is only created when a form is actually completed, so abandoned intakes never leave shell records behind. I owned this whole path, from the DynamoDB key design to the rendering code.

Intake configuration in a visual builder showing step, layout, and field views
Field editor with navigator, canvas preview, and field properties

Intake is config-driven. Clinics adjust fields and sections without an engineer, and the same schema drives the family-facing form.

Staff experience

Role-Aware Workspaces with Least-Privilege Access

One EMR instance serves very different people, from front-desk staff and insurance billers to therapists and practice owners. I built a role-aware application shell that reads each user's permissions and renders the dashboards, statistics, and actions appropriate to their role, backed by the RBAC resolvers and Lambdas I wrote for role and user management.

The navigation mirrors the backend's least-privilege rules rather than reimplementing them. A workspace context scopes clinical work to a single active patient, so therapists work within their authorized caseload without repeatedly searching and filtering, and without any path to records they should not see.

Tempo admin dashboard with operational highlights, clinic performance, billing snapshot, and recent activity
Patient directory with intake snapshot metrics and searchable patient cards
Patient workspace with cases, diagnosis, providers, and a patient summary sidebar
Delivery

Discovery in the Clinic, Acceptance Testing on a Tracker

Software for clinics has to match how clinics actually operate, so I did the discovery in person. I shadowed therapy sessions and interviewed therapists, office administrators, billing staff, caregivers, and clinic leadership, then translated what I saw into roughly 50 functional requirements mapped to the build timeline.

During rollout I ran weekly client status meetings and managed a change-request and acceptance tracker that grew to roughly 1,000 items, each tied to a screen or a clinic workflow. That loop let us triage feedback honestly, fix edge cases before they became habits, and ship on a steady weekly cycle.

Impact

From Paper to Production

We turned a 20-page paper intake packet into a configurable digital workflow that clinics adapt without engineering support.

~50functional requirements mapped in discovery
~1,000acceptance and change items worked through
~10,000patient records migrated in from the legacy EMR
Weeklyproduction release cadence during rollout
Data migration

Migrating Seven Years of Data Without an Export Path

Onboarding our first clinic meant migrating their history over from a legacy EMR whose vendor offered no supported export. I reverse-engineered the undocumented data model through the application itself, then wrote Python extraction and cleanup scripts that brought over more than seven years of patient files, clinical documentation, and billing entries.

I also built a HIPAA de-identification pipeline that stripped identifying details from roughly 10,000 records to prepare the data for planned predictive modeling. Along the way I uncovered serious security vulnerabilities in the legacy vendor's platform and compiled the findings into a formal disclosure report, which we submitted directly to the vendor.

Read the full migration and security case study
The team

How the Work Was Split

We were a small founding team, so the split was practical rather than strict. The founder led the core infrastructure, authentication, and external integrations. I owned the intake system end to end, including its schema, resolvers, and Python Lambdas, along with the clinical workspaces, the application frontend, and the discovery and acceptance-testing process. I also contributed backend code in the RBAC, audit, and insurance domains, and built the Python tooling we used for seeding and data migration.

Next case study

JstVerify

A developer observability platform