Back to Changelog
Featurev1.6.0

FHIR Resolver: One Call from FHIR Coding to OMOP CDM

New FHIR Resolver endpoints translate FHIR Codings and CodeableConcepts to OMOP standard concepts and CDM target tables in a single composite call. Available in the Python and R SDKs.

The new FHIR Resolver collapses the multi-step OMOPHub call sequence - get_concept_by_codemappingsvalidate → semantic search fallback - into a single composite endpoint. Pass a FHIR Coding (or CodeableConcept), get back the source concept, the OMOP standard concept, the relationship type, and the CDM target table the resource belongs in. Optional Phoebe recommendations and mapping-quality signals are one flag away. Built for FHIR-to-OMOP ETL pipelines that don't want to stitch four API calls together for every coded field.

Resolve a Single FHIR Coding

Translate a FHIR Coding (system URI + code) to an OMOP standard concept and CDM target table:

# Python
result = client.fhir.resolve(
    system="http://snomed.info/sct",
    code="44054006",
    resource_type="Condition",
)
res = result["resolution"]
print(res["standard_concept"]["concept_name"])  # "Type 2 diabetes mellitus"
print(res["target_table"])                       # "condition_occurrence"
print(res["mapping_type"])                       # "direct"
# R
result <- client$fhir$resolve(
  system = "http://snomed.info/sct",
  code = "44054006",
  resource_type = "Condition"
)
res <- result$resolution
cat(res$standard_concept$concept_name)  # "Type 2 diabetes mellitus"
cat(res$target_table)                    # "condition_occurrence"
cat(res$mapping_type)                    # "direct"

Non-standard codes (ICD-10-CM, ICD-9-CM, and other classifications) are automatically traversed to their standard equivalents - no separate mappings call needed. When no structured code is available, pass display text and the resolver falls back to semantic search.

Batch Resolution

Resolve up to 100 codings in a single call. Failed items are reported inline so one bad code doesn't take down the batch:

# Python
result = client.fhir.resolve_batch([
    {"system": "http://snomed.info/sct", "code": "44054006"},
    {"system": "http://loinc.org", "code": "2339-0"},
    {"system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "197696"},
])

print(result["summary"])  # {"total": 3, "resolved": 3, "failed": 0}

for item in result["results"]:
    if "resolution" in item:
        res = item["resolution"]
        print(f"{res['source_concept']['concept_code']} -> {res['standard_concept']['concept_name']} -> {res['target_table']}")
    else:
        print(f"Failed: {item['error']['code']} - {item['error']['message']}")
# R
result <- client$fhir$resolve_batch(list(
  list(system = "http://snomed.info/sct", code = "44054006"),
  list(system = "http://loinc.org", code = "2339-0"),
  list(system = "http://www.nlm.nih.gov/research/umls/rxnorm", code = "197696")
))

cat(sprintf("Total: %d, Resolved: %d, Failed: %d\n",
  result$summary$total, result$summary$resolved, result$summary$failed))

for (item in result$results) {
  if (!is.null(item$resolution)) {
    res <- item$resolution
    cat(sprintf("%s -> %s -> %s\n",
      res$source_concept$concept_code,
      res$standard_concept$concept_name,
      res$target_table))
  } else {
    cat(sprintf("Failed: %s - %s\n", item$error$code, item$error$message))
  }
}

CodeableConcept Resolution

A FHIR CodeableConcept can carry multiple codings for the same concept across different vocabularies. The resolver picks the best match using the OHDSI vocabulary preference order (SNOMED > RxNorm > LOINC > CVX > ICD-10) and returns the alternatives ranked alongside it:

# Python
result = 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",
)

best = result["best_match"]["resolution"]
print(best["source_concept"]["vocabulary_id"])  # "SNOMED" (preferred)
print(best["target_table"])                     # "condition_occurrence"

for alt in result["alternatives"]:
    print(f"  Alt: {alt['resolution']['source_concept']['vocabulary_id']}")
# R
result <- client$fhir$resolve_codeable_concept(
  coding = list(
    list(system = "http://snomed.info/sct", code = "44054006"),
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "E11.9")
  ),
  resource_type = "Condition"
)

best <- result$best_match$resolution
cat(best$source_concept$vocabulary_id)  # "SNOMED" (preferred)
cat(best$target_table)                   # "condition_occurrence"

for (alt in result$alternatives) {
  cat(sprintf("  Alt: %s\n", alt$resolution$source_concept$vocabulary_id))
}

When no coding resolves, pass the text field and the resolver falls back to semantic search on the human-readable label.

Install or Upgrade

# Python
pip install --upgrade omophub
# R
install.packages("omophub")

Resources