FHIR Validator CLI: Validate, Transform, And Verify FHIR
Before a FHIR resource ever touches a production system, you need to know it's valid. The FHIR Validator CLI, specifically the validator_cli.jar tool maintained by the HL7 community, is the standard way to check that your resources conform to FHIR profiles, implementation guides, and structural rules before they cause downstream failures. Whether you're building a SMART on FHIR app or preparing for an EHR integration, catching validation errors early saves you from painful debugging later.
The validator works as a standalone Java command-line tool. You point it at a resource (or a bundle of them), specify which profiles or IGs to validate against, and it tells you exactly what's wrong, missing required fields, incorrect code bindings, structural violations, all of it. It can also transform between formats and verify conformance against custom profiles. For teams working with EHR data from systems like Epic, Cerner, or Allscripts, this is a non-negotiable part of the development workflow.
At SoFaaS, we handle the complexity of FHIR-based EHR integration so you don't have to, but understanding how validation works at the resource level makes you a better builder regardless. This guide walks you through downloading and installing the FHIR Validator CLI, running your first validation, working with implementation guides, and using its transform and verification capabilities. By the end, you'll have a practical, working setup for validating FHIR resources from the command line.
Prerequisites and what the validator checks
Before you run a single command with the fhir validator cli, you need a few things in place. The validator is a Java-based tool, so your environment must meet a minimum Java version requirement, and the resources you plan to validate need to be in a format the tool actually accepts. Skipping this setup step is the most common reason teams see confusing errors on their first run, and it wastes time that you could spend building.
What you need before you start
The validator has two hard requirements before you can do anything useful. You need Java 11 or higher installed on your machine, and you need network access to download implementation guides and terminology packages from HL7's package registries (or a local cache if you're working in a restricted environment). Beyond those two, everything else is optional but worth knowing before you write your first command.
Here's what you need to have ready:
| Item | Required? | Notes |
|---|---|---|
| Java 11+ | Yes | Oracle JDK or OpenJDK both work. Run java -version to confirm. |
| validator_cli.jar | Yes | Download the latest release from the official HL7 FHIR releases repository. |
| FHIR resource files | Yes | JSON (.json) or XML (.xml) files you want to validate. |
| Profile or IG package ID | Situational | Required if validating against a specific implementation guide, e.g., hl7.fhir.us.core#6.0.0. |
| Terminology server access | Optional | Defaults to tx.fhir.org. You can disable this or point it at a local server. |
What the validator actually checks
When you run the tool against a resource, it performs several layers of checks automatically. The structural layer confirms that your resource is well-formed JSON or XML and that all required fields are present according to the base FHIR specification. The profile conformance layer checks that your resource meets any additional constraints defined in the profiles or implementation guides you specify at runtime. These two layers alone catch the vast majority of integration failures before they ever reach a live EHR system.

Profile conformance errors are the most common source of EHR integration rejections, and catching them locally before submission saves hours of painful debugging against a live endpoint.
Beyond structure and profiles, the validator also checks terminology bindings, meaning it verifies that coded values in fields like code, valueCodeableConcept, or system actually exist in the referenced code systems. It also validates reference integrity, checking that internal and external references point to real, resolvable resources within the context you provide.
Supported input formats and resource types
The validator accepts JSON and XML representations of FHIR resources at any scope: individual resources, bundles, or entire directories of files. You can point it at a single file path, a folder, or even a hosted URL. It supports FHIR R2 through R5, and you select the target version explicitly with a runtime flag. If you're working with US Core, Da Vinci, or other major US implementation guides, the validator pulls those packages automatically during the run and applies their constraints as part of the conformance check.
Step 1. Install Java and confirm your environment
The fhir validator cli runs entirely on the Java Virtual Machine, so your first task is confirming that your machine has Java 11 or higher installed and accessible from the command line. If you skip this check and your environment is running an older version, the validator will fail immediately with a cryptic error that has nothing to do with your FHIR resources. Get this right first, and everything else runs cleanly.
Check your current Java version
Open a terminal and run the command below. This single check tells you whether you're ready to move forward or whether you need to install or upgrade first.
java -version
You should see output that looks something like this:
openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+9)
OpenJDK Server VM (build 17.0.9+9, mixed mode, sharing)
The first number in the version string is what matters. If it's 11 or higher, you're good. If it's 8 or lower, or if the command returns a "not found" error, you need to install a compatible version before continuing.
Install Java if you need it
Both Oracle JDK and OpenJDK work with the validator. OpenJDK is free and widely used in healthcare development environments, so it's usually the practical choice. You can install it through your system's package manager.
# macOS (using Homebrew)
brew install openjdk@17
# Ubuntu / Debian Linux
sudo apt-get update && sudo apt-get install -y openjdk-17-jdk
# Windows (using Winget)
winget install Microsoft.OpenJDK.17
After installation on macOS or Linux, run
java -versionagain to confirm the new version is active before moving to the next step.
Confirm JAVA_HOME is set correctly
Some systems install Java without updating the JAVA_HOME environment variable, which causes certain tools to pick up the wrong version. Run the command below to confirm your environment points at the right installation:
echo $JAVA_HOME
If this returns an empty result or an outdated path, set it explicitly by adding export JAVA_HOME=$(java -XshowSettings:property -version 2>&1 | grep java.home | awk '{print $3}') to your shell profile and reloading it. With Java confirmed and JAVA_HOME set, your environment is ready for the validator.
Step 2. Get the validator CLI and run a first check
With Java confirmed, your next task is downloading the fhir validator cli jar file and running it against a real resource. The validator ships as a single self-contained validator_cli.jar file, which means there's no installer, no dependency management, and no PATH configuration required. You download one file and you're ready to work.
Download the latest validator jar
HL7 publishes the validator through their official FHIR release infrastructure. The most reliable way to get the current version is by downloading directly from the official HL7 FHIR build server using curl or wget. Run the command below from the directory where you want to store the jar:
# Using curl
curl -L https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar \
-o validator_cli.jar
# Using wget
wget https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar
Always download from the official HL7 FHIR Core releases page to ensure you have the latest version with up-to-date profile support.
Once the download finishes, confirm the file exists and is not empty by running ls -lh validator_cli.jar. You should see a file that's roughly 70 to 100 MB in size. A file smaller than that usually means the download was interrupted.
Run your first validation
Now point the validator at a FHIR resource file. The simplest invocation takes just a file path and a version flag. The example below validates a Patient resource stored as JSON against FHIR R4:

java -jar validator_cli.jar patient.json -version 4.0.1
The validator will pull the R4 core package on its first run, which takes a moment depending on your connection. After that, it prints a summary of errors, warnings, and information messages directly to the terminal. A clean resource produces output ending with Success: 0 errors, 0 warnings. Any structural problems or missing required fields appear as clearly labeled error lines with the exact path inside the resource where the problem occurs. Run this check on every resource before you move to profile-specific validation in the next step.
Step 3. Validate against the right FHIR version
The -version flag is one of the most important arguments you pass to the fhir validator cli, and it's also one of the most frequently misused. If you specify the wrong FHIR version, the validator loads the wrong core package, applies the wrong structural rules, and gives you results that look valid when the resource may actually break against your target EHR system. Getting the version right is not optional.
Pick the version that matches your integration target
Your version choice needs to match the FHIR version that the receiving system expects, not the version you happen to be most familiar with. Epic, for example, supports R4 (4.0.1) for most current API endpoints, while some older Cerner environments still operate on DSTU2. Check the implementation guide or the EHR vendor documentation to confirm which version applies before you run anything.
The table below shows the version strings you pass directly to the -version flag:
| FHIR Release | Version String | Common Use Case |
|---|---|---|
| DSTU2 | 1.0.2 |
Legacy Cerner and older integrations |
| STU3 | 3.0.2 |
Some payer APIs and older systems |
| R4 | 4.0.1 |
Current standard for most EHR APIs |
| R4B | 4.3.0 |
Newer profiles and certain IGs |
| R5 | 5.0.0 |
Emerging implementations |
A basic R4 validation command looks like this:
java -jar validator_cli.jar observation.json -version 4.0.1
If you omit the -version flag entirely, the validator defaults to R4, which may silently pass a resource that would fail against an R5 or DSTU2 endpoint.
Handle version-specific package downloads
When you run the validator for a new version for the first time, it downloads the corresponding core package from HL7's package registry and caches it locally in your .fhir directory. This download happens once and subsequent runs use the cache, so the first execution takes longer than expected. If you work in a network-restricted environment, you can pre-load packages manually by placing them in ~/.fhir/packages before your first run, and the validator will use the local cache instead of hitting the registry.
Step 4. Validate against profiles and IGs
Base FHIR structural validation is a starting point, not a finish line. Most EHR integrations require your resources to conform to specific implementation guides (IGs) like US Core or Da Vinci, which add constraints well beyond what the base specification enforces. The fhir validator cli handles this through two flags you combine on the same command: -ig to load a package and -profile to point at the specific profile URL you want to validate against.
Load an implementation guide with -ig
The -ig flag tells the validator which IG package to pull and apply during the run. You reference packages by their canonical package ID and version, using the same identifiers you'd find on packages.fhir.org. The validator fetches and caches the package automatically on first use.
java -jar validator_cli.jar patient.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0
You can load multiple IGs in one command by repeating the -ig flag. This is useful when your resource needs to conform to a base IG and an additional use-case-specific guide at the same time:
java -jar validator_cli.jar coverage.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-ig hl7.fhir.us.davinci-pdex#2.0.0
Loading the wrong IG version is a common mistake. Always match the IG version to the one your target EHR endpoint was tested against, not just the latest available.
Validate against a specific profile
Loading an IG makes its profiles available, but the validator does not automatically apply every profile in the package. You also need to pass the canonical URL of the profile you want to enforce using the -profile flag. Without this, the validator only checks base resource structure.

java -jar validator_cli.jar patient.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-profile http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient
The canonical URL for any profile lives in its StructureDefinition.url field, which you can look up in the published IG documentation. If the resource violates a must-support element or a cardinality constraint defined in the profile, the validator reports the exact path and the rule that failed, giving you a clear target to fix.
Step 5. Control terminology validation and privacy
Terminology validation is the part of the fhir validator cli that checks whether coded values in your resources actually exist in the correct code systems, things like SNOMED CT, LOINC, and ICD-10. By default, the validator sends these lookup requests to tx.fhir.org, HL7's public terminology server. That default works fine for most development work, but it becomes a problem when you're validating resources that contain real patient data or when you're operating in a network-restricted environment.
Configure the terminology server
The -tx flag lets you point the validator at a different terminology server, including a local or private instance. If your organization runs its own FHIR terminology service, use that URL here to keep lookups entirely within your network boundary:
java -jar validator_cli.jar observation.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-profile http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab \
-tx https://your-internal-tx-server/fhir
This swap is especially important in staging or pre-production environments where your pipeline processes de-identified data but you still need accurate terminology results without routing anything to a public endpoint.
Never send real patient data to the default public terminology server at tx.fhir.org, even accidentally. Set -tx explicitly whenever you work with data that could contain PHI.
Disable terminology checks entirely
When you need fast structural validation and you don't care about code system lookups, you can turn terminology validation off completely by passing -tx n/a as the flag value. This tells the validator to skip all external terminology calls and focus only on structural and profile conformance rules:
java -jar validator_cli.jar bundle.json \
-version 4.0.1 \
-tx n/a
This mode is useful for CI pipeline runs where you want quick feedback on structure without waiting for remote lookups. It's also the right choice when you're validating synthetic or test data and you know the code values are intentionally placeholder values that won't resolve against a real code system. Re-enable terminology validation before treating any results as production-ready, since disabling it means real binding errors will not surface in your output.
Step 6. Validate bundles, references, and resolution
Real-world FHIR data rarely arrives as a single isolated resource. Most EHR integrations work with bundles, which are collections of resources grouped into a single payload, like a transaction bundle containing a Patient, multiple Observations, and a Coverage resource together. The fhir validator cli can validate the entire bundle as a unit, check each resource inside it against its declared profiles, and verify that references between resources actually resolve within the bundle context.
Validate a bundle as a whole
You pass a bundle file to the validator the same way you pass any individual resource. What changes is that the validator automatically inspects each entry in the bundle and applies structural rules to each one. If any entry declares a profile in its meta.profile field, the validator picks that up and enforces the profile constraints without any additional flags from you.

java -jar validator_cli.jar transaction-bundle.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-tx n/a
When validating bundles in a CI pipeline, combine -tx n/a with bundle validation to keep run times short and avoid blocking on terminology lookups that aren't relevant to structural checks.
Control reference resolution behavior
By default, the validator checks whether references inside your bundle resolve to a real resource entry within that same bundle. If a resource references a Patient by ID and no matching Patient entry exists, the validator flags that as an error. This behavior protects you from submitting bundles with broken internal references to an EHR endpoint.
You can extend this behavior using the -allow-example-urls flag when working with test data that uses placeholder reference URLs not intended to resolve. For bundles where external references need to resolve against a remote server, pass the base URL of that server using -bundle-validation-policy alongside your other flags so the validator knows where to look:
java -jar validator_cli.jar patient-bundle.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-profile http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient \
-allow-example-urls true
Fixing broken references at this stage, before submission, saves you from the ambiguous rejection errors that EHR APIs return when a bundle arrives with unresolvable internal links.
Step 7. Manage output formats and pipelines
By default, the fhir validator cli prints its results to the terminal as plain text. That works fine when you're running checks manually during development, but it breaks down the moment you need to feed results into an automated pipeline, store them for audit purposes, or parse them programmatically. Controlling the output format is how you make the validator useful beyond a single developer's machine.
Choose your output format
The validator supports several output modes through the -output and -output-style flags. You can write results to a file instead of the terminal by passing a file path to -output, and you can switch the format to structured JSON by adding -output-style json. JSON output is far easier to parse in scripts and CI systems than plain text, since every error carries a typed severity, a path, and a message you can extract reliably.
java -jar validator_cli.jar patient.json \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-profile http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient \
-output results.json \
-output-style json
The output file contains a structured list of issues with fields for severity, location, and message text, which lets any downstream tool filter on errors versus warnings without string parsing.
Feed results into a CI pipeline
Once you have file-based JSON output, you can integrate the validator directly into a CI workflow. The validator returns a non-zero exit code when it finds errors, so any standard CI system, GitHub Actions, GitLab CI, or Jenkins, will automatically fail the build step if validation fails. You don't need extra tooling to gate a pipeline on validation results.
Wire the validator as a pre-merge check in your CI pipeline so that invalid FHIR resources never reach a staging or production environment.
A minimal GitHub Actions step looks like this:
- name: Validate FHIR resources
run: |
java -jar validator_cli.jar resources/ \
-version 4.0.1 \
-ig hl7.fhir.us.core#6.0.0 \
-tx n/a \
-output validation-results.json \
-output-style json
Pass the entire resources directory rather than a single file to validate every resource in one step and capture all results in a single output file your pipeline can archive or inspect.
Step 8. Transform and convert resources safely
The fhir validator cli does more than validate. It also converts FHIR resources between JSON and XML formats and applies StructureMap-based transformations, letting you handle format mismatches between systems without introducing a separate conversion tool into your stack. Using the validator for both tasks keeps your pipeline simpler and ensures the output you produce is structurally sound before it moves to the next stage.
Convert between JSON and XML
You use the -convert flag to switch a resource from one serialization format to the other. The validator reads the source file, parses it against the FHIR specification, and writes the converted output to the file path you specify with -output. The conversion preserves all field values and structure exactly as they appear in the source.
# JSON to XML
java -jar validator_cli.jar patient.json \
-version 4.0.1 \
-convert \
-output patient.xml
# XML to JSON
java -jar validator_cli.jar patient.xml \
-version 4.0.1 \
-convert \
-output patient.json
Always run validation on the converted output as a second step to confirm the round-trip produced a structurally valid result. Format conversion surfaces edge cases in fields that serialize differently between JSON and XML, such as extension arrays and primitive type decorators, and a quick post-conversion validation catches those before the file moves downstream to an EHR endpoint.
Run conversion and validation as two separate commands in sequence so that any conversion error is clearly isolated from a validation error in your pipeline logs.
Apply a StructureMap transform
For more complex transformations between FHIR versions or between different resource shapes, the validator supports StructureMap-based transforms through the -transform flag. You provide the path to your StructureMap file, and the validator applies the mapping logic to your input resource, writing the result to the output path you specify.
java -jar validator_cli.jar source-resource.json \
-version 4.0.1 \
-transform path/to/your-map.xml \
-output transformed-resource.json
This capability is particularly useful when your pipeline needs to convert resources from DSTU2 or STU3 shapes into R4-compliant equivalents before submitting them to a modern EHR endpoint. Write your StructureMap against the official FHIR mapping language specification and test it thoroughly against representative samples before you promote any transform to a production pipeline.

Next steps
You now have a complete working setup for the fhir validator cli, from installing Java and downloading the jar through validating against implementation guides, managing terminology checks, handling bundles, structuring pipeline output, and transforming resources between formats. Each step in this guide builds directly on the previous one, so you can start simple and layer in more specific validation rules as your integration matures. The most important habit to build is running validation early and often, before resources reach any live EHR endpoint.
Validation confirms that your resources are structurally sound, but connecting them securely to Epic, Cerner, or Allscripts is a different challenge entirely. It requires managing OAuth flows, consent handling, and HIPAA-compliant infrastructure that most teams don't want to build from scratch. If you want to skip that complexity and get your SMART on FHIR application connected to real EHR systems in days, launch your SMART on FHIR app with SoFaaS and let the platform handle the integration layer for you.