> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omophub.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Interoperability Test Matrix

> Supported FHIR operations, tested clients, auth patterns, and known integration scenarios for OMOPHub - check compatibility before writing code.

This page documents what OMOPHub supports, what has been tested against real clients, and where the boundaries are. Use it to evaluate compatibility before writing integration code.

## 1. FHIR R4 / R4B / R5 / R6 Terminology Operations

The FHIR Terminology Service is served at `https://fhir.omophub.com/fhir/{r4,r4b,r5,r6}/`. R4 is the default when no version prefix is provided.

| Operation                        | Resource                   |   Status   | Notes                                                                                                                                                                 |
| -------------------------------- | -------------------------- | :--------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `$lookup`                        | CodeSystem                 | Production | GET and POST. Returns OMOP properties (`concept-id`, `domain-id`, `concept-class-id`, `standard-concept`, etc.) and Phoebe recommendations via `property=recommended` |
| `$validate-code`                 | CodeSystem                 | Production | GET and POST. Validates code existence and optional `display` match                                                                                                   |
| `$subsumes`                      | CodeSystem                 | Production | Tests hierarchical subsumption between two concepts in the same system                                                                                                |
| `$find-matches`                  | CodeSystem                 | Production | Semantic property-based matching - OMOPHub's semantic search exposed in a FHIR envelope                                                                               |
| `$translate`                     | ConceptMap                 | Production | GET and POST. Maps between any two vocabularies via OMOP `Maps to` relationships                                                                                      |
| `$closure`                       | ConceptMap                 | Production | Incremental transitive-closure maintenance for streaming concept sets                                                                                                 |
| `$expand`                        | ValueSet                   | Production | Expands implicit ValueSets (`{system}?fhir_vs` and `{system}?fhir_vs=isa/{code}`). Deterministic sort by `concept_id` for stable pagination                           |
| `$validate-code`                 | ValueSet                   | Production | Validates code membership in an implicit ValueSet                                                                                                                     |
| `$diff`                          | CodeSystem                 | Production | **OMOPHub custom** - compares concepts between two OMOP vocabulary releases. No FHIR spec equivalent                                                                  |
| `GET /metadata`                  | CapabilityStatement        | Production | Returns declared resources, operations, and both JSON + XML formats                                                                                                   |
| `GET /metadata?mode=terminology` | TerminologyCapabilities    | Production | Returns the full list of supported `codeSystem` URIs dynamically from the vocab config                                                                                |
| `GET /CodeSystem?url=<uri>`      | CodeSystem (search-type)   | Production | HAPI FHIR's discovery call - returns a `searchset` `Bundle` with a per-vocabulary stub or empty Bundle for unknown URIs                                               |
| `GET /CodeSystem` (list-all)     | CodeSystem (search-type)   | Production | Returns all 20 per-vocab stubs + the unified OMOP omnibus CodeSystem                                                                                                  |
| `GET /CodeSystem/{id}`           | CodeSystem (instance read) | Production | Per-vocab stub IDs (`loinc`, `snomed`, `rxnorm`, …) + release IDs (`omop-v20250827`)                                                                                  |
| `GET /ValueSet?url=<uri>`        | ValueSet (search-type)     | Production | Preflight support check used by HAPI and EHRbase before `$expand` / `$validate-code`                                                                                  |
| `POST /` (Batch Bundle)          | Bundle                     | Production | FHIR batch Bundle with multiple `entry.request` items; metered per entry, not per call                                                                                |

### Not supported

| Capability                                                                | Reason                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FHIR clinical resources (`Patient`, `Condition`, `Observation`, …)        | OMOPHub is a terminology service, not a clinical-data server                                                                                                                                                                                      |
| FHIR REST interactions (`create`, `update`, `delete`, `history`, `patch`) | Read-only vocabulary data - content comes from OHDSI ATHENA releases                                                                                                                                                                              |
| Extensional ValueSet storage                                              | Only implicit ValueSets (`{system}?fhir_vs`). User-defined extensional ValueSets are on the roadmap                                                                                                                                               |
| Full-fidelity XML Bundle serialization                                    | Parameters, OperationOutcome, and single-resource responses serialize to clean FHIR XML. Bundle-shaped responses (search-type, batch) are best-effort - see the [XML support matrix](/api-reference/fhir-terminology/overview#xml-support-matrix) |

## 2. OMOPHub Concept Resolver

Purpose-built REST endpoints for FHIR-to-OMOP ETL pipelines. Returns OMOPHub's native JSON envelope rather than FHIR `Parameters`.

| Endpoint                                 | Description                                                                                                        |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `POST /v1/fhir/resolve`                  | Single `Coding` → OMOP standard concept + domain + CDM target table + mapping type                                 |
| `POST /v1/fhir/resolve/batch`            | Batch resolution, up to 100 codings per request. Metered per coding                                                |
| `POST /v1/fhir/resolve/codeable-concept` | Full `CodeableConcept` resolution with OHDSI vocabulary preference ranking and `text` fallback via semantic search |

See the [FHIR Integration guide](/guides/integration/fhir-integration) for request / response shapes and the [FHIR → OMOP Standardization](/guides/workflows/fhir-to-omop) guide for the end-to-end pipeline pattern.

## 3. Supported FHIR Code System URIs

OMOPHub maps 19 canonical FHIR system URIs + 1 OMOP omnibus URL (total: **20 CodeSystems** served via the Terminology Service). Source of truth is `apps/api/src/config/fhir-vocabularies.ts` - use `GET /fhir/r4/metadata?mode=terminology` to get the current list from the live service.

| FHIR System URI                               | OMOP `vocabulary_id` |
| --------------------------------------------- | -------------------- |
| `http://snomed.info/sct`                      | `SNOMED`             |
| `http://loinc.org`                            | `LOINC`              |
| `http://www.nlm.nih.gov/research/umls/rxnorm` | `RxNorm`             |
| `http://hl7.org/fhir/sid/icd-10`              | `ICD10`              |
| `http://hl7.org/fhir/sid/icd-10-cm`           | `ICD10CM`            |
| `http://hl7.org/fhir/sid/icd-10-pcs`          | `ICD10PCS`           |
| `http://hl7.org/fhir/sid/icd-10-cn`           | `ICD10CN`            |
| `http://hl7.org/fhir/sid/icd-10-gm`           | `ICD10GM`            |
| `http://hl7.org/fhir/sid/icd-9-cm`            | `ICD9CM`             |
| `http://hl7.org/fhir/sid/icd-9`               | `ICD9Proc`           |
| `http://hl7.org/fhir/sid/icd-9-cn`            | `ICD9ProcCN`         |
| `http://hl7.org/fhir/sid/icd-o-3`             | `ICDO3`              |
| `http://hl7.org/fhir/sid/cvx`                 | `CVX`                |
| `http://hl7.org/fhir/sid/ndc`                 | `NDC`                |
| `http://www.nlm.nih.gov/research/umls/hcpcs`  | `HCPCS`              |
| `http://www.whocc.no/atc`                     | `ATC`                |
| `http://unitsofmeasure.org`                   | `UCUM`               |
| `urn:iso:std:iso:11073:10101`                 | `MDC`                |
| `http://fdasis.nlm.nih.gov`                   | `UNII`               |
| `http://va.gov/terminology/medrt`             | `MED-RT`             |
| `https://fhir-terminology.ohdsi.org`          | OMOP unified omnibus |

<Note>
  If your FHIR data uses a system URI not in this list, the Resolver returns a `400` error with an `unknown_system` code and a suggested match from the levenshtein-closest URI. You can bypass URI resolution by passing `vocabulary_id` directly instead of `system`.
</Note>

## 4. Tested Client Scenarios

The following integration patterns have been validated against real clients.

### HAPI FHIR Server

HAPI FHIR's `RemoteTerminologyServiceValidationSupport` can delegate validation to OMOPHub. **Two deployment shapes** work, depending on whether you're using the stock JPA Starter or building a custom Spring Boot HAPI app:

**HAPI JPA Starter (`hapiproject/hapi:*` Docker image)** - the stock image's YAML schema does not expose a Bearer-token field for remote terminology services (`AppProperties.RemoteSystem` in the starter only binds `system` and `url`; `TerminologyConfig` never calls `addClientInterceptor`). Use a reverse-proxy pattern:

* Stand up nginx / Caddy / cloud gateway in front of OMOPHub
* Configure the proxy to inject `Authorization: Bearer $OMOPHUB_CLIENT_ID` on every upstream request
* Point HAPI's remote terminology URL at the proxy
* Config key: `hapi.fhir.remote_terminology_service.<name>.{system,url}` - note that older blog posts reference `hapi.fhir.validation.remote_terminology_service_urls`, which is a different property path that the JPA Starter silently ignores

**Custom Spring Boot HAPI app** - use the HAPI FHIR library directly and attach a `BearerTokenAuthInterceptor` programmatically:

```java theme={null}
@Bean
public IValidationSupport omophubTerminologyService(FhirContext ctx) {
  RemoteTerminologyServiceValidationSupport remote =
      new RemoteTerminologyServiceValidationSupport(ctx);
  remote.setBaseUrl("https://fhir.omophub.com/fhir/r4");
  remote.addClientInterceptor(
      new BearerTokenAuthInterceptor("oh_your_api_key"));
  return remote;
}
```

* **Verified operations:** `$lookup`, `$validate-code`, CodeSystem search-type (discovery call), `GET /metadata`
* **Verified against:** HAPI FHIR 8.8.0
* **See:** [HAPI FHIR Integration](/guides/integration/hapi-fhir) for full config, verification checklist, and the `CodeSystem is unknown` troubleshooting path

### EHRbase (openEHR)

EHRbase delegates template-terminology validation to an external FHIR R4 server via Spring's `RemoteTerminologyServiceValidationSupport`. Unlike HAPI JPA Starter, EHRbase supports OAuth2 `client_credentials` natively via `spring.security.oauth2.client.*` config blocks - and OMOPHub exposes a compatible token endpoint at `POST /oauth2/token`.

* **Connection:** Configure `validation.external-terminology.provider.omophub.url` to `https://fhir.omophub.com/fhir/r4/`
* **Auth:** OAuth2 `client_credentials` - `spring.security.oauth2.client.registration.omophub.client-id` set to your OMOPHub API key, `client-secret` to any non-empty placeholder (OMOPHub's shim doesn't validate it)
* **Verified operations:** ValueSet search-type (discovery), `$expand`, `$validate-code`, OAuth2 token exchange
* **Verified against:** `ehrbase/ehrbase:next` with `application.yml` overlay
* **End-to-end test result:** invalid SNOMED code submitted via composition → EHRbase returns HTTP 422 with `"does not match any option from value set ..."` - the full `template upload → composition commit → $validate-code → reject` loop is live
* **See:** [EHRbase / openEHR Integration](/guides/integration/ehrbase-openehr) for the complete docker-compose and template-binding walkthrough

### Direct REST / curl

All FHIR operations support both GET (query parameters) and POST (FHIR `Parameters` body):

```bash theme={null}
# GET style
curl "https://fhir.omophub.com/fhir/r4/CodeSystem/\$lookup?\
system=http://snomed.info/sct&code=44054006" \
  -H "Authorization: Bearer oh_your_api_key"

# POST style
curl -X POST "https://fhir.omophub.com/fhir/r4/CodeSystem/\$lookup" \
  -H "Authorization: Bearer oh_your_api_key" \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Parameters",
    "parameter": [
      { "name": "system", "valueUri": "http://snomed.info/sct" },
      { "name": "code",   "valueCode": "44054006" }
    ]
  }'
```

### Python SDK

```python theme={null}
import omophub
client = omophub.OMOPHub()

# Concept Resolver
result = client.fhir.resolve(
    system="http://snomed.info/sct",
    code="44054006",
    resource_type="Condition",
)

# Batch resolve up to 100 codings
batch = client.fhir.resolve_batch([
    {"system": "http://snomed.info/sct", "code": "44054006"},
    {"system": "http://loinc.org", "code": "2339-0"},
])

# CodeableConcept with OHDSI vocabulary preference ranking
cc = client.fhir.resolve_codeable_concept(
    coding=[
        {"system": "http://snomed.info/sct",          "code": "44054006"},
        {"system": "http://hl7.org/fhir/sid/icd-10-cm", "code": "E11.9"},
    ],
    resource_type="Condition",
)
```

### R SDK

R6 object-oriented style via `$`-dereferencing:

```r theme={null}
library(omophub)
client <- OMOPHubClient$new(api_key = "oh_your_api_key")

result <- client$fhir$resolve(
  system = "http://snomed.info/sct",
  code   = "44054006",
  resource_type = "Condition"
)
```

### MCP Server (AI agents)

The OMOPHub MCP Server exposes 11 tools to Claude, Cursor, VS Code, and any MCP-compatible AI client. Install with `npx -y @omophub/omophub-mcp`. See [AI Onboarding](/ai/onboarding) and the [MCP Tools Reference](/ai/mcp-tools) for the full tool list.

## 5. Authentication

| Pattern                                       |  Supported | Notes                                                                                                                                                                                                                         |
| --------------------------------------------- | :--------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bearer token (`Authorization: Bearer oh_xxx`) | Production | Primary auth for REST and FHIR endpoints. Transmitted over TLS 1.2+                                                                                                                                                           |
| OAuth2 `client_credentials` (RFC 6749)        | Production | `POST https://fhir.omophub.com/oauth2/token`. Accepts both `client_secret_basic` (Spring Security default) and `client_secret_post`. `client_secret` is not validated - the `client_id` (your API key) is the sole credential |
| API key in query parameter                    |     No     | Header-only for security                                                                                                                                                                                                      |
| No auth / open access                         |     No     | All endpoints require authentication                                                                                                                                                                                          |

The OAuth2 shim is specifically designed to satisfy Spring Security OAuth2 clients (HAPI FHIR custom builds, EHRbase, any Java/Kotlin Spring Boot stack). The returned `access_token` is your API key wrapped in an RFC 6749 envelope - the token exchange is nominal but lets Spring-based clients treat OMOPHub like any other OAuth2-protected resource.

## 6. Content Negotiation

| Content Type            |  Supported | Notes                                                                                                                                                                                                                                                                                                              |
| ----------------------- | :--------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `application/fhir+json` | Production | Default                                                                                                                                                                                                                                                                                                            |
| `application/json`      | Production | Treated as FHIR JSON                                                                                                                                                                                                                                                                                               |
| `application/fhir+xml`  | Production | Served when strictly preferred via `Accept` header q-values. Clean for `Parameters`, `OperationOutcome`, `CapabilityStatement`, `ValueSet`, `ConceptMap`, bare `CodeSystem` reads. **Bundle-shaped responses** (CodeSystem/ValueSet search-type, batch Bundles) are best-effort - request JSON for those endpoints |
| `*/*`                   | Production | Resolves to JSON via wildcard content negotiation                                                                                                                                                                                                                                                                  |

JSON wins on ties. Content negotiation follows RFC 7231 §5.3.2 q-value semantics; HAPI FHIR's default client sends both JSON and XML at q=1.0, so the tie-break selects JSON. See the [Content Type section](/api-reference/fhir-terminology/overview#content-type) of the FHIR Terminology Service overview for the full XML support matrix.

## 7. Rate Limits

FHIR endpoints share the same rate-limit and metering pool as the REST API. Each FHIR operation counts as one API call. Batch and bulk endpoints (`POST /v1/fhir/resolve/batch`, `POST /fhir/r4/` batch Bundles, `/v1/concepts/batch`, `/v1/search/bulk`, and so on) meter per item inside the batch - a 100-item batch counts as 100 calls against your monthly quota.

See [Rate Limits](/api-reference/rate-limit) for the headers (`ratelimit-limit`, `ratelimit-remaining`, `ratelimit-reset`, `ratelimit-window`, `retry-after`) and the contact path for requesting higher limits.

## 8. Vocabulary Coverage

OMOPHub serves the full OHDSI ATHENA vocabulary set except licensed vocabularies.

See [Known Limitations](/guides/production/known-limitations) for the full boundary discussion, and [Vocabulary Releases](/vocabulary-versions) for the current release cadence.
