Skip to main content
RapidDev - Software Development Agency
App Featuresai-features21 min read

How to Add a Health Symptom Checker to Your App — Copy-Paste Prompts Included

An AI health symptom checker needs four pieces: a multi-turn conversational UI, a Claude claude-sonnet-4-5 Edge Function with a strict triage-only system prompt, a disclaimer gate users must accept before first use, and an emergency banner that appears when triage_level is 'emergency'. With Lovable you can ship a working build in 1–2 days. Costs run $25–60/month at 1,000 users — Claude API at roughly $0.03 per session plus Supabase Pro.

4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members

Feature spec

Advanced

Category

ai-features

Build with AI

1–2 days with Lovable

Custom build

2–3 weeks custom dev

Running cost

$0–25/mo up to 100 users; $25–60/mo at 1K users

Works on

WebMobile

Everything it takes to ship a Health Symptom Checker — parts, prompts, and real costs.

TL;DR

An AI health symptom checker needs four pieces: a multi-turn conversational UI, a Claude claude-sonnet-4-5 Edge Function with a strict triage-only system prompt, a disclaimer gate users must accept before first use, and an emergency banner that appears when triage_level is 'emergency'. With Lovable you can ship a working build in 1–2 days. Costs run $25–60/month at 1,000 users — Claude API at roughly $0.03 per session plus Supabase Pro.

What a Health Symptom Checker Actually Is

A health symptom checker is an AI-powered conversational tool that asks users structured questions about how they feel and returns triage guidance — not a diagnosis. The key product distinction is triage versus diagnosis: the feature must tell users whether to seek emergency care, schedule a doctor visit, or manage symptoms at home. It must never state what disease someone has. Claude claude-sonnet-4-5 handles the multi-turn conversation and classification well, but only if the system prompt enforces strict output rules. The real engineering challenges are: enforcing the disclaimer before first use at the database level (not just frontend state), persisting full conversation context across stateless Edge Function calls, and rendering an emergency banner immediately when triage_level is 'emergency' regardless of how the user reached that screen.

What users consider table stakes in 2026

  • Clear medical disclaimer modal before any conversation starts — 'I understand this is not medical advice' checkbox must be accepted and stored
  • Conversational multi-step input that follows up on user responses, not a single symptom form
  • Severity triage output in one of four plain-language buckets: self-care, schedule appointment, urgent care, emergency
  • No specific disease names stated as definitive diagnoses — language like 'could be consistent with' only
  • Session history saved per user so they can track symptoms over time and share summaries with their doctor
  • Prominent 'Call 911' or emergency CTA with a red banner triggered by any emergency-level triage result

Anatomy of the Feature

Seven components. The disclaimer gate and the structured JSON output from Claude are the two pieces that determine whether your build is legally defensible and functionally correct.

Layers:UIDataBackend

Symptom Input Chat

UI

A multi-turn conversational UI built with shadcn/ui Chat components (web) or a Flutter ListView of message bubbles. Each user message is appended to the local state and sent to the Triage Engine Edge Function along with the full conversation history from symptoms_sessions.

Note: Use a fixed-height scrollable container with auto-scroll to the latest message on new turns; this is a UX baseline users expect from any chat interface.

Triage Engine

Backend

A Supabase Edge Function calling Claude claude-sonnet-4-5 via the @anthropic-ai/sdk package. The system prompt instructs Claude to: (1) never state a definitive diagnosis, (2) classify every response into one of four triage levels (self-care, appointment, urgent, emergency), (3) escalate immediately to 'emergency' for chest pain, shortness of breath, stroke symptoms, or suicidal ideation, and (4) return structured JSON: {triage_level, summary, recommendations, follow_up_question}.

Note: Pass the full messages JSONB array from symptoms_sessions on every call — the Edge Function is stateless and needs the conversation history to give coherent multi-turn responses.

Body Part Selector

UI

An SVG body diagram (react-body-highlighter on web or a custom Flutter SVG widget) that lets users tap an affected region to pre-fill symptom context. This reduces free-text entry errors and gives the AI a more structured starting point than 'I feel bad'.

Note: The body diagram is optional but significantly reduces the number of clarifying questions Claude needs to ask, shortening the session and reducing API token consumption.

Symptom History Log

Data

Two Supabase tables: symptoms_sessions stores the full conversation per session with triage outcome; symptoms_entries stores individual symptom data points (what hurts, how long, severity). RLS ensures users only access their own rows.

Note: The messages JSONB column in symptoms_sessions should store the raw Claude conversation format — an array of {role: 'user'|'assistant', content: string} objects — for direct replay into the Anthropic SDK.

Disclaimer Gate

UI

A modal shown on first app open with the disclaimer text and a checkbox. The acceptance timestamp is stored in Supabase user_metadata (auth.users metadata column). The Triage Engine Edge Function checks this field before processing any message, so bypassing the modal by navigating directly to the chat URL is blocked at the API level.

Note: Do not rely on frontend state or localStorage alone for disclaimer enforcement — users can clear state or navigate directly to the chat route. The Edge Function must verify disclaimer_accepted_at before processing.

Emergency Alert Banner

UI

A full-width red banner rendered conditionally when the Claude response JSON contains triage_level: 'emergency'. Shows '911' prominently, poison control number (1-800-222-1222 in the US), and nearest emergency room locator link. The banner persists until the user manually dismisses it or starts a new session.

Note: Parse triage_level from the structured JSON response, not from prose text. If Claude returns unstructured output, the banner will never appear — use the structured output instructions in your system prompt.

PDF Export

Backend

A Supabase Edge Function using pdf-lib (Deno-compatible, open-source) to generate a session summary PDF: symptom timeline, triage outcome, AI recommendations. User downloads it to share with their doctor at an appointment.

Note: pdf-lib runs in Deno without any Node.js dependencies, making it a clean fit for Supabase Edge Functions.

The data model

Two tables cover the full session and individual symptom tracking, with RLS ensuring strict per-user data isolation. Run this in the Supabase SQL editor:

schema.sql
1create table public.symptoms_sessions (
2 id uuid primary key default gen_random_uuid(),
3 user_id uuid references auth.users(id) on delete cascade not null,
4 messages jsonb not null default '[]',
5 triage_level text check (triage_level in ('self-care', 'appointment', 'urgent', 'emergency')),
6 disclaimer_accepted_at timestamptz,
7 created_at timestamptz not null default now()
8);
9
10create table public.symptoms_entries (
11 id uuid primary key default gen_random_uuid(),
12 session_id uuid references public.symptoms_sessions(id) on delete cascade not null,
13 symptom text not null,
14 duration text,
15 severity int check (severity between 1 and 10),
16 created_at timestamptz not null default now()
17);
18
19alter table public.symptoms_sessions enable row level security;
20alter table public.symptoms_entries enable row level security;
21
22create policy "Users see own sessions"
23 on public.symptoms_sessions for select
24 using (auth.uid() = user_id);
25
26create policy "Users insert own sessions"
27 on public.symptoms_sessions for insert
28 with check (auth.uid() = user_id);
29
30create policy "Users update own sessions"
31 on public.symptoms_sessions for update
32 using (auth.uid() = user_id);
33
34create policy "Users see own entries"
35 on public.symptoms_entries for select
36 using (
37 exists (
38 select 1 from public.symptoms_sessions s
39 where s.id = session_id and s.user_id = auth.uid()
40 )
41 );
42
43create policy "Users insert own entries"
44 on public.symptoms_entries for insert
45 with check (
46 exists (
47 select 1 from public.symptoms_sessions s
48 where s.id = session_id and s.user_id = auth.uid()
49 )
50 );
51
52create index symptoms_sessions_user_idx
53 on public.symptoms_sessions (user_id, created_at desc);

Heads up: The messages JSONB column stores the full Anthropic conversation format array. The Edge Function reads this on every turn and appends it to the Claude API call so multi-turn context is preserved across stateless function invocations.

Build it — pick your path

Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.

Hand-built by developersFit for this feature:

Required for HIPAA-compliant US health apps, ICD-10 symptom taxonomy integration, or audit logging for clinical review. Full control over every compliance and data governance requirement.

Step by step

  1. 1Set up HIPAA-aligned infrastructure: enable Supabase encryption at rest, configure a Business Associate Agreement (BAA) with your cloud provider, enable audit logging for all PHI access
  2. 2Integrate a clinical symptom taxonomy: map free-text symptoms to ICD-10 codes using a structured pre-processing step before the Claude call; this allows structured clinical output alongside the triage recommendation
  3. 3Implement audit logging: every Claude API call, every triage result, and every disclaimer acceptance is written to an immutable audit_log table with service_role; no UPDATE or DELETE policy on audit rows
  4. 4Build multi-language support: detect user locale, pass it to Claude's system prompt, and route to language-appropriate emergency numbers (not 911 for non-US users)

Where this path bites

  • HIPAA compliance requires legal review and cannot be achieved through infrastructure alone — content policies, access controls, breach notification procedures, and training are all required
  • ICD-10 taxonomy integration adds significant complexity and requires a medical content specialist to validate the symptom-to-code mapping accuracy

Third-party services you'll need

The core stack is two services: Claude for triage reasoning and Supabase for session storage. PDF export uses a free open-source library:

ServiceWhat it doesFree tierPaid from
Anthropic Claude APIclaude-sonnet-4-5 powers the multi-turn triage conversation and structured JSON outputNo free tier; pay-as-you-go from first token$3 per 1M input tokens, $15 per 1M output tokens; typical session ~2K tokens = ~$0.03 (approx)
SupabaseDatabase for session history, RLS for per-user data isolation, Edge Functions for triage API, Storage for PDF exportsFree tier: 500MB DB, 2 projects, 500K Edge Function invocations/monthPro $25/mo for production (8GB DB, unlimited projects, 2M Edge invocations)
pdf-libOpen-source PDF generation for session summary download; runs in Deno Edge FunctionsFully open-source, zero costFree

Swipe the table sideways to see pricing.

What it costs to run

Drag through the tiers to see how your monthly bill scales with users — no surprises later.

Estimated monthly running cost

$3/mo

Supabase free tier covers storage and Edge Functions. Claude API at ~$0.03 per session, 100 sessions/month = ~$3. Well within free limits at this scale.

Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.

What breaks when AI tools build this

The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.

Claude names a specific disease, creating legal liability

Symptom: Without a carefully engineered system prompt, Claude's default behavior is to be helpful and specific — which in a health context means stating 'this sounds like appendicitis' or 'you may have a UTI'. This is legally dangerous for a non-clinical application and can mislead users into self-treating when they need care.

Fix: Add explicit system prompt instructions: 'Never state a definitive diagnosis. Use only language like could be consistent with, symptoms may suggest, or commonly associated with. Always recommend professional evaluation.' Test by asking about common conditions — if Claude names them directly, tighten the constraint language.

Users bypass the disclaimer gate by navigating directly to the chat URL

Symptom: If the disclaimer check only lives in React state or localStorage, any user who navigates directly to /chat or clears their browser data skips it entirely. This is a legal liability, not just a UX issue — in many jurisdictions, you must demonstrate that users were informed of the non-medical nature of the tool before using it.

Fix: Store disclaimer_accepted_at in Supabase user_metadata via auth.updateUser(). In the Triage Engine Edge Function, check this field before processing any message. If it's null or missing, return a 403 with a redirect to the disclaimer page. The check lives server-side and cannot be bypassed by frontend manipulation.

Multi-turn conversation loses context after 5+ messages

Symptom: Each call to a Supabase Edge Function starts with a clean execution context — there is no server-side conversation memory between calls. If the Edge Function only receives the current message rather than the full history, Claude answers each turn as if it's the first message, producing incoherent follow-up questions.

Fix: On every Edge Function call, load the full messages JSONB array from symptoms_sessions for the current session, append the new user message, pass the complete array as the messages parameter to the Anthropic SDK call, then save the updated array (with Claude's response appended) back to the row.

Emergency banner never appears even when user describes chest pain

Symptom: If Claude returns its triage level embedded in prose text ('Based on your symptoms, I would classify this as an emergency...') rather than in a structured JSON field, the frontend cannot reliably parse it. Pattern matching on the word 'emergency' in prose text is fragile and misses edge cases.

Fix: Instruct Claude via the system prompt to always return valid JSON with a triage_level field as the first key. Parse the JSON response in the Edge Function and include triage_level as a top-level field in the API response. The frontend renders the banner by checking response.triage_level === 'emergency' — no text parsing.

CORS error when calling Edge Function from FlutterFlow

Symptom: Supabase Edge Functions require explicit CORS headers for cross-origin requests. A FlutterFlow app making HTTP requests to a custom Edge Function will fail with a CORS error if the function doesn't return Access-Control-Allow-Origin and doesn't handle OPTIONS preflight requests.

Fix: In your Deno Edge Function, return these headers on every response: Access-Control-Allow-Origin: *, Access-Control-Allow-Headers: authorization, x-client-info, apikey, content-type. Add an explicit handler that returns 200 for OPTIONS method requests before any other logic.

Best practices

1

Always enforce the disclaimer at the Edge Function level, not just frontend state — store acceptance timestamp in Supabase user_metadata and verify it server-side

2

Return structured JSON from Claude with an explicit triage_level field; never parse triage severity from prose text

3

Pass the full conversation history (messages JSONB array) to Claude on every Edge Function call — the function is stateless and needs the full context for coherent multi-turn responses

4

Test the emergency escalation path explicitly: type 'I have chest pain, left arm pain, and shortness of breath' and verify the red 911 banner renders before building anything else

5

Include a clear 'This is not a substitute for professional medical advice' disclaimer in the UI at all times, not only in the gate modal

6

Log every triage session (anonymized) to review output quality — Claude's triage accuracy should be audited against known symptom patterns before you grow your user base

7

Add a session cooldown or rate limit (max 3 sessions per user per 24 hours) to prevent abuse and control Claude API costs

When You Need Custom Development

AI tools can ship a functional symptom checker with correct disclaimer enforcement and triage output. These requirements push past what a prompted build can reliably deliver:

  • App targets the US market and collects Protected Health Information — requires a HIPAA BAA with your cloud provider, encryption at rest and in transit, and access audit logging
  • Integration with a real clinical symptom database or ICD-10 taxonomy for structured, clinically validated triage output rather than AI-reasoned classification
  • Audit logging of all AI responses is required for regulatory compliance or clinician review — every message, triage result, and model version must be stored immutably
  • Multi-language support across 10+ locales with medically accurate translations and locale-specific emergency numbers (911 is US-only)

RapidDev builds these features for production

Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.

Get a fixed-price quote

Frequently asked questions

Is a symptom checker app legal to build?

Yes, with the right guardrails. Apps that provide triage guidance (should I see a doctor?) are generally legal as long as they do not diagnose specific conditions and include clear disclaimers. Apps that cross into providing clinical diagnosis or treatment recommendations may be regulated as medical devices in the US (FDA) or EU (MDR). Consult a health tech lawyer before launching if your app targets patients or healthcare providers.

Do I need a medical license to publish this?

Not for a general wellness or triage guidance app that clearly states it is not medical advice. Medical licenses are required for apps that provide clinical decision support to licensed practitioners, act as prescription tools, or make specific diagnostic claims. The safeguard is consistent disclaimer language, structured triage output (not diagnosis), and no specific disease naming.

How do I prevent Claude from giving a specific diagnosis?

Engineer the system prompt with explicit negative constraints: 'Never state a definitive diagnosis. Never say the user has [condition]. Use only language such as symptoms could be consistent with or may suggest. Always recommend professional evaluation.' Test it by describing symptoms of common conditions like appendicitis or UTI — if Claude names them directly, tighten the language. Include an output validator in your Edge Function that checks for diagnostic phrasing before returning to the client.

Can users save their symptom history?

Yes. The symptoms_sessions table stores the full conversation per session with triage outcome and timestamp. Users can view past sessions in a history screen grouped by date, see how their triage levels have changed over time, and download session summaries as PDF using the pdf-lib Edge Function for sharing with their doctor.

What happens when someone describes an emergency?

The Claude system prompt instructs it to return triage_level: 'emergency' for chest pain, stroke symptoms (face drooping, arm weakness, speech difficulty), severe difficulty breathing, and suicidal ideation. The Edge Function surfaces this field in the API response, and the frontend renders a full-screen red banner with '911' prominently displayed and poison control (1-800-222-1222 in the US). The banner persists until dismissed.

How much does the Claude API cost per session?

A typical symptom checker session of 5–8 message turns uses approximately 2,000 tokens total at $3 per 1M input tokens and $15 per 1M output tokens for claude-sonnet-4-5. That works out to roughly $0.03 per session. At 1,000 sessions per month you're spending about $30 on Claude API — affordable at that scale.

Does this work without an account (guest mode)?

It can, with limitations. Guest mode removes session history persistence (no Supabase auth to scope rows to) and makes disclaimer enforcement harder since you're relying on localStorage instead of user_metadata. If you want guest mode, store a session token in localStorage and scope the symptoms_sessions rows to that token, but accept that users who clear their browser lose all history and the disclaimer check is less reliable.

How do I make this HIPAA compliant?

HIPAA compliance requires a Business Associate Agreement (BAA) with every service provider that handles PHI — Supabase, your email provider, any third-party analytics. Supabase offers BAAs on their Team plan ($599/mo). Beyond infrastructure, you need access audit logs, encryption at rest and in transit, breach notification procedures, and staff training. HIPAA is a compliance program, not just a technical configuration. Engage a health tech lawyer and a compliance consultant before targeting US clinical users.

RapidDev

Need this feature production-ready?

RapidDev builds a health symptom checker into real apps — auth, database, payments — at $13K–$25K.

Book a free consultation

30-min call. No commitment.

Want this built for you?

We ship production apps at a fixed price — $13K–$25K, 6–10 weeks, source code yours. You've seen what it takes; we do it every week.

Get a fixed-price quote

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.