Skip to main content

Overview

OMOPHub exposes its vocabulary data through a standards-compliant FHIR Terminology Service at /fhir/{r4,r5,r6}/. Any FHIR-compatible client (HAPI FHIR, Firely, etc.) can query OMOP concepts using standard FHIR operations. This is NOT a full FHIR server - it serves terminology resources only (CodeSystem, ConceptMap, ValueSet). For clinical resources, continue using your FHIR server and point it at OMOPHub for terminology resolution.

Endpoints

OperationPathDescription
MetadataGET /fhir/{version}/metadataCapabilityStatement describing server capabilities
CodeSystem searchGET /fhir/{version}/CodeSystem?url={uri}Discover support for a canonical system URI (e.g. http://loinc.org). Omit url to list all supported CodeSystems
CodeSystem instance readGET /fhir/{version}/CodeSystem/{id}Fetch a single CodeSystem by FHIR id (stub id like loinc or release id like omop-v20250827)
$lookupGET|POST /fhir/{version}/CodeSystem/$lookupConcept details by code
$validate-codeGET|POST /fhir/{version}/CodeSystem/$validate-codeCheck if a code is valid in a CodeSystem
$translateGET|POST /fhir/{version}/ConceptMap/$translateTranslate between vocabularies
$expandGET|POST /fhir/{version}/ValueSet/$expandExpand a ValueSet to list matching codes
ValueSet searchGET /fhir/{version}/ValueSet?url={uri}Preflight check for ValueSet support before calling $expand / $validate-code
ValueSet/$validate-codeGET|POST /fhir/{version}/ValueSet/$validate-codeCheck if a code is a member of a ValueSet
$subsumesGET|POST /fhir/{version}/CodeSystem/$subsumesTest subsumption between concepts
$find-matchesGET|POST /fhir/{version}/CodeSystem/$find-matchesFind concepts by property criteria
$closurePOST /fhir/{version}/ConceptMap/$closureCompute subsumption closure for a concept set
BatchPOST /fhir/{version}/Execute multiple GET operations in one request

OMOPHub Extensions

These operations and properties are OMOPHub-specific and not part of the FHIR Terminology Service specification.
ExtensionPath / ParameterDescription
$diffGET|POST /fhir/{version}/CodeSystem/$diffCompare concepts between vocabulary versions
Phoebe recommendations$lookup?property=recommendedClinically-related concept recommendations from OMOPHub’s Phoebe concept-set recommendation engine. See $lookup
target-table product property$translate response product[]Each $translate match includes the OMOP CDM destination table (condition_occurrence, drug_exposure, measurement, …) - saves a separate domain lookup for ETL pipelines. See $translate
Per-vocabulary CodeSystem stubsGET /CodeSystem/{stub-id}Thin FHIR-conformant CodeSystem stubs (one per canonical system URI) for clients that expect per-vocab discovery rather than OMOPHub’s unified omnibus CodeSystem. See CodeSystem search

FHIR Versions

All four FHIR versions are supported from the same endpoint:
  • /fhir/r4/ - FHIR R4 (4.0.1) - US Cures Act, EHDS mandate
  • /fhir/r4b/ - FHIR R4B (4.3.0)
  • /fhir/r5/ - FHIR R5 (5.0.0)
  • /fhir/r6/ - FHIR R6 (6.0.0)
  • /fhir/ - Defaults to R4

Authentication

OMOPHub supports two authentication methods for FHIR operations. Both use the same API key (oh_xxx) from your dashboard and share the same rate-limit and metering pool.

Bearer token (default)

Pass the API key directly in the Authorization header:
curl "https://fhir.omophub.com/fhir/r4/metadata" \
  -H "Authorization: Bearer oh_your_api_key"
This is what curl, Postman, the OMOPHub SDKs, and any FHIR client that lets you configure a static bearer token should use.

OAuth2 client_credentials (for Spring-based clients)

FHIR clients built on Spring Security OAuth2 - HAPI FHIR JPA Starter, EHRbase, most Java/Kotlin Spring Boot stacks - expect to obtain a Bearer token from an OAuth2 token endpoint. OMOPHub exposes a minimal RFC 6749 client_credentials shim at https://fhir.omophub.com/oauth2/token:
curl -X POST "https://fhir.omophub.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=oh_your_api_key"
The response echoes the API key as the access_token ({"access_token": "oh_...", "token_type": "Bearer", "expires_in": 31536000}). Both client_secret_basic (HTTP Basic auth header, Spring’s default) and client_secret_post (form body) authentication methods are accepted. client_secret is not validated - the client_id is the sole credential. Supply any non-empty placeholder for client_secret to satisfy Spring’s config schema. For full walkthroughs, see the HAPI FHIR and EHRbase / openEHR integration guides.

Content Type

OMOPHub responds with Content-Type: application/fhir+json by default and serves FHIR XML on request. Content negotiation follows RFC 7231 §5.3.2 with two OMOPHub-specific rules:
  • JSON wins on ties. When a client advertises both JSON and XML at the same q-value (HAPI FHIR’s default client sends Accept: application/fhir+xml;q=1.0, application/fhir+json;q=1.0, ...), OMOPHub returns JSON. XML is served only when XML is strictly preferred - i.e. the client’s highest-scoring XML media type has a higher q-value than its highest-scoring JSON media type.
  • Wildcards are honored. */* and application/* count as matches at their advertised q-value, so a client sending Accept: */* receives JSON (the default), and a client sending Accept: application/*, application/fhir+xml;q=0.5 also receives JSON (the wildcard matches JSON at q=1.0, which beats the explicit XML at q=0.5).

Requesting XML explicitly

Send Accept: application/fhir+xml (with no JSON at equal or higher q-value):
curl "https://fhir.omophub.com/fhir/r4/CodeSystem/\$lookup?\
system=http://snomed.info/sct&code=44054006" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/fhir+xml"
Response (abbreviated):
<?xml version="1.0" encoding="UTF-8"?>
<Parameters xmlns="http://hl7.org/fhir">
  <parameter>
    <name value="system"/>
    <valueUri value="http://snomed.info/sct"/>
  </parameter>
  <parameter>
    <name value="code"/>
    <valueCode value="44054006"/>
  </parameter>
  <parameter>
    <name value="display"/>
    <valueString value="Type 2 diabetes mellitus"/>
  </parameter>
</Parameters>

XML support matrix

OMOPHub’s XML serializer is clean for single-resource responses and best-effort for Bundle-shaped responses. Request JSON for the Bundle-shaped endpoints unless you have a specific reason to need XML.
EndpointResponse shapeXML status
GET /metadataCapabilityStatement✅ Clean XML
GET /CodeSystem/$lookupParameters✅ Clean XML
GET /CodeSystem/$validate-codeParameters✅ Clean XML
GET /CodeSystem/$subsumesParameters✅ Clean XML
GET /CodeSystem/$find-matchesParameters✅ Clean XML
GET /CodeSystem/$diffParameters✅ Clean XML
GET /ConceptMap/$translateParameters✅ Clean XML
POST /ConceptMap/$closureConceptMap✅ Clean XML
GET /ValueSet/$expandValueSet✅ Clean XML
GET /ValueSet/$validate-codeParameters✅ Clean XML
GET /CodeSystem/{id} (instance read)CodeSystem✅ Clean XML
Handler-emitted 4xx / 5xx errorsOperationOutcome✅ Clean XML
Pre-auth errors (401 missing API key)OperationOutcome⚠️ JSON-only (see below)
GET /CodeSystem (search-type, with or without ?url=)Bundle⚠️ Request JSON
GET /ValueSet?url=... (search-type)Bundle⚠️ Request JSON
POST / (batch Bundle)Bundle⚠️ Request JSON
Bundle-shaped responses should be requested as JSON. The XML serializer produces malformed output for nested resources inside a Bundle.entry[].resource (the inner resource’s type is not used as the wrapping element name). The three Bundle-shaped endpoints above are all discovery / batch operations - FHIR clients calling them from code already prefer JSON, so this is not a common path in practice. Request JSON for those endpoints explicitly, or let content negotiation pick JSON on a tie (the default HAPI / Firely / EHRbase client behavior).
Pre-auth error responses come back as JSON even when XML was requested. When a request fails authentication (401, missing or invalid API key) or exhausts the monthly quota (429), the FHIR-scoped error middleware rewrites the response into an OperationOutcome resource but emits it as JSON regardless of Accept. The response body is still valid FHIR OperationOutcome - HAPI, Firely, and most FHIR clients parse it correctly - it’s only the content-type header and wire format that don’t honor the XML preference. Handler-emitted errors (404 unknown code, 400 bad parameter, 403 restricted vocab, 500 internal) do respect the Accept header and return XML.
Errors on any /fhir/* path - including auth failures, missing routes, and unsupported operations - are returned as FHIR OperationOutcome resources in whichever format the client negotiated (JSON by default, XML when requested). The generic REST JSON envelope ({"success": false, "error": {...}}) is never used on FHIR routes.

CodeSystem URLs

OMOPHub serves CodeSystem resources at two equivalent URL shapes:
  1. Canonical per-vocabulary URIs - e.g. http://snomed.info/sct, http://loinc.org, http://www.nlm.nih.gov/research/umls/rxnorm. These resolve to per-vocabulary stubs (content: "not-present") and are what FHIR clients like HAPI and EHRbase use for discovery. Every URI in the Supported Vocabularies table is a valid CodeSystem URL.
  2. Unified OMOP omnibus URL - https://fhir-terminology.ohdsi.org. A single CodeSystem covering all 130+ OMOP vocabularies by concept_id. This is the OHDSI community canonical URL; use it when you want to address concepts by their OMOP internal ID rather than by vocabulary-specific code.
Both shapes resolve to the same underlying concept data. Operations like $lookup and $validate-code dispatch on the system / url parameter, so you can use whichever matches your client’s expectations. Codes resolved via OMOPHub are interchangeable with codes from any other FHIR server serving OMOP vocabularies. Discover the full list of supported URLs with CodeSystem search or the metadata endpoint.

Supported Vocabularies

Query CodeSystem search without a url parameter for the live list, or see the current registry below. Each row is a valid CodeSystem URL (usable in $lookup, $validate-code, $translate, etc.) with a corresponding stub id (usable in GET /CodeSystem/{id}).
FHIR System URIStub idOMOP vocabulary_idDescription
https://fhir-terminology.ohdsi.orgomop-v{release}(unified)All OMOP concepts by concept_id
http://snomed.info/sctsnomedSNOMEDSNOMED CT (International Edition)
http://loinc.orgloincLOINCLab tests, measurements
http://www.nlm.nih.gov/research/umls/rxnormrxnormRxNormDrugs (US)
http://hl7.org/fhir/sid/icd-10icd-10ICD10ICD-10 (WHO)
http://hl7.org/fhir/sid/icd-10-cmicd-10-cmICD10CMICD-10-CM (US)
http://hl7.org/fhir/sid/icd-10-cnicd-10-cnICD10CNICD-10 Chinese Edition
http://hl7.org/fhir/sid/icd-10-gmicd-10-gmICD10GMICD-10 German Modification
http://hl7.org/fhir/sid/icd-10-pcsicd-10-pcsICD10PCSICD-10 Procedure Coding System
http://hl7.org/fhir/sid/icd-9-cmicd-9-cmICD9CMICD-9-CM (legacy)
http://hl7.org/fhir/sid/icd-9icd-9-procICD9ProcICD-9 Procedure Codes
http://hl7.org/fhir/sid/icd-9-cnicd-9-proc-cnICD9ProcCNICD-9 Chinese Edition Procedures
http://hl7.org/fhir/sid/icd-o-3icd-o-3ICDO3Oncology morphology
http://hl7.org/fhir/sid/cvxcvxCVXVaccines
http://hl7.org/fhir/sid/ndcndcNDCDrug products (US)
http://unitsofmeasure.orgucumUCUMUnits of measure
urn:iso:std:iso:11073:10101mdcMDCISO/IEEE 11073 Medical Device Codes
http://www.whocc.no/atcatcATCDrug classification (WHO)
http://fdasis.nlm.nih.govuniiUNIISubstance identifiers (FDA)
http://va.gov/terminology/medrtmed-rtMED-RTMedication reference (VA)
http://www.nlm.nih.gov/research/umls/hcpcshcpcsHCPCSProcedures (US outpatient)

Three Access Patterns

1. By vocabulary-specific code (type-level, most common):
GET /fhir/r4/CodeSystem/$lookup?system=http://snomed.info/sct&code=44054006
Pattern 1 is what ETL developers and FHIR clients need most of the time - they have FHIR-native codes from source systems and want to resolve them against the canonical vocabulary URI. 2. By OMOP concept_id (via the unified omnibus URL):
GET /fhir/r4/CodeSystem/$lookup?system=https://fhir-terminology.ohdsi.org&code=201826
Pattern 2 is useful when you already have an OMOP concept_id (from a previous $lookup, from an ETL pipeline, or from a $translate result) and want to look it up without maintaining a vocabulary-ID round-trip. 3. By CodeSystem instance ID (instance-level):
GET /fhir/r4/CodeSystem/loinc/$lookup?code=2951-2
GET /fhir/r4/CodeSystem/omop-v20250827/$lookup?code=201826
Pattern 3 uses the CodeSystem’s FHIR id from CodeSystem search - either a per-vocab stub id (loinc, snomed, …) or a release-specific unified id (omop-v20250827). This is what HAPI FHIR emits after its discovery phase, and what you get back from CodeSystem instance read.

Designations (Synonyms)

$lookup responses include designation entries for concept synonyms. Each designation has a language code and a value string. These are the alternate names from the OMOP concept_synonym table.

Property Naming

Standard OMOP properties use kebab-case names for interoperability. Relationship properties use their OMOP relationship names (e.g., Maps to, Is a).

Standard OMOP properties

PropertyTypeDescription
concept-idintegerOMOP concept_id
source-concept-codeCodingOriginal source code as FHIR Coding (system URI + code)
concept-namestringOMOP concept_name
domain-idcodeOMOP domain (Condition, Drug, Measurement, etc.)
vocabulary-idcodeOMOP vocabulary_id (SNOMED, ICD10CM, LOINC, etc.)
concept-class-idcodeOMOP concept_class_id (Clinical Finding, Disorder, etc.)
standard-conceptcodeS=Standard, C=Classification, NS=Non-standard
inactivebooleanWhether concept is inactive
valid-start-datedateDate concept became valid
valid-end-datedateDate concept becomes invalid
invalid-reasoncodeD=Deleted, U=Updated (omitted if valid)

Relationship properties

PropertyTypeDescription
Maps toCodingStandard concept mapping
Mapped fromCodingInverse of Maps to
Is aCodingParent/supertype
SubsumesCodingChild/subtype
Use the property query parameter on $lookup to request specific properties (e.g. property=concept-id&property=domain-id). When omitted, all standard properties are returned.

Metering

FHIR operations count against the same API-call quota as REST API calls - there is no separate FHIR pool. Two exceptions to the “1 operation = 1 call” rule:
  • Batch Bundles are metered per entry. A 50-entry batch counts as 50 calls.
  • Resolver batch (POST /v1/fhir/resolve/batch) is also metered per coding, matching FHIR batch behavior.
Rate-limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) are included on every response.

See Also