Synthetic Patient Data: How to Prototype and Demo Health Apps Without Touching PHI
Every compliance article about healthcare software lands on the same instruction: use synthetic patient data in your dev environment. Then it stops. So you're seeding a prototyping environment against a deadline, with an empty database and no sense of which option leaves you defensible if anyone asks later.
That question already has a public test case. A security researcher found 886,521,320 medical records sitting open on the internet, and the company's defense was that all of them were fake. Whether a defense like that holds comes down to how the records were made.
Provenance decides the quieter problems too. Ask GPT-4-mini for 250 patients and not one of them comes back under 25, so your pediatric edge cases ship untested and you meet them in production.
How a dataset was made sets what you're legally allowed to do with it, which generator is worth setting up, and whether the records break your app the way real patients will. The word on the label answers none of that, and it won't warn you about the 2 places teams still get this wrong after doing everything else right.
How do you get realistic patient data for a healthcare app without using PHI?
Generate it from population statistics rather than real records, which puts the output outside HIPAA. Faker or Mockaroo covers a demo, Synthea produces loadable FHIR at volume, and an LLM writes clinical narrative. Then build a second cohort designed to break the app.
Key Takeaways:
- Copying production records into staging is an impermissible use HIPAA presumes is a breach. Provability is what synthetic data buys you.
- "Anonymized" has no HIPAA definition, so ask which of 4 provenance classes a dataset falls into. Population-statistics output sits outside HIPAA; output from your own PHI is contested.
- Most teams end up running 2 generators. A pitch demo and a FHIR conformance test need opposite things from the same records.
- Generated data is cleaner and narrower than production. Synthea fills required fields and rarely optional ones, and an LLM asked for 250 patients returns none under 25.
Real patient data in staging is a breach you have to disprove
Somewhere between the first working feature and the first real user, somebody runs a dump from production into staging. It happens quietly, usually because the alternative is inventing 500 patients by hand.
Real data goes into staging because it's the fastest thing to do
Production data is the only data guaranteed to be shaped like production. Copy it into a staging environment and the app either works against it or it doesn't, and you know which by the end of the afternoon. Every other route costs setup time before it answers anything.
The reasoning holds right up until someone asks about it:
- it's internal
- it's temporary
- nobody outside the company can see it
The rule counts internal use, and it presumes you're wrong
The Breach Notification Rule reaches acquisition, access, use, or disclosure of unsecured protected health information (PHI) in a way the Privacy Rule doesn't permit. An impermissible use is presumed a breach unless you document a risk assessment showing low probability of compromise.
Use is in that list, which is why nobody outside the company has to see anything for this to count. The presumption runs against you, so showing that nothing happened is your job. That documentation has to exist before anyone asks for it.
2 teams seeded test environments, and both ended up arguing about what was in them
The American College of Cardiology was rebuilding its National Cardiovascular Data Registry and handed software development vendors a test environment of 250 tables filled with fabricated data. One of the 250 held real patients by mistake: names, dates of birth, Social Security numbers, internal patient IDs. Roughly 1,400 institutions were affected.
The copy happened in 2009 or 2010. It went unnoticed until December 2015, and the notices went out in February 2016. 4 development vendors could reach that table, and the ACC said only the one with a business reason to open it had. That's a team that used fabricated data 249 times out of 250.
The newer case runs in the opposite direction. In August 2025 researcher Jeremiah Fowler found 886,521,320 records across 68.53 GB with no password on them. Deep 6 AI said the database was an ephemeral test environment, isolated from production, holding dummy data from MIT's MIMIC set and no real patient records.
Fowler said he had validated that physicians' names in it appeared to belong to real individuals. Nothing appears on the OCR breach portal and no regulator inquiry has been reported.
Both teams were careful. One of them could settle the question inside a week; the other is still 2 statements on the internet that don't agree. Provability is what got tested, and provability is what synthetic medical data buys you: a privacy position you can state in a sentence instead of reconstructing it from backups. It's the same gap that makes PHI a live problem in vibe-coded healthcare apps.

Synthetic, de-identified, or anonymized: provenance decides your legal position
Synthetic, de-identified, anonymized. The 3 words show up interchangeably on vendor product pages and in whatever sentence a teammate uses to reassure you, and they describe 3 different legal positions.
Only one appears in the regulation. 45 CFR 164.514 defines exactly 2 de-identification methods, expert determination at (b)(1) and Safe Harbor at (b)(2). No provision defines "anonymized" at all.
Where the data came from decides your position. There are 4 ways a healthcare dataset gets made, and they don't leave you in the same place.

The unsettled row is unsettled among people who do this for a living
Row 2 is the one worth staring at, because that's where the argument actually is.
Tonic.ai sells synthetic data generation and pairs it with HIPAA expert determination, an expert reviewing both the synthesis approach and the source data before the output is treated as compliant. A synthesis vendor buying expert determination on top of its own synthesis is the market pricing the uncertainty.
OpenMined's legal analysis concludes synthetic data sits outside HIPAA entirely and can be shared without contractual obligation. 2026 commentary treats generation from real EHR records as an open question that needs counsel. Both readings are credible and they disagree, which is the honest state of row 2.
Row 2 stops being hypothetical the moment you fine-tune on your own records, because those records travel, and that's where LLM APIs and HIPAA become the same conversation.
So the operating rule is a small one. When a vendor or a teammate reaches for one of those 3 words, ask which row they mean. HIPAA compliance for everything downstream of that dataset turns on the answer, and an answer they can't give is itself the answer. Rows 1 and 3 are the ones you can put in writing. Row 1 is the one worth staying in on purpose, because synthetic healthcare data built from population statistics is the only class that never needs the conversation at all.
Pick the generator by what you're trying to prove
The best fake patient data generator depends on a question most teams skip: what does this data have to prove? A pitch demo and a FHIR conformance test need opposite things from the same records, and a tool built for one is bad at the other by design.
Answer that question first and the data generation options sort themselves into 6.

2 calls the table implies and doesn't make
Oracle Health's own documentation describes the cernerdemo tenant as a full production-level tenant populated with realistic synthetic data, where PHI cannot be exposed. The EHR vendors run their own developer environments on synthetic data, which settles the question of whether it's good enough for yours. MITRE retired the SyntheticMass FHIR API, though, so a hosted endpoint is somebody else's uptime.
Most teams end up running 2 of these rather than picking 1, usually a fast generator for demo data and Synthea for depth. Read the effort column as a budget line: 2 tools often cost less total time than forcing 1 to do both jobs.
Sandboxes earn their place at connectivity validation. Reach for one when you need to know whether your auth flow survives a real vendor endpoint. Getting test data in front of a user by the end of the week is a different errand.
Faker and Mockaroo get you a believable demo by Friday
The pitch is Friday. The app shows 4 patients called Test Test, and it has to get through an investor demo, then user testing, then a usability testing round before anybody writes a real record into it.
You can generate fake patient data for all of that by Wednesday. Neither of the 2 tools that do it knows any medicine, and this week that doesn't matter.
Faker hands you a person and leaves the medicine to you
Faker's core library has no clinical fields. Names, addresses, dates, phone numbers, and nothing a clinician would recognize. Its own documentation shows you how to extend it: a hand-written list of medical professions, which marks the ceiling fairly. Clinical vocabulary means reaching for a community package:
- faker-healthcare-system, which supplies NUCC provider taxonomy codes and classifications
- headfake, which drives generation from YAML templates and produces NHS numbers and age-weighted mortality
Point it at your app's own data model and let a loop write the seed data directly, so the fields land where the screens already expect them and there's no import step in between.
Mockaroo gets further without writing anything
Mockaroo's schema builder runs in the browser and carries a Health field category among its built-in types. It also derives a schema from a sample file you upload, so the columns come out matching your app's own model.
That upload step is the whole reason to prefer it over Faker for a founder who'd rather not open a terminal. Export the result, load it, move on to the rest of healthcare app prototyping.
This data won't survive a validator, and that's the right trade
A 2025 medical-imaging de-identification study had to swap real addresses in for Faker's, because de-identification tools recognize Faker addresses as fake and skip straight past them.
The output is detectably synthetic. That's fine on a slide and useless for testing anything that inspects the data, which includes most of what a clinician or a validator does with a record.
This data is built to be looked at. When looking at it is the entire requirement, you've made the right trade.
Synthea gives you FHIR bundles at volume, once you get past its defaults
The fastest route to synthetic FHIR data is a download link. Plenty of teams never need anything past it.
You can skip the build entirely
MITRE publishes ready-made sets. Samples of 100 or 1,000 patient records in several formats, a 21GB SyntheticMass archive covering 1 million records in FHIR, CSV, and C-CDA, and the 9GB Coherent Data Set, which links FHIR resources to DICOM images, genomic data, ECG waveforms, and clinical notes in a single record.
The HL7 FHIR export is what most teams want, and it's already sitting in those archives. Download it and load it. Building it yourself is only worth an afternoon when you need a population these sets don't contain.
The build is 5 steps, and step 3 is the one that bites
About an afternoon, assuming the JDK is the only thing you're missing.
- Java JDK 17 or newer. The project recommends an LTS release, 17 or 25.
- git clone https://github.com/synthetichealth/synthea.git, then cd synthea and ./gradlew build check test.
- Open src/main/resources/synthea.properties and set 4 values to true before you generate anything:
- exporter.fhir.export
- exporter.fhir.transaction_bundle
- exporter.practitioner.fhir.export
- exporter.hospital.fhir.export
Skip the last 2 and the organizations and practitioners your patient records point at never get exported at all. CSV, C-CDA, and bulk ndjson sit in the same file, off by default.
- Run ./run_synthea -p 100. Population size is the flag you'll change most, and it's also what turns this into load testing. -a 60-65 sets an age range and -g M or -g F sets gender. A trailing state and city narrows geography, as in ./run_synthea -p 100 Massachusetts or ./run_synthea Alaska Juneau. Names with spaces need quoting.
- Output lands in ./output/fhir, one transaction Bundle per patient.
The load is where the afternoon actually goes
Each of those FHIR bundles gets POSTed. Synthea's documented form is curl http://hapi.fhir.org/baseR4 --data-binary "@<bundle>.json" -H "Content-Type: application/fhir+json", and a conformant server answers with a transaction-response Bundle.
Order is what costs you. Load the hospital and practitioner bundles first, then the patient bundles, because patient records reference Organization and Practitioner resources that Synthea writes to separate files, and a reversed order leaves those references dangling.
Get the order wrong and nothing errors. The counts look right, and every Patient points at an Organization that was never written.
MITRE publishes a walkthrough for standing up the FHIR test server itself, if you don't already have one to point at.

The profile you picked decided what you got
Synthea supports US Core 6.1.0, 5.0.1, 4.0.0, and 3.1.1. The DocumentReference and DiagnosticReport resources carrying clinical notes appear only under R4 with US Core enabled. If the notes aren't there, the combination you asked for is why.
Synthea modules are JSON state machines edited through a graphical Module Builder, and the Generic Module Framework binds their codes to real terminologies: SNOMED CT for conditions, LOINC for observations, RxNorm for medications. Those are the same code systems your integration will meet in production, which is most of why the output is worth loading at all.
LLMs fill in the clinical narrative, then quietly skew your cohort
Everything Synthea gives you is structured and conformant. The gap in synthetic EHR data is the free text, and free text is what your users are actually going to read.
The narrative is the gap
Synthea emits clinical notes, and they're simple ones.
What an LLM is genuinely good at is the rest: free-text intake answers, patient message threads, and notes that read like a clinician dictated them between appointments. Nothing else in the toolbox produces any of it.
A patient-facing app that has never rendered a real-sounding message thread has never been tested on the thing users spend their time inside.
Ask for 250 patients and look at who shows up
A study out of the University of St Andrews prompted GPT-3.5-turbo and GPT-4-mini for 250 UK patients each, with no demographic steering, then compared what came back against the England and Wales 2021 census. Neither model produced a single patient under 25. GPT-3.5 produced nobody older than 47, GPT-4-mini nobody older than 56. Both skewed heavily male. Every variable diverged from census expectations at p < 0.0001.
Whole populations your app has to serve simply never appear in the output.

The same study found that prompts carrying explicit demographic targets restore the balance, which makes this a prompt-design problem with a known fix.
The population problem and the accuracy problem fail differently
One distorts who's in your data. The other distorts what your data says, which is the one that bites if you're building AI training data out of it.
An evaluation of LLM-generated data at million-note scale, checked against MIMIC, found coarse clinical information survives while the fine-grained detail needed for ICD-10 coding doesn't. Errors were dominated by misreading clinical context, alongside temporal confusion, measurement errors, and fabricated claims.
The move that sends a team backwards is pasting real records into a model and asking for more like them. As of June 2026 OpenAI signs no BAA for its Free, Plus, Pro, or self-serve Team and Business tiers, so those records land somewhere with nothing covering them.
What happens to PHI in AI prompts is its own post, and worth reading before anyone runs that experiment.
Your synthetic health data needs patients that break the app
You built demo-friendly patients because a demo was what you were building. Then the demo data became the test data, and the test data quietly became the specification for what your app can handle.
Everybody builds the demo cohort and stops there
The swap is never a decision. It happens because the demo data exists and the deadline doesn't care.
MITRE's own documentation says Synthea generates basic FHIR resources, populating required fields and rarely the optional ones. The best open tool in this space ships data cleaner than production, and its maintainers say so.
Epic publishes a roster of 9 named test patients on its SMART launcher and no sandbox total anywhere openly accessible, which is why the counts you find quoted online disagree with each other.
The public HAPI server runs without auth or US Core validation, so auth paths and profile conformance go untested. A sandbox environment is curated by definition. Its Patient resources carry no emergency contacts, no marital status, and no communication preferences.
3 things real endpoints do that your test data never will
Each one has a field name attached, which is the difference between a test plan and a shrug.
- Condition.clinicalStatus is only conditionally present, and US Core requires it be absent when verificationStatus is entered-in-error. Cerner's developer forum confirms these can't be filtered server-side, so the client has to cope with a missing value.
- MedicationRequest.requester is mandatory under US Core, and real records leave it blank. The HL7 Validator then correctly marks the resource non-conformant, which is a rare event in production and a never event in generated data.
- The US Core Patient race and ethnicity CDCREC extensions throw validation errors from a code-system OID mismatch that the implementation guide itself flags as a known condition.
All 3 arrive the first time you point the app at a real endpoint, which is what the EHR integration guide walks through. Generated EHR test data will never hand you any of them.
Build the hostile cohort on purpose
Generate 2 patient cohorts and keep them separate. The demo cohort stays photogenic, because somebody still has a pitch on Friday.
The hostile one is the deliverable:
- patients at both ends of the age range
- patients on 8 concurrent medications
- rare conditions
- records with fields simply missing
- histories spanning a decade
- at least 1 patient malformed in each of the 3 ways above

Clinical plausibility is still the constraint. Every one of those edge cases has to be somebody a clinician would believe, or you're testing your app against noise.
Name the hostile cohort as a build task with an owner, because a cohort nobody owns reverts to the demo one. That's the whole difference between synthetic health data that proves something and synthetic health data that agrees with you.
When synthetic runs out, expert determination is the bill
There's a class of question no generated patient can answer. Better to know where that line sits before you've built something on the wrong side of it.
Synthetic data hits its ceiling at validation and regulatory submissions
Validating a model against what happened to actual patients. Regulatory submissions. Anything where the distributions have to be true rather than merely plausible. Those need real records, and no amount of configuration gets you there.
Synthea is explainable and reproducible by construction. That's the same sentence as saying it can only tell you what somebody modeled into it, which is a virtue right up until the question you're asking is what the world actually did.
What expert determination actually costs
Budget a few thousand to tens of thousands of dollars, per Limina's June 2026 guide. Timelines run a few weeks for a simple structured dataset, several months for a complex multi-source set prepared for an FDA submission.
No degree or certification program defines who counts as an expert, and the Privacy Rule sets no numerical threshold for "very small" risk. OCR reviews the expert's professional experience and training during an audit or investigation, which is to say afterwards.
If OCR decides the expert was unqualified, the methodology inadequate, or the certification superficial, the dataset becomes identifiable PHI again, and breach notification follows it everywhere it has already been shared.
The arithmetic is lopsided. Expert determination costs a few thousand to tens of thousands of dollars, once. Skipping it, or buying it cheap, costs you every copy of the dataset reverting to PHI wherever it has already traveled, and by then you don't control the list.
How Specode can help
Everything above is a discipline somebody has to remember on the worst possible day. Specode moves it into the defaults instead: preview environments carry an explicit warning that they aren't HIPAA-compliant, and Section 5.3 of the terms bars PHI from development and staging outright. The boundary in the diagram earlier in this post is already where the platform puts it, which takes compliance off the list of things your development process depends on anyone remembering.
The seeding runs 2 ways, and neither asks you to choose a generator first. Auth flows ship with a pre-populated synthetic provider template, so there's something to log into on day one without anyone writing a seed script. And on request, the AI coder will populate any role the app defines (patients, providers, administrators, or whatever else your data model contains) with synthetic records filling every field that model supports.
Faker doesn't know your schema. Synthea doesn't know your schema. The builder that wrote your schema does, so the records come out matching screens that already exist instead of needing a mapping layer first. No general-purpose test data for healthcare apps can do that, because it has never seen your data model. One path ends in a demo on Friday. The other ends in an afternoon of import scripts.
Frequently asked questions
The question is malformed. HIPAA governs information about real people, so data modeled from population statistics falls outside it. Data generated from real PHI is contested.
De-identified data starts from real records, so HIPAA governs the process and re-identification risk never reaches zero. Synthetic data never had a real individual behind it.
There isn't one. Synthea for FHIR at volume, Mockaroo for a fast demo without code, an LLM for narrative text.
Testing, yes. Training is where fidelity limits bite: cohort skew and lost fine-grained detail become model behavior rather than test noise.
Building one cohort. Demo-friendly data passes every test and teaches the team nothing, because it never exercises missing fields, extreme ages, or polypharmacy.








