Map FHIR resources to OMOP CDM v5.4
/fhir2omop/createMaps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows,
grouped by destination table in tables.
Standards basis: FHIR R4 (v4.0.1) defines the accepted source elements and OMOP CDM v5.4 defines the output columns. The published Vulcan FHIR-to-OMOP IG v1.0.0 is an informative FHIR R5 baseline; this endpoint documents and implements the equivalent R4 source elements, rather than accepting R5-only fields.
This response is a mapping result, not a complete CDM load pipeline.
When a source cannot supply a field that CDM v5.4 requires, the row is
still returned with that field unset; the value is not inferred. Common
cases are year_of_birth without a usable birthDate,
drug_exposure_end_date without an explicit end or single-event timing,
and a required event date (such as condition_start_date,
procedure_date, or death_date) whose source has no timing with at
least day precision. Apply your own policy to such rows before loading
them into a strictly conformant CDM instance.
Current resource coverage:
Patient->person;deceased[x]can also producedeath, the first address can producelocation, and more than one supplied race producesobservationrace rows (see Patient demographics below)observation_period-> one request-local derived row per person, spanning the populated dates of that person's visit, clinical, and death rows; this is not enrollment or capture-completeness evidenceLocation->locationandcare_siteOrganization->care_site; its first address can producelocationHealthcareService->care_sitePractitionerandPractitionerRole->providerEncounter->visit_occurrenceCondition->condition_occurrenceProcedure->procedure_occurrenceMedicationRequest,MedicationStatement, andMedicationAdministration->drug_exposureImmunization->drug_exposureObservation->measurementorobservation. For coded Observations, the resolved OMOP concept domain selects the table; value form only breaks ties. For text-only Observations, numeric values route tomeasurementand nonnumeric values toobservation.AllergyIntolerance->observation
Medication is reference data for medication resources; it does not
create its own row because OMOP CDM has no Medication table. Administrative
linkages (provider, care site, and location) are best-effort and limited to
references supplied in the request. Patient.managingOrganization is a
record custodian, not a care-delivery site. Provider specialty is not
mapped. Recorded Practitioner.gender is distinct from Person
demographics: male and female resolve to validated OMOP Gender
concepts in provider.gender_concept_id; other, unknown, and absent
gender remain unmapped. Address.country is resolved to location.country_concept_id,
and CMS Place of Service codings in Location.type are resolved to
care_site.place_of_service_concept_id.
A PractitionerRole that identifies one supplied Practitioner aliases
that canonical provider: by a top-level structural reference, a
parent-contained #id reference, or an exact identifier.system and
identifier.value match against a top-level Practitioner. No remote
identifier lookup is performed. When Reference.type is present it must
be Practitioner; duplicate contained IDs and identifier matches are
ambiguous. An explicit reference that is unresolved, ambiguous, or
unsupported retains a role-fallback provider row and is returned in
diagnostics. provider_role_contexts preserves role-specific
specialty and care-site context that a canonical OMOP provider row cannot
represent together.
Patient demographics:
- Sex at birth, race, and ethnicity are read from these US Core
extensions, with their US Core 6.1.0 structures and value sets, on
any Patient (US Core profile conformance is not required). Sex at
birth falls back to Patient
gender. Other extensions, including US Core sex and gender identity, are ignored.- Birth sex:
http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex(valueCodefromhttp://hl7.org/fhir/us/core/ValueSet/birthsex) - Race:
http://hl7.org/fhir/us/core/StructureDefinition/us-core-race(ombCategoryfromhttp://hl7.org/fhir/us/core/ValueSet/omb-race-category) - Ethnicity:
http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity(ombCategoryfromhttp://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category)
- Birth sex:
gender_concept_idis sex at birth. A supplied birth sex always decides it:MandFare resolved;UNK,ASKU,OTH, a code outside the value set, conflicting values, and a birth sex withoutvalueCodeleave it0, and Patientgenderis not used.- Without a birth sex, Patient
gendermaleorfemaleis resolved under the OMOP convention that the supplied gender represents sex at birth;otherandunknownkeep concept0. gender_source_valueis the chosen source code (Ffor birth sex,femalefor Patientgender).- Each race category is resolved separately, and null flavors (
UNK,ASKU) are ignored. One distinct standard race setsrace_concept_id. More than one sets it to1546847(More than one race) and adds oneobservationrow per race, withobservation_concept_id4013886(Race), the race invalue_as_concept_id,observation_type_concept_id32817, the category code invalue_source_value, and noobservation_source_valueorobservation_date. A loading pipeline that requiresobservation_datemust apply its own date policy. - The single non-null ethnicity category is resolved, and null flavors
are ignored; more than one distinct category leaves
ethnicity_concept_id0. Ethnicity is not derived from race, and no demographic is inferred from names, addresses, or other extensions. race_source_valueandethnicity_source_valuelist every supplied category and detailed code in source order, joined with|, or the extension text when no code is supplied. Detailed codes and text are not resolved.- Every supplied birth sex, gender, and OMB category code has a
mappingsentry whosenotenames its source and outcome. When a birth sex is supplied, Patientgenderis reported unselected with the noteFHIR administrative gender; not used, birth sex supplied. Conflicting values and a birth sex withoutvalueCodeare also returned indiagnosticswith pathextension:birthsexorextension:ethnicity.summarycounts each demographic field once, as described underSummary. - Differences from the reference conventions: Vulcan's example gender
ConceptMap maps
otherandunknownto concepts, which stay0here; more than one race follows the OHDSI THEMIS convention, also used by Vulcan, rather than the CDM 5.4 note that mixed races use0; and Vulcan's suggestedobservationrows for multiple ethnicities are not produced.
DiagnosticReport, ServiceRequest, CarePlan, DocumentReference,
Composition, Specimen, DeviceUseStatement, Coverage, Claim, and
other unsupported resource types are accepted in a Bundle but ignored: they
create no row and no dropped entry. dropped is reserved for supported
row-producing resources that could not be shaped because the subject/patient,
clinical code/text, or medication data was not usable. A single-Patient
Bundle uses the sole Patient only when subject/patient is absent. An
explicit subject/patient reference that is unresolved, ambiguous, or
unsupported drops the clinical resource in every request scope.
Coded Observation routing is selected from the resolved OMOP concept
domain. Numeric and nonnumeric value[x] forms establish the preferred
target only when the code is valid for both tables. A text-only
Observation has no resolver target, so numeric values route to
measurement and nonnumeric values to observation. Numeric values
populate value_as_number in the selected row; other non-coded values
populate value_as_string for an observation or value_source_value
for a measurement. Coded valueCodeableConcept values are resolved
against the selected row's value_as_concept_id and use the selected
bare code in value_source_value, leaving an observation's
value_as_string empty; unmapped or target-invalid coded values remain
0.
Other unsupported value[x] forms and Observation components do not
populate separate converted values. A
numeric comparator (<, <=, >, >=) is represented only by a
measurement's operator_concept_id; units remain source text and have
unit_concept_id of 0.
A standard OMOP concept_id is selected for each primary clinical coding
after considering all of the resource's supplied codings. An unambiguous
coded medication route is resolved independently to
drug_exposure.route_concept_id. Alongside the OMOP rows grouped by
table (tables), the response carries mappings (an entry for every
supported source coding, linked back to the row it produced; some, such
as demographic null flavors, are reported without being resolved),
provider_role_contexts (source role details linked to provider rows),
dropped (resources that could not be shaped into a row),
diagnostics (explicit references that could not safely create a link,
and conflicting or unsupported Patient demographic extensions),
vocab_version (the OMOP vocabulary release codes were resolved
against), and a small summary of the resolution outcomes.
A concept_id of 0 is reported, not omitted (OMOP "no matching
concept" semantics): it covers both a coding with no standard match
(UNMAPPED) and an unverified suggestion for a text-only resource
(UNCHECKED). Visit and unit concept fields currently remain 0;
Person demographics and Provider recorded gender follow the policies
above. Coded Observation values may populate value_as_concept_id.
Concepts set by a fixed convention rather than terminology resolution
are measurement operator_concept_id, set from a value comparator (<,
<=, >, >=), and the multiple-race concepts described above. Clinical
*_source_value fields contain the selected FHIR code (or source text
for text-only resources).
The corresponding selected mappings entry preserves the coding system
and full source-coding provenance.
Known OID-form coding systems are accepted as either FHIR OID URNs (for
example, urn:oid:2.16.840.1.113883.6.1 for LOINC) or bare OIDs, and
are normalized to their canonical system URLs before terminology
resolution. mappings[].source_system reports that canonical URL, so the
OID and URL forms produce the same mapping. An unknown OID is not
rewritten and may be UNMAPPED.
Other *_source_value fields preserve row-specific raw source values,
such as resource identifiers, names, units, or status codes.
MedicationRequest uses 32838 (EHR prescription) for
drug_type_concept_id; other current resources use 32817 (EHR). This
is a coarse provenance policy: it does not infer patient-reported,
medication-history, or other more-specific type concepts from FHIR
status fields.
Dates and datetimes:
- A
*_dateis the calendar date (YYYY-MM-DD) of a source value with at least day precision. A*_datetimeis set only when that value has a time of day, as local time without a UTC offset (YYYY-MM-DDTHH:MM:SS, with fractional seconds to microseconds when supplied):2024-01-15T23:30:00-05:00becomes2024-01-15T23:30:00. A datetime without a timezone is read as local time. - A partial date (
2024or2024-03) leaves both columns unset. It still counts as that source's value, so later sources in the list below are not used. - Free-text timing (such as
onsetString),AgeandRangeforms, and values that are not dates are ignored, so a later source in the list is used if there is one. - Other than the sources below and the same-day ends of single-event medication records, no date is imputed: partial dates are not completed, and a row is not dated from another resource such as its Encounter.
- Date values never cause a request to be rejected, and the response does not report which date elements were unusable.
Timing sources, in priority order where several are listed:
Patient:birthDatesetsyear_of_birth,month_of_birth, andday_of_birthfrom the parts it supplies;birth_datetimeis not set.deceasedDateTimesets thedeathdates.Practitioner:birthDatesets the provider'syear_of_birth.Encounter:period.startandperiod.end.Condition: start fromonsetDateTimeoronsetPeriod.start, thenrecordedDate(when the condition was recorded, not when it began); end fromabatementDateTimeorabatementPeriod.end.Procedure: start fromperformedDateTimeorperformedPeriod.start; end fromperformedPeriod.endonly.Observation:effectiveDateTime,effectivePeriod.start, oreffectiveInstant.AllergyIntolerance:recordedDate, thenonsetDateTimeoronsetPeriod.start.MedicationStatement: start fromeffectiveDateTimeoreffectivePeriod.start; end andverbatim_end_datefromeffectivePeriod.end.dateAssertedrecords when the statement was made and is not used.MedicationAdministration: aneffectiveDateTimeis a single event that sets both start and end; aneffectivePeriodsets the start and, when present, the end andverbatim_end_date.Immunization:occurrenceDateTimeis a single event that sets both start and end;expirationDateis not used.MedicationRequest:authoredOn, the order date, sets the start; it is not evidence of administration. No end is set, and the validity period is not used as an exposure duration.- Differences from the Vulcan maps:
birth_datetimeis not set frombirthDate, Condition also readsonsetPeriod.start, and AllergyIntolerance falls back to its onset whenrecordedDateis missing.
Medication details:
- For
MedicationRequest,dispenseRequest.numberOfRepeatsAllowedsetsrefillsand a whole-dayexpectedSupplyDurationsetsdays_supply. - All non-empty dosage text is preserved in
sig. - Coded dosage routes and
Immunization.routeare target-validated in the OMOP Route domain. Conflicting routes are left unset; route codings shared by every dosage instruction identify the same route. Immunization.lotNumberis preserved inlot_number.
Medication codes are resolved whether they appear inline
(medicationCodeableConcept) or via a medicationReference to a contained,
relative (Type/id), or bundle-entry (urn:uuid) Medication resource.
A reference that resolves to another resource type is dropped even when it
supplies display text; an unresolved or display-only reference may use
its display as text-only medication input.
Resources that cannot be shaped into a row — a medication with no usable
code, resolvable reference, or display, or any clinical resource whose
subject/patient reference cannot be tied to a person — are reported under
dropped rather than emitted as blank rows. The Bundle must contain at
least one Patient resource.
Structural references resolve only to top-level resources supplied in the
request, by Type/id or an exactly matching Bundle fullUrl (including
urn:uuid). Contained references are supported for medication code lookup
and PractitionerRole.practitioner enrichment; the latter also supports
exact request-local identifier matching without a remote lookup. Every
nonzero structural foreign key targets a row in the same response. Missing
optional links remain unset without a diagnostic; an explicit optional
reference that is unresolved, ambiguous, conflicting with the row's
person, or unsupported remains unset and is returned in diagnostics
with its source path and outcome.
All row IDs start at 1 for each request and are not stable or global.
For clinical conversion rows whose resource supplies an id, mappings
associates each row with that source FHIR resource ID. A person row
retains the Patient ID or its first identifier value in
person_source_value, when present; other reference and derived rows do
not uniformly carry a FHIR resource ID. Input resources without those
source identifiers cannot be correlated across responses from the
returned rows alone. Consumers combining responses need to establish
their own stable keys and remap every primary and foreign key together.
Body parameters
FHIR resources (single resource or Bundle). Must contain at least one Patient resource. Supported row-producing resources are Patient, Location, Organization, HealthcareService, Practitioner, PractitionerRole, Encounter, Condition, Procedure, MedicationRequest, MedicationStatement, MedicationAdministration, Immunization, Observation, and AllergyIntolerance. Standalone Medication resources are consumed by medication references rather than mapped to their own table. Unsupported resource types are accepted in a Bundle but ignored.
FHIR resources mapped to OMOP CDM v5.4
Response fields
OMOP CDM v5.4 rows grouped by destination table. IDs are sequential and
scoped to one response; they are not stable keys across requests.
Fields with no value are unset (omitted from the row), except concept
IDs reported as 0. Each *_datetime comes from the same source as its
*_date and is set only when that source has a time of day.
Year from Practitioner.birthDate.
For recorded Practitioner.gender, male and female resolve to validated OMOP Gender concepts. other, unknown, and absent values remain 0.
The source practitioner identity. A Practitioner contained by a PractitionerRole is scoped as PractitionerRole/<role-source-value>#<contained-id> so identical local contained IDs do not collide; an id-less parent uses an explicitly marked response-local role ordinal such as @role-index:1.
The recorded FHIR administrative-gender value for this Provider.
Remains 0 for FHIR administrative-gender enum-policy results.
Standard OMOP Gender concept for sex at birth, from US Core birth sex when supplied, otherwise from Patient gender male or female. 0 for absent, unknown, other, unsupported, or conflicting values.
Year from Patient.birthDate.
Month from Patient.birthDate, when it supplies one.
Day from Patient.birthDate, when it supplies one.
Not set; Patient.birthDate has no time of day.
Standard OMOP Race concept from a US Core race OMB category; 1546847 (More than one race) when more than one distinct race resolves, with each race in an observation row. 0 when no category resolves.
Standard OMOP Ethnicity concept from the US Core ethnicity OMB category. 0 when absent, unresolved, or conflicting; never derived from race.
The selected sex-at-birth source code: the US Core birth sex valueCode, or Patient gender when no birth sex is supplied. Conflicting birth sex values are joined with |; empty when the birth sex has no valueCode.
OMOP source concept of the selected sex-at-birth code, when that code is itself an OMOP source concept; 0 otherwise, as for FHIR administrative gender codes.
Every supplied US Core race category and detailed code, joined with | in source order, or the extension text when no code is supplied.
OMOP source concept of a single resolved race code, when that code is itself an OMOP source concept; 0 otherwise, including when more than one race resolves.
Every supplied US Core ethnicity category and detailed code, joined with | in source order, or the extension text when no code is supplied.
OMOP source concept of the resolved ethnicity code, when that code is itself an OMOP source concept; 0 otherwise.
Date from Patient.deceasedDateTime; unset for a boolean-only or partial value.
Earliest populated date among the person's visit, clinical, and death rows in this request; not enrollment evidence.
Latest populated date, including end dates, among the person's visit, clinical, and death rows in this request; not enrollment evidence.
Date from Encounter.period.start.
Date from Encounter.period.end.
Date from Condition.onsetDateTime or onsetPeriod.start, otherwise Condition.recordedDate.
Date from Condition.abatementDateTime or abatementPeriod.end.
Date from the resource's timing source, such as effective[x], occurrenceDateTime, or MedicationRequest.authoredOn.
Date from an explicit FHIR Period end, or the same day as the start for a single-event administration or immunization. Unset when the source supplies neither.
Date from an explicit FHIR Period.end only; inferred same-day ends are not verbatim source values.
Direct MedicationRequest.dispenseRequest.numberOfRepeatsAllowed value, when supplied.
Direct positive whole-day MedicationRequest.dispenseRequest.expectedSupplyDuration; no dose or quantity conversion is applied.
Newline-joined non-empty FHIR Dosage.text instructions in source order.
Target-valid OMOP Route concept for an unambiguous coded FHIR route; 0 for an unmapped coded route, omitted for absent, text-only, or conflicting routes.
Direct FHIR R4 Immunization.lotNumber value.
Selected source coding or text for an unambiguous FHIR route.
Date from Procedure.performedDateTime or performedPeriod.start.
Date from Procedure.performedPeriod.end.
Date from Observation.effectiveDateTime, effectivePeriod.start, or effectiveInstant.
OMOP "Meas Value Operator" standard concept qualifying value_as_number (<, <=, >, >=), parsed from a numeric-string value or a FHIR valueQuantity.comparator. 0 when no operator (a bare number).
For an Observation, date from effectiveDateTime, effectivePeriod.start, or effectiveInstant. For an AllergyIntolerance, date from recordedDate, otherwise onsetDateTime or onsetPeriod.start.
One entry per supported source coding (or one entry for a text-only primary resource with no coding), describing how it resolved and linking back to the row it produced. A coded route or Observation valueCodeableConcept is a separate entry linked to its medication, vaccine, or observation row. A Patient demographic code links to its person row, or to its observation race row when the person has more than one race.
The id of the OMOP row this coding produced (e.g. condition_occurrence_id),
within omop_table. A resource with multiple codings yields one entry
per coding, all sharing this id.
The OMOP concept-ID field populated from this source coding, such as condition_concept_id, route_concept_id, or race_concept_id.
The standard concept's own code: the source code itself for an ALREADY_STANDARD row, the standard concept's code for a MAPPED row, or the suggested code for an UNCHECKED row. Omitted for UNMAPPED rows.
ALREADY_STANDARD (source coding is already a standard OMOP concept),
MAPPED (source coding was mapped to a standard concept), UNCHECKED (a
standard code was suggested for a text-only resource but not verified
against the OMOP vocabulary, so concept_id stays 0), or UNMAPPED
(no standard concept found).
ALREADY_STANDARDMAPPEDUNCHECKEDUNMAPPEDWhether this source coding was selected for the linked row's *_source_value field. Always present; false for alternate codings and text-only rows. For a Patient demographic, it marks the code that determined the PERSON field or race observation row, even when that code has no concept; it is false for race and ethnicity null flavors, conflicting values, a Patient gender overridden by birth sex, and race categories that did not determine the field.
Additional context for the entry. A coded route is noted as
FHIR route. Patient demographic entries name their source and, when
not applied, why:
US Core birth sex;US Core birth sex; null flavor;US Core birth sex; outside value set;US Core birth sex; conflicting valuesFHIR administrative gender; assumed sex at birth;FHIR administrative gender; not a sex-at-birth value;FHIR administrative gender; not used, birth sex suppliedUS Core race OMB category;US Core race OMB category; more than one race(linked to itsobservationrace row);US Core race; null flavorUS Core ethnicity OMB category;US Core ethnicity OMB category; conflicting categories;US Core ethnicity; null flavor
Additive FHIR provenance for every supplied PractitionerRole. Each context identifies the canonical or role-fallback provider row and preserves source role facts that OMOP's singular provider columns cannot represent together.
The PractitionerRole source identity: its FHIR id, identifier value,
name, or fullUrl. When none is available, it is PractitionerRole.
The role's supplied practitioner reference, when present.
The logical identifier supplied on a PractitionerRole's practitioner reference.
Every organization, healthcareService, or location reference supplied by the role.
The FHIR element path on the PractitionerRole.
Supported resource instances that could not be shaped into an OMOP row because the subject/patient, clinical code or text, or medication data was missing or unusable, including an explicit subject/patient reference that was unresolved, ambiguous, or unsupported. A resource that lacks only a date or another CDM-required field is returned as a row instead. Unsupported resource types are ignored and do not appear here.
Explanations for explicit references that could not safely produce
an OMOP link or canonicalize a PractitionerRole provider identity, or explicit
subject/patient references that caused a clinical row to be dropped.
Missing optional references are normal and do not produce a diagnostic.
References resolve only against resources supplied in this request.
Outcomes distinguish unresolved, ambiguous, conflicting, and unsupported
references. Patient demographic extensions that conflict, or a birth
sex without valueCode, are also reported here; their path is
extension:birthsex or extension:ethnicity and they have no
reference.
FHIR element path on the source resource.
The supplied Reference.reference value, when present.
UNRESOLVEDAMBIGUOUSCONFLICTINGUNSUPPORTEDThe OMOP vocabulary release returned for coded concept resolution (for example, "v20260227"), for reproducibility. It is generally absent for requests containing only text-only resources.
The request's data-quality headline: how row-producing coded concepts,
selected routes, and Patient demographic fields split, and the share
that was not already in a target standard vocabulary. Each is counted
once even when it carried several codings — unlike mappings, which has
one entry per coding. For example, a medication code and its selected
coded route on the same drug_exposure row are separate outcomes;
independently checked report-only alternate route codings do not alter
summary. A Patient demographic counts once per PERSON field, or per
race observation row, that received a non-null value; conflicting
values count once as unmapped. Null flavors, Patient gender other or
unknown, a Patient gender overridden by birth sex, and race
categories that did not determine the field are reported in mappings
only.
Resolution outcomes already a standard OMOP concept (ALREADY_STANDARD).
Resolution outcomes mapped or suggested to a standard concept (MAPPED or UNCHECKED).
Resolution outcomes with no standard concept found (UNMAPPED).
Share of resolution outcomes not already standard ((normalized + unmapped) / total).