Importing FHIR Patient resources from CSV
How AdaptivMapr maps messy patient roster spreadsheets to a FHIR Patient resource, including identifiers, name parts, and contact points.
A FHIR Patient resource looks deceptively simple in the spec. In practice, every customer who hands you a roster CSV will flatten it into something that does not match the spec at all. Names live in one column. Identifiers are mixed in with phone numbers. Birthdates carry a four-digit year half the time and a two-digit year the rest.
The patient_demographics template that ships with AdaptivMapr exists to absorb that mismatch. It is a medium-risk template (auto-routed through the HITL queue when AgentGate is wired) and emits a FHIR R4 Patient resource, with identifiers split out into the right Identifier.system slots.
What the template knows
Each field carries multilingual hints (DE/FR/IT/EN/ES) and one or more validators. For the Swiss case, the AHV/AVS/NSS hint lives on the same field — see the dedicated post on the Swiss healthcare ID problem for why that is a single column, not three.
The mapper sees a header like:
["Pat-Nr.", "Vorname", "Familienname", "Geb.Dat.", "AHV", "Telefon"]and resolves it to identifier, name.given, name.family, birthDate, identifier[ahv], and telecom[phone]. The first match comes from the heuristic layer (the German hint list contains "Geburtsdatum", "Geb.Dat.", "Geboren"). The AHV hit comes from the validator layer — the regex matches even when the column is labelled "Sozialversicherungsnummer".
The output shape
Once the mapping is confirmed, the row emits a partial FHIR Patient resource. Identifiers go into identifier[] with the right system URI; the name parts get bundled into a HumanName with use: "official"; the phone number becomes a ContactPoint with system: "phone".
{
"resourceType": "Patient",
"identifier": [
{ "system": "urn:oid:2.16.756.5.32", "value": "756.1234.5678.97" }
],
"name": [
{ "use": "official", "family": "Müller", "given": ["Anna"] }
],
"birthDate": "1981-03-14",
"telecom": [
{ "system": "phone", "value": "+41 79 123 45 67" }
]
}What the template does not do
AdaptivMapr maps and validates. It does not write to your FHIR server. The emitted resource is a JSON payload your code can post to /Patient on whichever server you target — HAPI, Medplum, Firely, an in-house store. The boundary is deliberate: every customer has a different policy on Patient.active, on identifier.assigner, on which extensions to attach.
Schema-only is enough for most files
The whole flow above runs in schema-only mode. Headers plus three sample rows is enough for the cascade to confirm the mapping; the full file never leaves the customer. If a particular file needs row-level cleanup (split "John A. Doe" into first/middle/last, normalise "M"/"male"/"Männlich"), that is a full-data call routed through PHI Gateway under the inherited BAA.
Want to try it? The patient_demographics template ships with an anonymised fixture; you can run it through the workbench or via MCP in under a minute. See the full template catalog for the other nine.
Try it in 30 seconds
Schema-only mode is free and unlimited. No DPA, no card, no signup required for the MCP free tier.