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.
OMOPHub’s FHIR-to-OMOP mappings follow the HL7 FHIR-to-OMOP Implementation Guide (Informative 1, v1.0.0), including its administrative ConceptMaps (see Administrative ConceptMaps below).

Endpoints

OMOPHub Extensions

These operations and properties are OMOPHub-specific and not part of the FHIR Terminology Service specification.

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:
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:
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):
Response (abbreviated):

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.
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}).

Administrative ConceptMaps

Some FHIR resources carry administrative/structural codes that have no OMOP vocabulary-table representation — gender, encounter class, condition status, immunization route, and the like. OMOPHub resolves these via the HL7 FHIR-to-OMOP IG ConceptMaps. Both the FHIR Resolver (POST /v1/fhir/resolve) and ConceptMap/$translate accept these source systems:
The IG relationship is preserved: an equivalent mapping is an exact match, while source-is-broader-than-target / source-is-narrower-than-target are approximate. The resolver surfaces this in relationship_id (and a mapping_note advisory); $translate returns the corresponding FHIR equivalence/relationship. A few codes (e.g. admit-source other) are intentionally unmapped in the IG and resolve to concept_id 0.
allergy-intolerance-category codes map under both AllergyType and IntoleranceType — the resolver returns the allergy interpretation as the primary match and the intolerance interpretation as an alternative. Disambiguate using AllergyIntolerance.type.

Three Access Patterns

1. By vocabulary-specific code (type-level, most common):
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):
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):
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

Relationship properties

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