AlfaCura
AI-matched physiotherapy platform for the Italian market — five-layer clinical triage bot, specialization-aware matching, full physio practice suite.
Client
AlfaCura
Industry
AI healthcare / Italian physiotherapy
Duration
Full build · 2025–2026
AlfaCura is an Italian healthcare AI triage platform that has processed 13,000+ patient conversations in production — 10,000+ in Italian and 3,000+ in English. Patients describe symptoms in natural language; the system red-flags emergencies, extracts clinical entities, and matches each patient to the best-fit physiotherapists. The core is a five-layer safety pipeline: deterministic regex emergency detection runs before any LLM inference, so the model never gets the chance to mishandle a red-flag case. Built solo as lead full-stack engineer on FastAPI, PostgreSQL, and the Anthropic Claude API (Haiku, chosen over Gemini for stronger Italian clinical comprehension), with a React web app and a React Native mobile app on a shared Supabase backend. PHI is encrypted at rest with signed consent and a full audit trail.
Overview
The product
A full-stack healthcare marketplace for the Italian physiotherapy market. Patients describe symptoms in natural Italian to an AI triage bot; the system red-flags emergencies, extracts clinical entities, and matches each patient to the three physios best suited to them. Physios get a dashboard that runs their whole practice — agenda, patients, finance, consent forms, 24/7 AI assistant.
| Field | Detail |
|---|---|
| Role | Lead full-stack engineer — architecture, AI pipeline, web + mobile, data model, deployment |
| Stack | React · React Native (Expo Router) · FastAPI · Supabase (Postgres + Auth + Storage + RLS) · Anthropic Claude · Stripe · Netlify |
| Scope | ~40k LOC across web, mobile, Python bots, and 32 Postgres migrations |
The problem
Italian patients looking for a physiotherapist today pick one from a directory, hope the specialization matches, and often discover mid-visit that the physio doesn't actually treat their condition. Physios on the other side waste calendar slots on non-targeted patients, field the same WhatsApp questions all day, and juggle bookings, invoices, consent forms and reviews across five disconnected tools.
The brief was to build a single product that:
- Collects a patient's symptoms in conversational Italian, safely.
- Matches them to the three best-fit physios (specialization, zone, availability — not just distance).
- Gives the physio a management suite that replaces the five tools.
- Stays compliant with Italian healthcare data rules — PHI encrypted at rest, signed consent, audit trail.
What I built
1. The triage bot — 5-layer Italian clinical pipeline
triage_bot.py (~2,800 LOC, Python/FastAPI) replaces a naive "send the message to the LLM" approach with a deterministic pipeline:
- Layer 1 — Safety detector. Hardcoded regex for cardiac, stroke, self-harm and other emergency phrasings in Italian. Catches the cases an LLM must never fumble.
- Layer 2 — Intent router. Rules-first, LLM fallback. Distinguishes
data_response,question,crisis_signal,off_topicso the dialog manager knows what to do next. - Layer 3 — Entity extraction. Italian-language NER for age, temperature, pain scale, duration, body part, gender, contextual preferences.
- Layer 4 — Dialog manager. State machine with 9 red-flag investigation protocols. Stateful across turns via a
dialog_statecolumn persisted in Postgres — the conversation survives refreshes. - Layer 5 — LLM response generator. Anthropic Claude with a tight system prompt, only invoked once the previous layers have structured the context.
The pipeline was originally written as Deno Edge Functions, then migrated to Python/FastAPI on subject-matter advice that Python would give better clinical reasoning. Frontend has a single env-var switch (VITE_PYTHON_BOT_URL) and backward-compatible endpoints at /functions/v1/claude-triage — so either backend can serve at any time and rollback is instant.
Why Claude (Haiku) over Gemini
The model was chosen, not defaulted. On Italian clinical phrasing — where a misread symptom is a safety issue, not a UX bug — Claude Haiku gave noticeably stronger comprehension of colloquial Italian and clinical context than Gemini in side-by-side testing, at a latency and cost that suited a per-conversation triage flow. Evaluating rather than reaching for the default is the difference between a demo and a production clinical system.
2. The matching algorithm
After triage extracts the clinical picture, an availability scanner (availability_scanner.py) walks every professional's configured working_hours, subtracts booked appointments and blocked_slots, and ranks the intersection of:
- Clinical specialization — filter by pathology tags, not just a free-text specialty field.
- Service area — only physios whose declared zones cover the patient's address.
- Real-time availability — slots calendar-synced in real time.
- Review quality — only feedback from verified visits (review rows tied to completed appointment IDs).
Output: the top 3 physios with a match percentage the patient can understand, plus an AI-generated explanation of why each is the right fit.
3. The professional suite (web + mobile)
26 screens on mobile alone, fully mirrored on the web dashboard:
| Area | What it does |
|---|---|
| Agenda | Google Calendar-style day/week/month views. Respects configured working hours, draws blocked slots as gray overlays, colors appointments by status. |
| Pazienti | Patient CRM with filters (new / with appointments / archived), per-patient medical record store. |
| Finanza | Invoices with auto-generated PDFs, revenue analytics. |
| Abbonamenti | Stripe subscriptions with hosted checkout + customer portal. |
| Consensi | Signed consent documents, legally traceable via audit trail. |
| Recensioni | Only from patients who actually completed a visit. |
| Staff | Multi-user clinics with role-based access. |
| Chatbot AI | A dedicated AI assistant per physio that answers patient questions 24/7 on their behalf — configurable tone, specializations, answer bounds. |
| Zone di servizio | Geographic coverage definitions feeding the match algorithm. |
4. The patient suite
A medical archive that does more than store PDFs:
- Uploads (referti, analisi, documents) auto-routed through AI and filed into ordered sections.
- History travels with the patient across physios.
- Appointments, consent forms, messages and the AI-triage chat all live in one timeline.
5. Security & compliance
Italian healthcare data is PHI. Migration 032 (phi_encryption.sql) + 033 (auto_encrypt_triggers.sql) + 034 (drop_plaintext_columns.sql) migrated the entire patient dataset to encrypted-at-rest PHI columns via Postgres triggers, then dropped the plaintext columns in the same deploy. Audit trail (audit_trail.sql) logs every read/write on a patient record. RLS policies enforce that a physio can only see their own patients.
6. Mobile app (React Native + Expo Router)
The mobile experience isn't a thin web wrapper — it's a first-class React Native app with:
- Auth-first flow with a guest mode: unauthenticated users can use triage and search; gated areas render an inline "Accedi / Registrati" card instead of a hostile redirect.
- Contextual drawer: the left drawer adapts to the current section — on Agenda it shows Oggi/Settimana/Mese toggles + hour config; on Pazienti it shows filters — while the bottom tab bar handles top-level navigation.
- Agenda config: auto-creates the professional record on first use (fixing a "profile not configured" dead end), persists weekly working hours to a JSONB column, manages blocked slots against the same table the matcher reads from.
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ Web (React + Vite) Mobile (RN + Expo Router) │
│ · LandingPage · Professional suite (26 screens) │
│ · Professional dashboard · Patient suite │
│ · Patient dashboard · Guest mode │
│ · Triage chatbot · Triage chatbot │
└──────────────────────┬──────────────────────────┬───────────────────┘
│ │
▼ ▼
┌───────────────────────────────────┐ ┌─────────────────────────────┐
│ Python Bot Service (FastAPI) │ │ Supabase Edge Functions │
│ · triage_bot.py (5-layer) │ │ · Stripe checkout/webhook │
│ · professional_chat_bot.py │ │ · Invoice PDF generator │
│ · availability_scanner.py │ │ · Notifications/reminders │
│ · Anthropic Claude client │ │ · Consent signing │
└──────────────────┬────────────────┘ └──────────────┬──────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ Supabase (Postgres) │
│ 32 migrations · RLS on every table · PHI-encrypted columns │
│ · professionals · patients · appointments · messages │
│ · services · blocked_slots · invoices · consent_documents │
│ · reviews · notifications · audit_trail · patient_chats │
└─────────────────────────────────────────────────────────────────────┘Hard problems I solved
Prompt-injection defense inside a clinical chatbot
The bot reads patient free text, which is hostile input by definition. The pipeline treats the LLM as the least-trusted component: safety regex gates run before any LLM call, and the dialog manager — not the LLM — decides when to escalate, ask for more info, or hand off to a human.
Stateful conversations on a stateless backend
Added a dialog_state column to patient_chats; every turn round-trips the serialized state. Refreshing the page, switching devices, or coming back three days later resumes the exact conversation position.
Auth-first vs. public-first
The product needs both: a public chatbot that anonymous visitors can try, and a locked-down patient/physio app. Landed on auth-first with guest mode — guests pass through the route guard, gated screens render an inline sign-in prompt rather than a hostile redirect.
Results
13,000+
Conversations processed in production
10,000+
Italian conversations
3,000+
English conversations
5-layer
Safety pipeline before any LLM call
32
Postgres migrations
~40k
LOC across web, mobile & Python bots
- 13,000+ production conversations across two languages, every one gated by deterministic safety rules before the model was invoked.
- One product replaced five tools for the physio (directory, calendar, CRM, invoicing, consent software).
- Matching quality up, filler visits down. Physios see only patients targeted to their specializations.
- 24/7 responsiveness without hiring a receptionist — each physio's chatbot answers common patient questions on their behalf.
- Compliant by construction — PHI encrypted at rest, signed consent, audit trail, RLS.
Lessons
- Don't let the LLM near untrusted input unsupervised. Layering deterministic guards in front turns "AI chatbot" from a liability into a feature.
- Migrations are a product feature. The PHI-encryption rollout was three stitched migrations with triggers — deployed with zero plaintext ever reaching the final schema.
- Two clients, one contract. Keeping the web and mobile apps on the same Supabase tables (not separate microservices) made feature parity free and bug-fixes a single PR.
Built by Ojas Gangwal at Khaas. Want something similar?
Book a 30-min call →Need something like this?
Book a 30-min call. We'll scope your project together — no obligation.