Patient-first longitudinal care for small clinics. Every visit produces a structured record the patient can understand, and every doctor sees that patient's history before they pick up a pen.
HealthJourney is a multi-tenant SaaS for 1–3 doctor clinics in India (about 20–50 patients per day). Patients complete intake over WhatsApp, doctors work in natural language, and the system turns that into structured prescriptions, diagnoses, lab results, and multilingual post-visit summaries.
The marketing homepage currently uses the KairoLife brand. The application, API, and this repository are HealthJourney.
This is research / early-stage software, not a certified medical device. Do not store real patient data until you have reviewed tenancy, encryption, and the privacy law that applies to you (for example DPDPA in India). See Disclaimer.
Before the visit. Reception sends an OTP-gated intake link. The patient answers a chat-style questionnaire. Gemini Flash writes a specialty-aware summary for the doctor.
During the visit. The doctor reviews intake and history, types a prescription in natural language (Tab Paracetamol 500mg BD x 5 days), records a diagnosis, orders investigations, and adds notes. AI parses drugs and tags diagnoses asynchronously so it never blocks the consult.
After the visit. The patient gets a WhatsApp (SMS fallback) summary in English, Hindi, or Telugu: what was found, what to take, warning signs, follow-up. The same record is on the patient portal. Staff can print a clinic-letterhead PDF.
Labs and time. Upload a PDF or image. AI extracts values, maps them to a test-code dictionary, and queues low-confidence fields for staff review. Returning patients get history, lab trend charts, duplicate-investigation flags, recurring-condition flags, and allergy conflicts.
| Area | Included in v1.0 |
|---|---|
| Tenancy | Shared-schema PostgreSQL with Row-Level Security on clinical tables |
| Auth | JWT for clinic staff (Doctor, Receptionist, Lab Tech, Clinic Admin); phone OTP for patients |
| Comms | Twilio WhatsApp primary, SMS fallback, per-clinic credentials |
| AI | Provider-agnostic AIService with Gemini 2.5 Flash |
| Jobs | Celery two-queue setup: ai_tasks (heavy) and notifications (OTP / WhatsApp) |
| Files | S3 presigned uploads, tenant-scoped keys |
| Portal | Intake chat, visit summaries, language toggle, lab results |
| Longitudinal | History panel, Recharts lab trends, rules-based clinical flags |
Not in v1: Docker Compose, appointment scheduling, ABHA linkage, HL7/FHIR, insurance, payments, ICD-10 entry, predictive diagnosis, native mobile apps.
flowchart LR
subgraph clients [Clients]
Staff[Clinic UI]
Patient[Patient portal]
end
subgraph app [Next.js]
Staff --> Next
Patient --> Next
end
Next -->|REST / JWT| API[FastAPI]
subgraph workers [Celery]
AIQ[ai_tasks]
NQ[notifications]
end
API --> PG[(PostgreSQL + RLS)]
API --> Redis[(Redis)]
API --> S3[(S3)]
API --> Celery
AIQ --> Gemini[Gemini Flash]
NQ --> Twilio[Twilio WhatsApp / SMS]
Tenant isolation is enforced in the database, not only in application WHERE clauses. FastAPI middleware sets app.current_tenant_id on the transaction; PostgreSQL RLS policies filter every clinical row.
| Layer | Choice |
|---|---|
| API | Python 3.11+, FastAPI, SQLAlchemy 2 async, Alembic, Pydantic v2 |
| Web | Next.js (App Router), TypeScript, Tailwind, Recharts |
| Data | PostgreSQL 16 (Supabase session pooler on port 5432 works; transaction pooler on 6543 does not, because RLS needs session state) |
| Jobs | Celery + Redis (Upstash rediss:// in production) |
| AI | Google Gemini 2.5 Flash |
| Files | AWS S3, region ap-south-1 by default |
| SMS / WhatsApp | Twilio |
backend/ FastAPI app, Celery workers, Alembic migrations, tests
app/api/v1/ HTTP routers
app/models/ SQLAlchemy models
app/services/ OTP, Twilio, S3, Gemini, lab normalizer, flags
app/worker/ Celery app + ai_tasks / notifications
alembic/versions/ 0001–0007
frontend/ Next.js App Router
app/(clinic)/ Staff: patients, visit queue, doctor screen
app/(portal)/ Patient: OTP, intake, summaries
app/(public)/ Clinic onboarding
.planning/ Design notes and v1.0 milestone archive
- Python 3.11+
- Node.js 20+
- PostgreSQL 16 (local or Supabase session pooler)
- Redis (local or Upstash)
- An S3 bucket and IAM keys
- A Google AI Studio API key
- Twilio (optional until you send real OTP / intake / summary messages)
There is no Docker Compose. Run processes natively. Production Dockerfiles for the API and worker live under backend/.
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
make install
cp .env.example .env # fill in real values
make migrate
make dev # http://localhost:8000 — docs at /docsWorkers (separate terminals, required for intake summaries, Rx parse, labs, WhatsApp):
make worker-ai
make worker-notifyKeep the two workers in different processes. AI backlog must not block OTP delivery.
cd frontend
cp .env.local.example .env.local
npm install
npm run dev # http://localhost:3000Open /onboarding to create a clinic, then use the clinic app and patient portal.
More detail, including Supabase URL pitfalls: backend/SETUP.md.
Copy backend/.env.example and frontend/.env.local.example. Never commit .env files.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
yes | postgresql+asyncpg://… — session pooler port 5432, not 6543 |
JWT_SECRET |
yes | python -c "import secrets; print(secrets.token_hex(32))" |
REDIS_URL |
yes | redis://localhost:6379 locally; rediss://…?ssl_cert_reqs=required on Upstash |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / S3_BUCKET_NAME |
yes | Lab and file uploads |
GEMINI_API_KEY |
yes | Intake, Rx parse, diagnosis tags, lab vision, summaries |
ADMIN_API_KEY |
production | Protects admin tenant-create endpoints |
TWILIO_* |
for messaging | Platform defaults; each clinic can store its own credentials |
FRONTEND_URL |
recommended | Used in intake links sent to patients |
NEXT_PUBLIC_API_URL |
frontend | Defaults to http://localhost:8000 |
cd backend
make testTests need a live Postgres matching DATABASE_URL. They create and delete their own tenants. Coverage includes JWT/RBAC, RLS isolation, OTP brute-force rules, lab name normalization, and AI task helpers.
- Tenant isolation is PostgreSQL RLS plus
FORCE ROW LEVEL SECURITY. - Staff tokens and patient tokens are different JWT
typevalues; patient routes reject staff tokens. - OTP codes expire, are single-use, and are rate-limited at the database.
- S3 keys are prefixed by tenant; uploads use presigned URLs.
- API docs (
/docs) are disabled whenENVIRONMENT=production.
If you run this with real patients: rotate JWT_SECRET and ADMIN_API_KEY, put TLS in front of the API, lock down S3, and complete your own DPDPA / HIPAA / DLT (SMS) work. This repo does not ship a compliance program.
Issues and pull requests are welcome.
- Fork and branch from
main. - Keep tenant isolation at the database layer. Do not replace RLS with ad-hoc
WHERE tenant_id = …as the only control. - Put slow AI work and Twilio sends on Celery, on the correct queue.
- Run
make testinbackend/andnpm run lintinfrontend/before you open a PR.
MIT.
HealthJourney is provided as-is for education and for teams who will complete their own clinical, legal, and operational review. It is not a substitute for professional medical judgment, not CDSCO-cleared, and not guaranteed to meet DPDPA, HIPAA, or similar obligations out of the box. Rules-based flags are advisories, not diagnoses.