ThinkSys Inc · Team of 10
DentScribe
A healthcare SaaS that turns a dentist-patient conversation into a clinical note, saving 40-50 minutes per patient visit.

dentscribe.ai · the public marketing site · the portal sits behind a login
At a glance
- 19 portal surfaces
- 4 user roles
- 10 Redux slices
Scope
The web portal and admin applications — the surface where an AI-generated clinical note is reviewed and signed — and REST endpoints on the API behind them. I integrated the generation pipeline; I did not author it.
What I built
- Built the React and TypeScript portal for a multi-tenant clinical product: 19 surfaces spanning clinical review, billing and admin across four user roles.
- Built the review surface for AI-generated clinical notes — tabbed note, transcript, aftercare and coordinator sections, with editing where a clinician is expected to correct the draft.
- Built Node.js and TypeScript REST endpoints behind the portal — ownership-scoped TypeORM queries, validated DTOs, sanitized inputs and paginated list responses.
- Integrated the portal against a queue-backed generation pipeline, surfacing report state and triggering writeback into the practice's management software.
- Worked directly with the US client on requirements, demos and release cycles, coordinating API contracts across the backend and mobile teams.
Constraints and trade-offs
Access is derived in one guard from role, tenant and subscription state together, rather than checked where each screen needs it. Scattered checks are easier to write and are how a trial-expired account ends up reading another practice's data through a route nobody re-checked. One guard means every new surface inherits the rule instead of re-implementing it.
The review surface has no silent-accept path. A machine-drafted clinical note is signed by a clinician who is accountable for it, so the interface is built to make the draft easy to disagree with — sections separated, edits where correction is expected — rather than optimised for the fastest possible approval.
On the API, ownership is a condition inside the query rather than a check after it. Loading a row by id and then comparing its owner reads more naturally, and it is how one forgotten branch hands a practice someone else's records. Scoping the where clause to the signed-in user means the unauthorised row is never loaded at all, and "not found" and "not yours" become indistinguishable from outside — which, for patient data, is what they should be.
Context
The product listens to a dental appointment, drafts the clinical note the dentist would otherwise have written up afterwards, and files it back into the software the practice already runs on. The note is the visible part; getting it back into their existing system is the part that makes it worth paying for.
I worked on the portal — where a generated note is reviewed and signed — plus the administrative applications around it, and on supporting REST endpoints in the API behind them. The portal is the window onto the whole pipeline, so building against it meant knowing where every field came from.
Stack
- Worked in
- React
- TypeScript
- Redux Toolkit
- Material UI
- MUI X Data Grid
- Stripe
- Axios
- Node.js
- TypeORM
- PostgreSQL
- Integrated
- AWS S3
- OpenAI API