CLI tool
HL7/fhir avatar
HL7/fhir

The FHIR specification is a repository, not a document

Official source for the HL7 FHIR Specification

761 stars215 forksHTMLNOASSERTION

At a glance

What is it?
The HL7/fhir repository holds the source for the FHIR specification, from resource and datatype definitions to value sets, examples and narrative pages, and it is built into the published site by a separate publisher called kindling. Contributors do not open pull requests directly, because every change has to go through the Jira workflow and normative content needs a named review team.
Who is it for?
Adopt the HL7/fhir repository as your working copy if you implement against FHIR and need to read the definitions rather than just the published pages, since the source carries the resource and datatype definitions, value sets and examples that the site is generated from. Do not try to contribute by opening a pull request, because the documented workflow routes every change through Jira and normative content needs review by the FHIR normative review team.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The repository is the input to a publisher

This repository holds the source for the FHIR specification: resource and datatype definitions, value sets, examples and the narrative pages.

The important architectural detail is the second sentence. The specification is built from this source by kindling, the FHIR specification publisher. So the published specification at hl7.org is an artefact of a build, and the repository holds the inputs.

That arrangement has consequences for how you work with the spec. If you are reading FHIR, the published site is the right place, and the README says so directly: only the editors of the specification need to build it, and if that is not you, one of the listed links will probably be more useful. For a reader, that is the published specification or the current build at build.fhir.org.

For an implementer, the source is more useful than the site in specific ways. The definitions are structured data rather than prose, so you can read a resource's element list as data. The value sets are the enumerations, and reading them in the source shows you exactly what is in each. The examples are real instances, and the directory of them is the closest thing the repository has to a conformance test corpus.

The repository also carries machinery that has nothing to do with FHIR as a standard. There is a build.gradle.kts and a settings.gradle.kts for the Gradle build, an ivy.xml and ivysettings.xml for dependency resolution, a .idea/ directory, a clean.sh and clean.bat, and a master-branch-pipeline.yml and pull-request-pipeline.yml that describe the CI. The source/ directory holds the specification content itself, with qa/, schema/, tests/, tools/ and implementations/ alongside it.

Building needs 16 GB of RAM and about ten minutes

The build requirements are stated plainly, and the RAM figure is the one that will stop you if you are on a laptop.

You need Java 17 or later, and at least 16 GB of RAM, because the build runs with a 12.5 GB heap as configured in gradle.properties. Then you run:

code
./gradlew publish

or use publish.sh on bash or publish.bat on Windows, which do the same thing. The output goes in publish/. A full build takes around 10 minutes, though the README notes this depends a lot on the hardware.

Ten minutes is the figure for a machine that meets the memory requirement, and it is for the whole specification. The build does incremental work, and the mechanism is worth knowing because it changes what a local build is for.

The build keeps track of what changed since the last build in temp/build.cache, and only rebuilds the affected resources and pages. It does a full build when nothing has changed, when there is no previous output, or when you ask for one, which is the publishFull target:

code
./gradlew publishFull

So the first build is the expensive one and everything after is a partial rebuild of what you touched. That is the difference between a ten minute wait and a short one, and it is why a local build is a realistic thing for an editor to run while working on a single resource rather than only for a release.

The memory requirement is the other half of the picture. A 12.5 GB heap on a machine with exactly 16 GB of RAM means the build is close to the limit, and the heap is a Gradle setting you could lower, at the cost of a build that needs more collections and therefore runs longer.

You can build one resource instead of the whole specification

The command line parameters are the reason a local build is practical for contributors, and they are passed through Gradle with `--args`:

code
./gradlew publish --args="-resource patient -nosound"

The parameter table covers the useful cases. `-nopartial` always does a full build, the same as publishFull. `-resource [name]` only rebuilds the named resource, and `-page [file]` only rebuilds the named page, for example fhirpatch.html. `-nogen` does not generate the specification, it just runs the validation. `-validation-mode [mode]` takes normal, which is the default, none or extended. `-validate [id]` only validates the named example.

Two of the rest are practical rather than technical. `-nosound` does not play the tones at the start and end of the build, which matters if you are running it repeatedly. And `-output [folder]` puts the output somewhere other than publish/.

There are also options for the build environment itself. `-folder [folder]` builds from a folder other than the current directory, `-fhir-settings [file]` is the location of the fhir-settings.json file that holds API keys, and `-diff [program]` selects the program used to show differences when a reference implementation does not round-trip an example. That last one is a nice detail, since a round-trip failure is exactly the kind of problem you want to see the diff for.

Finally, `-name [text]` sets the name of the build shown on each page, defaulting to Local Build followed by the machine name, and `-web` produces the final form for publication on hl7.org, which is for core editors only.

Verbose or custom logging is set up with a logback configuration file:

code
./gradlew publish -Plogback.configurationFile=~/my-logback-config.xml

Contributions go through Jira, not through pull requests

The contribution workflow is the part of this repository that surprises people, and the README is explicit about it.

To propose a change, use the Jira issue tracker rather than making a pull request directly, since all changes must be approved through the Jira workflow. The instructions say to use that rather than making a PR directly, without exception.

So a pull request to this repository is not the entry point for a change. A change starts as a Jira issue, goes through approval there, and is then applied by the editors or committers.

Maintenance follows the same pattern. The project is maintained by Grahame Grieve and David Otasek on behalf of the FHIR community, and changes to normative content require review by the FHIR normative review team, with the CODEOWNERS file recording who that is. That distinction between normative and non-normative content is the governance mechanism, and it is why the repository has a CODEOWNERS file and a REVIEW_PROFILE.md at all.

The rest of the workflow is documented and works the way you would hope. Each pull request is built by the pull request pipeline, and if the build succeeds it is uploaded to build.fhir.org/branches under the name of the branch, where the changes can be reviewed. Once a PR is merged to master, the master branch pipeline builds it and uploads it to build.fhir.org. Build results are posted to the committers and notification stream on chat.fhir.org.

So the review loop has two stages: the Jira approval decides whether the change happens, and the branch build shows you what the change looks like before it goes live. A .mergify.yml is present for merge automation, along with a PULL_REQUEST_TEMPLATE.md.

The repository carries the parts of FHIR that are not the specification

Alongside the specification content, the tree holds a set of directories that reflect what it means to maintain a healthcare standard in practice.

There is a qa/ directory, which is the quality assurance material, and a GenerateQADocs.docm at the root, a Word macro document for generating it. There is a records.csv, which is the kind of file that tracks the history of individual resources. A suppressed-messages.txt suggests that validation output is filtered, and a simple-error.txt alongside it.

The schema/ directory holds the schema output, and implementations/ holds reference implementation material, which is what the `-diff` parameter compares against when checking that an example round-trips. The tests/ directory is the specification's own test suite, and the separate fhir-test-cases repository holds the broader test case corpus.

There is also a plugin/ directory and a tools/ directory, a health-cards-dev-tools directory, and an images/ directory for the graphics used in the pages. A deploy.build.fhir.org.enc file at the root is an encrypted file, presumably credentials for the deployment target, and publish.ini sits next to the publish scripts.

The security angle is worth noting given the contents. There is a SECURITY.md in the repository, which is standard, and the deployment secret is encrypted rather than stored in plain text.

The licence field reads Other, a custom licence GitHub cannot classify, with the LICENSE file at the root. For a specification, the licence is the interesting part, because a normative standard has to be usable by anyone implementing it, and the specifics are in that file rather than in a standard identifier.

The last push was on 2026-10-21, so the repository is continuously worked on, and there are no GitHub releases because the published artefact is the site, not a release.

Where this repository sits in the FHIR ecosystem

The surrounding ecosystem is what makes this repository's role clear.

There is the published specification at hl7.org and the current build at build.fhir.org, which is the continuously updated version built from master. There is the Jira tracker for proposing changes, the FHIR chat at chat.fhir.org, and the FHIR test cases repository, which is the conformance corpus kept separately from the specification source.

That last split is the one to understand. The specification defines what the resources mean and what the profiles require, and the test cases define what a given statement can be checked against. Keeping them apart means a test case can be updated without touching normative content, which matters because normative content has a much heavier approval path.

Against other specification formats, FHIR's own approach is unusual in how the specification is authored. It is not a document with a table of contents and a version history. It is a build graph of resource definitions, value sets, invariants and examples, from which both the published narrative and the machine-readable artefacts are produced. The cost of that approach is the build machinery you have just read about, and the benefit is that a change to one resource can be validated, reviewed and published without regenerating the world.

For an implementer, the practical guidance is to read the published site for understanding and to clone this repository when you need the definitions as data, the full example set, or the exact value set membership. And for a contributor, the practical guidance is that Jira is the door, not the pull request.

Editorial conclusion

Adopt the HL7/fhir repository as your working copy if you implement against FHIR and need to read the definitions rather than just the published pages, since the source carries the resource and datatype definitions, value sets and examples that the site is generated from. Do not try to contribute by opening a pull request, because the documented workflow routes every change through Jira and normative content needs review by the FHIR normative review team. Verify first which branch you need, since master is the published line and pull requests are built separately onto build.fhir.org, and check the build requirements, because a local build needs Java 17 and at least 16 GB of RAM.

Frequently asked questions

What is in the HL7/fhir repository?

The source for the FHIR specification: resource and datatype definitions, value sets, examples and the narrative pages. The specification is built from that source by kindling, the FHIR specification publisher, and the published site is the build output.

How do I propose a change to the FHIR specification?

Through the Jira issue tracker, not a pull request. The README says to use Jira rather than making a PR directly, since all changes must be approved through the Jira workflow, and changes to normative content additionally require review by the FHIR normative review team.

What do I need to build the FHIR specification locally?

Java 17 or later and at least 16 GB of RAM, because the build runs with a 12.5 GB heap. Then run `./gradlew publish`, or use publish.sh or publish.bat, and the output goes in publish/. A full build takes around 10 minutes depending on the hardware.

Official sources

  1. HL7/fhir on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hl7-fhir.svg)](https://hysenlabs.com/projects/hl7-fhir)

Was this useful?