Multi-tenant clinic OS & AI medical copilot
Iatrova
A clinical operating system for Indian outpatient practices: patient records with DPDP consent, double-booking-proof appointments, AI prescription drafting and bilingual PDFs.
My role: Lead full-stack and systems engineer / architect
- Django 5
- PostgreSQL 16
- HTMX
- Celery
- Gemini

Overview
Iatrova is a multi-tenant SaaS for independent clinics and polyclinics in India. It covers the whole patient journey, from reception check-in and the live waiting queue to the doctor's consultation, AI-assisted prescription drafting, bilingual letterhead prescriptions and GST billing, with strict tenant isolation and consent handling under India's Digital Personal Data Protection (DPDP) Act. A REST API lets voice receptionists such as Jawably book appointments directly.
The challenge
Independent clinics in India often run on paper registers, phone calls and a separate billing tool. Receptionists double-book slots, doctors rewrite prescriptions by hand, and patient data sits in places that would not meet the consent and handling rules of the Digital Personal Data Protection Act.
Iatrova had to give each clinic one system for the whole visit, keep every clinic's data strictly separate on shared infrastructure, make AI genuinely useful to doctors without exposing patient identities, and produce documents patients and tax authorities accept, including Hindi prescriptions and GST invoices.
How it works
- 1Reception check-in
- 2Token queue
- 3Doctor encounter
- 4AI prescription draft
- 5Allergy check
- 6Hindi/English PDF + GST invoice
Architecture
Iatrova is a Django 5.1 application on Python 3.12 with PostgreSQL 16. Every clinical model inherits from a ClinicScopedModel whose manager requires a clinic filter, and a TenantMiddleware resolves the active clinic from the logged-in user, with a switcher for staff who belong to several clinics. Isolation is covered by dedicated tests. The interface uses server-rendered templates with HTMX and Alpine.js for live search, queue polling and autosave, styled with Tailwind CSS.
Appointments are generated from each doctor's working hours, leave and slot length, and booked inside atomic transactions with select_for_update() so concurrent receptionists cannot take the same slot. Check-in issues a daily token shown on a live queue. The doctor workstation stores vitals as structured JSON and autosaves every 10 seconds. The prescription copilot sends de-identified shorthand to Gemini or Claude, validates the result with Pydantic, checks it against recorded allergies and logs token usage per clinic.
WeasyPrint renders letterhead prescriptions in English and Hindi with Noto Sans Devanagari, and GST invoices with SAC 999312 and yearly numbering. Celery and Redis run scheduled jobs: marking no-shows at 23:30, follow-up reminders and daily owner summaries, with a notification log for WhatsApp Cloud API templates and email. A Django REST Framework API under /api/v1/ uses SimpleJWT and SHA-256 hashed, scoped API keys, documented with OpenAPI 3.0 and Swagger UI, and django-simple-history keeps an audit trail of visits, prescriptions and invoices.
What I built
Tenant isolation at the ORM layer
Custom ClinicScopedModel and ClinicScopedManager primitives enforce clinic filtering on every query, with a TenantMiddleware that resolves the active clinic and automated isolation tests.
Patients and DPDP consent
Clinic-unique sequential health IDs (IAT-<YEAR>-0001), allergies and chronic conditions, consent timestamps, and live HTMX search across names, phones and IDs.
No double-bookings, live queue
A slot engine built from doctors' working hours and leave, with select_for_update() row locks so simultaneous receptionists can never double-book. Check-ins issue daily tokens to a live queue.
Doctor workstation
A dual-pane encounter screen for complaints, examination and diagnoses, a structured vitals ribbon, 10-second background autosave and past-visit history.
AI prescription copilot
Turns shorthand like “amox 500 tds 5d, pcm sos, ors, review 3d” into structured, Pydantic-validated prescriptions, flags allergy conflicts, and never sends patient identifiers to the AI provider.
Bilingual PDFs and GST billing
WeasyPrint letterhead prescriptions in English and Hindi (Devanagari), and GST tax invoices with SAC codes, yearly numbering and Cash, UPI or card payments.
Engineering decisions
- Enforce tenant filtering in the ORM base classes rather than in each view, so a missed filter cannot leak another clinic's data.
- Lock appointment rows during booking so concurrency is handled by PostgreSQL, not by hoping receptionists don't click at the same time.
- Strip names and phone numbers from AI prompts and log token usage per clinic to enforce plan limits.
- Expose scoped, hashed API keys and an OpenAPI spec so voice agents and booking bots can integrate safely.
Results
- Zero cross-tenant access, enforced at the ORM layer and covered by isolation tests.
- Double-bookings prevented by database row locking.
- Bilingual English/Hindi prescriptions and GST-compliant invoices generated automatically.
- Ruff and mypy (django-stubs) clean, with 19 pytest unit and concurrency tests.
Stack
- Backend
- Python 3.12, Django 5.1, Celery, Redis, django-allauth, django-simple-history
- Frontend
- Django templates, Tailwind CSS, HTMX, Alpine.js, Django Unfold admin
- Data
- PostgreSQL 16, Redis 7
- AI
- Google Gemini 1.5 Flash / Claude SDK, Pydantic v2
- API & documents
- Django REST Framework, SimpleJWT, drf-spectacular (OpenAPI 3.0), WeasyPrint
- Quality & DevOps
- pytest-django, factory_boy, Ruff, mypy with django-stubs, GitHub Actions, Docker, Gunicorn
Related services
Need something like this?
I can build a version of this for your product, your data and your stack.
Start a project