1. Exchanging Data vs. Exchanging Meaning
FHIR solved the data exchange problem. A hospital can send a FHIRObservation to a research platform, a FHIR MedicationStatement to a safety database, or a FHIR Condition to a registry. The structure is standardized. The JSON is valid. The API works.
But open the payload and look at the CodeableConcept - the field that carries the clinical meaning. You’ll find LOINC codes from one system, proprietary local codes from another, and sometimes just a text field with “Blood Sugar” and no structured code at all. The data exchanged successfully. The meaning got lost in translation.
This is the semantic interoperability gap. FHIR standardizes structure; OMOP standardizes vocabulary. To go from exchanged FHIR data to analyzable OMOP records, you need to resolve every CodeableConcept to a standard OMOP concept ID.
OMOPHub gives you two purpose-built surfaces for this job:
- The FHIR Resolver (
POST /v1/fhir/resolve*) - a composite, OMOP-aware endpoint that takes a FHIRCodingorCodeableConceptand returns a standard OMOP concept, a mapping type, and a CDM target table. This is what you want for ETL pipelines. - The FHIR Terminology Service (
https://fhir.omophub.com/fhir/{r4,r4b,r5,r6}/*) - a spec-conformant FHIR terminology server implementing$lookup,$validate-code,$translate,$expand,$subsumes,$closure,$find-matches, and the OMOPHub-custom$diff. This is what you want when a FHIR client, HAPI FHIR server, or SMART app needs to talk to a real terminology service.
2. Three Integration Paths
OMOPHub exposes three ways to do vocabulary resolution over FHIR data. Pick the one that matches your job:| Your job | Use | Endpoint |
|---|---|---|
ETL: map a FHIR Coding to an OMOP standard concept + CDM target table | FHIR Resolver | POST https://api.omophub.com/v1/fhir/resolve |
| A FHIR client needs a conformant terminology server (validate-code, expand…) | FHIR Terminology Service | https://fhir.omophub.com/fhir/r4/* |
| HAPI FHIR server needs a remote terminology backend | FHIR Terminology Service | See HAPI FHIR Integration |
| Building SMART on FHIR / CDS Hooks apps at point of care | FHIR Resolver | See EHR Integration |
| Custom traversal, hierarchy walks, ingredient resolution, semantic search | REST primitives | /v1/concepts/*, /v1/search/*, /v1/mappings/* |
3. Authentication & Endpoints
OMOPHub serves the Resolver and the Terminology Service from two hostnames, but both are backed by the same service and the same API key.| Surface | Base URL | Format |
|---|---|---|
| REST API + FHIR Resolver | https://api.omophub.com | OMOPHub JSON envelope |
| FHIR Terminology Service | https://fhir.omophub.com | FHIR Parameters / OperationOutcome |
Authorization header:
/v1/* and /fhir/*, but the convention is: api.omophub.com for REST and the Resolver, fhir.omophub.com for the Terminology Service. Use the matching host in your clients so DNS, CORS, and logs stay clean.
FHIR Terminology Service versions are path-segment prefixes:
/fhir/r4, /fhir/r4b, /fhir/r5, /fhir/r6. Omitting the segment (/fhir/metadata) defaults to r4. Response headers X-Vocab-Release and X-Vocab-Release-Status tell you which OMOP vocabulary release served the request.4. Path A: The FHIR Resolver (Recommended for ETL)
The Resolver is a single composite endpoint that chains every step of FHIR → OMOP resolution:- Resolve the FHIR
systemURI to an OMOPvocabulary_id. - Look up the source concept by code + vocabulary.
- If the source concept is non-standard, follow
Maps toto the standard equivalent. - If the code isn’t found, fall back to semantic search on the
displaytext, filtered by the expected domain. - Determine the CDM
target_table(e.g.measurement,drug_exposure,condition_occurrence) from the resolved concept’s domain. - Optionally return Phoebe recommendations and mapping-quality signals.
POST /v1/fhir/resolve- singleCodingPOST /v1/fhir/resolve/batch- up to 100 codings per request, metered as N callsPOST /v1/fhir/resolve/codeable-concept- fullCodeableConceptwith OHDSI vocabulary preference ranking when multiple codings are present
4.1 Single Coding
Python
{success, data, meta} shape. The resolution object always contains a source_concept (what was found for the input code) and a standard_concept (what you should write to CDM tables after Maps to traversal) - they’re identical when the source is already standard.
4.2 Batch Resolve
Bulk ETL workloads should use the batch endpoint. Each item is resolved independently and errors are reported per-item:4.3 CodeableConcept with Vocabulary Preference
When a singleCodeableConcept carries multiple codings for the same clinical meaning (common in EHR exports - SNOMED and ICD-10-CM for the same condition), the CodeableConcept endpoint picks the best one per OHDSI’s vocabulary preference:
Python
data.all_matches, so you can audit the preference decision.
4.4 Response Shape: the Fields That Matter
Every resolver response - single, batch, and CodeableConcept - returns the sameresolution object. The fields worth knowing:
| Field | What it tells you |
|---|---|
source_concept | The OMOP concept matching the input code in its source vocabulary. May be non-standard. |
standard_concept | The OMOP concept after Maps to traversal. This is what you write to the CDM. |
mapping_type | direct (source was already standard), mapped (followed Maps to), semantic_match (fell back to display-text search), or unmapped. |
similarity_score | Only present when mapping_type = semantic_match - the text similarity of the fallback match. |
target_table | The OMOP CDM destination table (measurement, condition_occurrence, drug_exposure, procedure_occurrence, observation, …). Computed from the standard concept’s domain. |
domain_resource_alignment | aligned / misaligned - whether the resolved concept’s domain matches the FHIR resource type you declared in resource_type. A misaligned Observation → drug domain is a strong signal that the source data is wrong. |
alternative_standard_concepts | Other plausible Maps to targets (for ambiguous source codes). |
recommendations | Phoebe recommendations, if include_recommendations: true. |
mapping_quality | high / medium / low / manual_review, if include_quality: true. |
5. Path B: The FHIR Terminology Service
When you need a real FHIR terminology server - because your client speaks FHIR, because you’re conformance-testing, or because HAPI FHIR wants a remote backend - point it athttps://fhir.omophub.com. Every operation is spec-conformant (for FHIR R4/R4B/R5/R6) and returns FHIR Parameters or OperationOutcome resources with application/fhir+json.
Every FHIR route accepts an optional version prefix:
/fhir/r4. Replace with /fhir/r5 or /fhir/r6 as needed.
5.1 CapabilityStatement - GET /metadata
Every FHIR server starts here. OMOPHub returns a CapabilityStatement listing the supported resources and operations:
CodeSystem (read, search-type, $lookup, $validate-code, $subsumes, $find-matches), ValueSet ($expand, $validate-code), and ConceptMap ($translate, $closure).
5.2 $lookup - concept details for a code
Returns the display, designations, and properties for a given code. Supports the full FHIR $lookup parameter set, plus the OMOPHub-specific property=recommended for Phoebe recommendations.
Parameters resource with name, display, designation[], and property[] entries.
5.3 $validate-code - check that a code belongs to a system or value set
Two variants: one against a CodeSystem, one against a ValueSet.
Parameters resource with result: boolean, display: string (the canonical display), and message: string (on mismatch).
5.4 $translate - map a code between vocabularies
Uses the OMOP Maps to relationship graph to translate a source code to a target CodeSystem.
match parameters, each with an equivalence (equivalent, wider, narrower, …) and a concept Coding.
5.5 $expand - enumerate a value set
Expands a ValueSet URL into its member concepts. Supports filter, count, offset, and includeDesignations. Works for implicit SNOMED value sets (http://snomed.info/sct?fhir_vs) and for OMOPHub’s curated value sets.
ValueSet with an expansion containing total (across the whole match, not just this page), offset, parameter[], and contains[] (the member concepts).
5.6 $subsumes - hierarchy relationship between two codes
Answers: is code A an ancestor, descendant, or equivalent of code B in the same CodeSystem? Useful for phenotype inclusion/exclusion rules.
outcome: subsumes | subsumed-by | equivalent | not-subsumed.
5.7 $find-matches - semantic search in a FHIR envelope
OMOPHub’s semantic search exposed through the standard FHIR operation. Send property parameters describing what you’re looking for; get Coding results back.
/v1/search/semantic endpoint.
5.8 $closure - maintain a transitive closure table
The standard FHIR operation for incrementally building a transitive closure as new codes are seen. Useful when you’re materializing a phenotype or cohort definition over streaming data.
ConceptMap with the new subsumption relationships discovered for the codes in this batch, relative to everything already in the closure.
5.9 $diff - OMOPHub-custom vocabulary version comparison
Not in the FHIR spec. OMOPHub adds this because vocabulary releases change over time and CDM operators need to know what moved. $diff compares two OMOP vocabulary releases and returns the added, removed, and changed concepts for a given CodeSystem.
5.10 Batch Bundle - multiple operations in one request
OMOPHub supports the FHIRbatch/transaction Bundle pattern. Send a Bundle with multiple entry.request items and get back a Bundle with one response per entry.
6. Path C: Raw Vocabulary Primitives
The Resolver and the Terminology Service are opinionated. When you need full control - walkingHas ingredient relationships, custom semantic-search filters, hierarchy traversal with specific relationship types - drop to the REST primitives:
Python
- You need
Has ingredient/RxNorm has ingresolution for drugs (see the MedicationStatement use case in §8 below). - You want to control the exact relationship types walked during a hierarchy traversal.
- You want to combine semantic search with domain filters that the Resolver doesn’t expose.
- You’re building tooling that needs raw concept metadata (vocabulary ID, concept class, domain, validity).
7. Use Case: FHIR Observation → OMOP Measurement
The most common FHIR-to-OMOP mapping. Lab results and vital signs arrive asObservation resources and need to become OMOP measurement records.
Scenario: A blood glucose Observation arrives with LOINC code 2339-0. Resolve it to a standard OMOP concept and build a measurement row.
Python
mapping_type = direct. The Resolver confirms target_table = "measurement" - use that as a sanity check that your downstream CDM writer is pointed at the right table. If the Observation carried a local code instead of LOINC, the Resolver would fall back to semantic search over the display text automatically.
See also: Lab normalization in EHR Integration for the sidecar-app architecture that wraps this resolution pattern.
8. Use Case: FHIR MedicationStatement → OMOP Ingredient
Medication reconciliation requires rolling specific product codes up to their active ingredient for class-level analysis. Scenario: A FHIRMedicationStatement arrives with NDC code 0071-0155-01 (Lipitor 10mg). For a drug safety study, you need the active ingredient (Atorvastatin), not the branded product.
The Resolver handles the NDC → standard RxNorm leg. The ingredient traversal is a separate concepts.relationships call because “Has ingredient” is a non-hierarchical relationship, not an ancestor.
Python
drug_exposure_concept_id (the branded product) in one call. Ingredient-level analysis is a separate question - OMOP stores drug composition as relationships, not hierarchy - and requires a follow-up call on the raw primitives. Don’t try to squeeze this into the Resolver; use the right tool per leg.
See also: Real-time medication CDS in EHR Integration for the CDS-Hooks pattern that combines ingredient lookup with drug-drug interaction alerting.
9. FHIR Extensions & CodeableConcept Nuances
Real-world FHIR uses extensions - extra data fields not in the base spec. US Core adds detailed race/ethnicity extensions toPatient; mCODE adds staging and grade extensions to Condition; QI-Core adds adherence extensions to MedicationStatement. When extensions contain CodeableConcept values, the resolution pattern is identical to the base resources.
Python
CodeableConcept lives at the top of a resource, inside a component, or inside an extension.
When a
CodeableConcept in an extension has a text field but no coding, send it to the Resolver with display set and no system/code. The Resolver will fall back to semantic search over the text, scoped to whatever resource_type hint you provide.10. Versioning, Rate Limits & Errors
Vocabulary versioning
Every response from the Terminology Service carries two headers telling you which OMOP vocabulary release served the request:- Header:
X-Vocab-Release: 2026v1 - Query parameter:
?vocab_release=2026v1 - Path segment in OMOPHub’s legacy path-versioned routes (not used by the FHIR service - use the header for FHIR requests)
CapabilityStatement at /metadata and listed in detail in vocabulary-versions.
Rate limits
FHIR and REST share the same quota pool per API key. Default free-tier limit is 60 requests/minute. Every response includes:POST /v1/fhir/resolve/batch or a 10-entry FHIR Bundle counts as 50 or 10 calls respectively. Plan your quota accordingly.
Error envelopes
The two surfaces return different shapes:400 for bad parameters, 401 for missing/invalid API key, 404 for unknown code or value set, 410 for archived vocabulary releases, 429 for rate-limit exhaustion, 5xx for server errors.
11. Next Steps
FHIR → OMOP Standardization
The complete end-to-end workflow from FHIR-coded data to OMOP CDM tables.
Interoperability Test Matrix
Supported FHIR operations, tested clients, and auth patterns at a glance.
HAPI FHIR Integration
Point your HAPI FHIR server at OMOPHub as a remote terminology service - one config change, no local ATHENA download.
EHR Integration
CDS Hooks, SMART on FHIR apps, sidecar architectures, and real-time vocabulary resolution at the point of care.
AI/LLM Grounding
The Extract → Ground → Reason pattern for using OMOPHub as a vocabulary backbone for clinical LLMs.
Known Limitations
FHIR-specific caveats: implicit ValueSets only, Bundle-shaped XML is best-effort, and what’s on the roadmap.