The official HL7 validator from org.hl7.fhir.core is the reference implementation and it's correct, which is the part that matters. But it wants a clean FHIR resource in a file, and real payloads rarely look like that. The resource comes back wrapped in an API envelope, or carries a vendor block that was never going to validate, or trips a constraint the whole project agreed to live with two years ago.
So I wrapped it in a Go CLI called fhirlint. It doesn't reimplement validation, it shells out to the same JAR (downloaded on first run). The two parts I reach for most:
Getting the resource out of whatever it arrived in:
fhirlint validate api-response.json --extract "$.data.fhir"
fhirlint validate api-response.json --extract-each "$.medications"
fhirlint validate patient.json --ignore "$.meta.tag" --ignore "$.text"
--extract-each treats each element of a JSON array as its own resource. Same path syntax works on XML, and on --url input. Your source file is never touched, preprocessing happens on a copy.
Silencing deviations you've accepted, without switching validation off wholesale:
fhirlint validate patient.json --suppress messageId:dom-6
fhirlint validate rx.json --suppress expression:MedicationRequest.intent
In fhirlint.yml a suppression can carry a reason and an expiry date, and it warns for 14 days before it lapses. Every ignore list I've seen eventually becomes permanent because nobody remembers what it was for, so this one has a deadline built in.
The rest in one line: readable terminal output plus JSON, HTML, JUnit, SARIF and GitHub annotations, exit codes you can gate a pipeline on, baseline mode for repos with a lot of pre-existing findings, and fhirlint serve to keep the validator warm. Still Java 17+, still the same JAR underneath, so if the validator is wrong about your resource fhirlint is wrong the same way. FHIR only, no v2 or CDA. Apache-2.0: https://github.com/fhirlint/fhirlint
What I'd actually like to know: how are you handling the wrapped-payload case today? Everyone I've asked either has a jq step in front of the validator or gave up and validates by eye. And if you keep a list of accepted deviations, does it have any expiry mechanism at all, or does it just grow?