> ## 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.

# R SDK - FHIR Resolver for OMOP concepts

> Resolve FHIR Coding and CodeableConcept values to OMOP standard concepts from the OMOPHub R SDK with one call - ideal for R-based OHDSI pipelines.

## Resolve a Single FHIR Coding

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

```r theme={null}
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"
```

## Resolve a Non-Standard Code (Maps to Traversal)

ICD-10-CM and other classification codes are automatically mapped to their standard equivalents:

```r theme={null}
result <- client$fhir$resolve(
  system = "http://hl7.org/fhir/sid/icd-10-cm",
  code = "E11.9"
)
res <- result$resolution
cat(res$source_concept$vocabulary_id)    # "ICD10CM"
cat(res$standard_concept$vocabulary_id)  # "SNOMED"
cat(res$mapping_type)                    # "mapped"
cat(res$relationship_id)                 # "Maps to"
cat(res$target_table)                    # "condition_occurrence"
```

## Text-Only Resolution (Semantic Search Fallback)

When no structured code is available, pass the display text for semantic search:

```r theme={null}
result <- client$fhir$resolve(
  display = "Blood Sugar",
  resource_type = "Observation"
)
res <- result$resolution
cat(res$standard_concept$concept_name)  # "Glucose [Mass/volume] in Blood"
cat(res$mapping_type)                    # "semantic_match"
cat(res$similarity_score)                # 0.91
cat(res$target_table)                    # "measurement"
```

## Skip URI Resolution with vocabulary\_id

If you already know the OMOP vocabulary, bypass the URI lookup:

```r theme={null}
result <- client$fhir$resolve(
  vocabulary_id = "ICD10CM",
  code = "E11.9"
)
```

## Include Phoebe Recommendations

Get related concepts for phenotype development alongside the resolution:

```r theme={null}
result <- client$fhir$resolve(
  system = "http://snomed.info/sct",
  code = "44054006",
  include_recommendations = TRUE,
  recommendations_limit = 5L
)
for (rec in result$resolution$recommendations) {
  cat(sprintf("  %s (%s) via %s\n",
    rec$concept_name, rec$domain_id, rec$relationship_id))
}
```

## Include Mapping Quality Signal

Assess whether a resolution needs manual review:

```r theme={null}
result <- client$fhir$resolve(
  system = "http://snomed.info/sct",
  code = "44054006",
  include_quality = TRUE
)
cat(result$resolution$mapping_quality)  # "high", "medium", "low", or "manual_review"
```

## Batch Resolution

Resolve up to 100 codings in a single call. Failed items are reported inline:

```r theme={null}
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))
  }
}
```

Apply shared options to the entire batch:

```r theme={null}
result <- client$fhir$resolve_batch(
  list(list(system = "http://snomed.info/sct", code = "44054006")),
  resource_type = "Condition",
  include_quality = TRUE
)
```

## CodeableConcept Resolution

Resolve a FHIR `CodeableConcept` with multiple codings. The resolver picks the best match per OHDSI vocabulary preference (SNOMED > RxNorm > LOINC > CVX > ICD-10):

```r theme={null}
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"

# Other resolved codings ranked by preference
for (alt in result$alternatives) {
  cat(sprintf("  Alt: %s\n", alt$resolution$source_concept$vocabulary_id))
}
```

Falls back to the `text` field via semantic search when no coding resolves:

```r theme={null}
result <- client$fhir$resolve_codeable_concept(
  coding = list(list(system = "http://loinc.org", code = "99999-9")),
  text = "Blood Sugar",
  resource_type = "Observation"
)
# best_match resolved via semantic search; failed coding in unresolved
```

## Tibble Output for Batch

Pass `as_tibble = TRUE` to `resolve_batch()` to get a flat [`tibble`](https://tibble.tidyverse.org/) with one row per input coding - ready to pipe into `dplyr` / `tidyr`:

```r theme={null}
library(dplyr)

tbl <- client$fhir$resolve_batch(
  list(
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "E11.9"),
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "I10"),
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "J45.909")
  ),
  as_tibble = TRUE
)

tbl |>
  filter(status == "resolved") |>
  select(source_code, standard_concept_name, target_table)
#> # A tibble: 3 x 3
#>   source_code standard_concept_name       target_table
#>   <chr>       <chr>                       <chr>
#> 1 E11.9       Type 2 diabetes mellitus    condition_occurrence
#> 2 I10         Essential hypertension      condition_occurrence
#> 3 J45.909     Asthma                      condition_occurrence
```

The tibble columns are: `source_system`, `source_code`, `source_concept_id`, `source_concept_name`, `standard_concept_id`, `standard_concept_name`, `standard_vocabulary_id`, `domain_id`, `target_table`, `mapping_type`, `similarity_score`, `status`, and `status_detail`.

Failed rows are kept in-place with `status = "failed"` and the API error text in `status_detail` - do not silently drop them:

```r theme={null}
tbl |> filter(status == "failed") |> select(source_code, status_detail)
```

The batch summary (`total` / `resolved` / `failed`) is attached as an attribute:

```r theme={null}
attr(tbl, "summary")
#> $total   [1] 3
#> $resolved [1] 3
#> $failed   [1] 0
```

The default `as_tibble = FALSE` still returns the list-shaped `list(results, summary)` - existing code keeps working unchanged.

## Standalone Wrappers

The R6 interface is always available:

```r theme={null}
client$fhir$resolve(system = "http://snomed.info/sct", code = "44054006")
```

For pipe-friendly use, three standalone wrappers forward to the same R6 methods and take the client as their first argument:

```r theme={null}
library(dplyr)

# Equivalent to the R6 form
client |>
  fhir_resolve(
    system = "http://snomed.info/sct",
    code = "44054006",
    resource_type = "Condition"
  )

tbl <- client |>
  fhir_resolve_batch(
    codings = list(
      list(system = "http://snomed.info/sct", code = "44054006"),
      list(system = "http://loinc.org", code = "2339-0")
    ),
    as_tibble = TRUE
  )

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"
  )
```

Pick whichever form reads better for the surrounding code - both are fully supported.

## FHIR Client Interop

`omophub_fhir_url()` returns the OMOPHub FHIR Terminology Service base URL for a given FHIR version (`"r4"` default, plus `"r4b"`, `"r5"`, `"r6"`). Use it with [`httr2`](https://httr2.r-lib.org/) or [`fhircrackr`](https://cran.r-project.org/package=fhircrackr) when you want raw FHIR `Parameters` / `Bundle` responses instead of OMOPHub's Concept Resolver envelope.

```r theme={null}
library(httr2)

# Call CodeSystem/$lookup directly against OMOPHub's FHIR endpoint
resp <- request(omophub_fhir_url()) |>
  req_url_path_append("CodeSystem/$lookup") |>
  req_url_query(
    system = "http://snomed.info/sct",
    code = "44054006"
  ) |>
  req_headers(Authorization = paste("Bearer", Sys.getenv("OMOPHUB_API_KEY"))) |>
  req_perform()

params <- resp_body_json(resp)
# Raw FHIR Parameters resource with the concept display, designations, etc.
```

R5/R6 endpoints work the same way:

```r theme={null}
omophub_fhir_url("r5")
#> "https://fhir.omophub.com/fhir/r5"
```

<Note>
  Use `client$fhir$resolve()` (or `fhir_resolve()`) when you want OMOP-enriched answers - standard concept, CDM target table, mapping quality. Use `omophub_fhir_url()` + `httr2` when you need raw FHIR responses for FHIR-native tooling.
</Note>

## Error Handling

The resolver signals errors using standard R conditions:

```r theme={null}
tryCatch(
  client$fhir$resolve(
    system = "http://www.ama-assn.org/go/cpt",
    code = "99213"
  ),
  omophub_api_error = function(e) {
    cat(e$status_code)  # 403
    cat(e$message)       # "vocabulary_restricted"
  }
)
```

| Error Code          | HTTP | Cause                                                                       |
| ------------------- | ---- | --------------------------------------------------------------------------- |
| `unknown_system`    | 400  | FHIR code system URI not recognized (includes a typo suggestion when close) |
| `missing_input`     | 400  | Neither `(system + code)` / `(vocabulary_id + code)` / `display` provided   |
| `concept_not_found` | 404  | No concept found via lookup or semantic search                              |

<Note>
  See the [FHIR Resolver API Reference](/api-reference/fhir/resolve) for full response schemas and field descriptions.
</Note>
