CapabilityStatement: A FHIR Server Should Only Claim What It Can Prove
CapabilityStatement: A FHIR Server Should Only Claim What It Can Prove
Version: dev
API source revision: 946221398bc8fa8731838e98e3d040c102df7b63
Synced documentation snapshot: 4c36f1c8e5983943710398c2d8c1bd4ea689dbdd
Last verified: September 23, 2026
Examples: synthetic only
A FHIR CapabilityStatement should answer a concrete engineering question: what can this server actually do at this endpoint?
FHIR R4 makes the distinction explicit. A CapabilityStatement can describe actual software, desired capabilities, or requirements. For a REST server's /metadata endpoint, the useful interpretation is the first one: current operational functionality, not roadmap intent.
Ilavrita treats that distinction as a design constraint. The current generated API reference at revision 946221398bc8fa8731838e98e3d040c102df7b63 describes GET /fhir/R4/metadata as generated from the same table used to register FHIR routes, with a resource appearing only when the matching interactions are implemented and covered by tests.
That is the goal behind a rule we keep repeating in this project:
Claims stop where the tests stop.
This post explains why that matters, what a CapabilityStatement can prove, what it cannot prove, and how we are trying to keep Ilavrita's public contract narrower than its ambitions.
What FHIR R4 actually requires
FHIR R4 requires RESTful servers to provide a CapabilityStatement describing the interactions and resource types they support. The specification also says resource types or operations that are not listed are not supported.
Primary specification references:
That makes /metadata more than documentation.
A client can inspect it before deciding whether an integration is compatible. Tooling can use it to generate or constrain behavior. A conformance assessment can compare the server's declaration with what the server actually does.
The important word is actual.
A roadmap can say we intend to support Patch. An ADR can explain how we intend to implement PostgreSQL. A package can contain types for an operation that has not reached the HTTP boundary. None of those facts make the operation a current server capability.
Ilavrita's rule: derive the declaration from the serving surface
The current Ilavrita API reference describes GET /fhir/R4/metadata this way: the statement is generated from the table from which the FHIR routes are registered, so it cannot name an interaction the router does not dispatch.
That direction of dependency matters.
The dangerous design is:
product roadmap
↓
hand-written capability document
↓
running server
The safer direction is:
implemented + registered behavior
↓
CapabilityStatement
It does not eliminate bugs. It does eliminate one easy class of overclaim: adding a capability to a brochure-like metadata document while forgetting that the server does not actually serve it.
At the current API revision, /metadata itself is deliberately reachable without an authenticated principal. That is consistent with the R4 guidance that servers requiring authentication should still make their CapabilityStatement available before authorization.
A declared resource is not the same thing as a storable resource
This is an important architectural distinction in Ilavrita.
The storage layer is not the public FHIR contract.
The synced project documentation records 126 R4 resource types as declared. It also explains why the list is not simply every type the underlying storage can hold. A type is exposed only when the server can place it within the authorization model and cover the corresponding behavior.
That means this inference is invalid:
storage accepts JSON with resourceType=X
therefore Ilavrita supports FHIR resource X
A public FHIR resource surface includes more than persistence. It includes routing, response semantics, authorization, history behavior, error shapes, and whatever search or operations the CapabilityStatement advertises for that resource.
Keeping that boundary explicit is one reason Ilavrita puts the FHIR contract above its embedded runtime rather than allowing runtime concepts to leak into /fhir/R4.
See the architecture documentation and known limitations for the current boundary.
Search parameters are capabilities too
A server can support search without supporting every possible FHIR search parameter.
Ilavrita's current API reference says the parameters a resource answers are declared in its CapabilityStatement entry. The implementation deliberately refuses a parameter outside its supported set instead of silently pretending to answer it.
That makes the CapabilityStatement useful to a client before the query is sent.
For example, the synced documentation shows a Patient capability entry containing parameters such as _id, _lastUpdated, identifier, family, given, gender, active, and birthdate.
The point is not that this is the largest possible list. The point is that the list should correspond to searches the server knows how to execute.
FHIR R4's CapabilityStatement model provides resource-level searchParam declarations precisely so clients can inspect this surface rather than assume that every parameter defined by the specification is implemented by every server.
Unsupported interactions should remain visibly unsupported
Ilavrita currently distinguishes a resource type it does not declare from an interaction it does not implement.
The synced project documentation records this behavior:
- a type outside the declared resource surface returns
404; - a recognized but unimplemented FHIR interaction returns
501; - failures remain FHIR-shaped
OperationOutcomeresponses rather than leaking framework errors.
The generated API reference at revision 946221398bc8fa8731838e98e3d040c102df7b63 explicitly lists type-level and whole-system history as unimplemented routes returning 501.
That is useful evidence because it demonstrates a negative capability.
A server's contract is not only what succeeds. It is also how unsupported behavior fails.
If /metadata omitted type-level history while the server quietly returned some approximate history implementation, the metadata would not tell the whole truth. If /metadata advertised it while the route returned 501, the declaration would be false.
The testable target is agreement between those two surfaces.
CapabilityStatement and OpenAPI answer different questions
Ilavrita publishes an OpenAPI description as well as a FHIR CapabilityStatement.
They overlap, but they are not interchangeable.
OpenAPI describes the HTTP surface: paths, methods, request shapes, responses, and non-FHIR endpoints such as /healthz, /version, and authentication routes.
CapabilityStatement is the FHIR-native declaration of the server's interoperability surface: FHIR version, resource types, interactions, search parameters, operations, versioning behavior, and related capability metadata.
The repository documentation says CI checks the OpenAPI description against a running server. The CapabilityStatement is generated from registered FHIR behavior.
For an integration engineer, both are useful:
OpenAPI → what HTTP interface exists?
CapabilityStatement → what FHIR behavior does this endpoint claim?
Neither one, by itself, proves that every edge case is correct.
What /metadata cannot prove
This is the more important half of the article.
A CapabilityStatement is a declaration. Even when generated from implementation structures, it does not establish production readiness.
It does not prove that authorization has no bypass.
It does not prove that a backup can be restored under every failure mode.
It does not prove that a deployment satisfies HIPAA, GDPR, EHDS, or another regulatory regime.
It does not replace an external security review.
It does not turn a developer preview into a system suitable for patient data.
And it does not automatically prove behavioral conformance merely because an interaction name appears in the resource.
That is why the declaration needs tests behind it.
Ilavrita's synced security documentation says project isolation, authorization, authentication, privilege separation, audit, and deleted-data controls are enforced and tested, while request correlation is not implemented and TLS remains a deployment concern. It also explicitly records that no external security review has happened.
Those limitations remain true regardless of how complete /metadata looks.
The evidence chain we want
For a capability claim to be useful, we want a reader to be able to walk backwards from the claim to evidence.
The intended chain is:
website/blog claim
↓
CapabilityStatement or API description
↓
registered implementation
↓
tests / conformance evidence
↓
exact source revision
A break anywhere in that chain should weaken the wording of the public claim.
For example, saying "FHIR transaction is implemented" should require more than seeing a transaction string in a document. The implementation has to establish the behavior that makes a transaction a transaction: all-or-nothing execution, entry semantics, authorization, audit behavior, and failure handling.
Likewise, saying "validation is implemented" is too broad unless we state what validation means: base definitions, profiles, terminology, invariants, references, and external validator coverage are different claims.
The CapabilityStatement is therefore the beginning of evidence discovery, not the end.
A documentation mismatch found during this verification
This post uncovered a source-of-truth problem we are deliberately not hiding.
Payload's current docs-meta says the synced dev revision is:
946221398bc8fa8731838e98e3d040c102df7b63
The generated API reference is also stamped with that revision.
But individual synced documentation records currently carry GitHub source URLs pinned to an older snapshot:
4c36f1c8e5983943710398c2d8c1bd4ea689dbdd
There is also stale prose inside that older documentation snapshot. For example, the roadmap checklist marks conditional create/update/delete as complete and then later says conditional interactions are the next missing capability. The known-limitations page likewise contains older paragraphs that contradict newer capability tables.
So this article does not use those contradictory paragraphs as evidence for current behavior.
For current HTTP capability claims, the generated API reference stamped 946221398bc8fa8731838e98e3d040c102df7b63 is the stronger source available in the publishing environment. Older synced docs are used only where their statements agree with that API surface or describe architectural principles rather than changed capability state.
That mismatch should be fixed. A trust-based documentation system should not make a reader choose which paragraph to believe.
Reproduce the inspection
On a running Ilavrita instance, the first two requests an evaluator should make are:
curl -s http://127.0.0.1:8090/version
and:
curl -s \
-H 'Accept: application/fhir+json' \
http://127.0.0.1:8090/fhir/R4/metadata
The first tells you which binary you are interrogating. The second tells you what that endpoint says it supports.
Then test the declaration.
If a resource advertises read, read one using synthetic data. If it advertises search-type, exercise a declared parameter. If an interaction is absent, confirm that the server does not quietly approximate it.
For repository verification, the documented development commands are:
make build
make test
make api-lint
make api-verify
The repository also documents a conformance script that runs the HL7 validator over produced FHIR representations.
Verification limitation for this post: the GitHub connector available to this publishing run refused direct dev branch file access, so I could not perform a fresh clean-clone execution of those commands. I therefore do not claim that I independently reran the test suite for this article. The capability claims above are limited to the Payload-synced evidence and the generated API reference, with their revision mismatch stated explicitly.
That is what “claims stop where the tests stop” requires when the test environment itself is unavailable.
FAQ
Does a CapabilityStatement mean a FHIR server is conformant?
No. It declares capabilities. Those claims still need behavioral verification. FHIR explicitly positions CapabilityStatement as part of its conformance framework and as a basis for compatibility and conformance assessment—not as a substitute for the assessment itself.
Should /metadata require authentication?
FHIR R4 says servers that require authentication should still return a CapabilityStatement before authentication or authorization. Ilavrita's current API reference exposes /fhir/R4/metadata without touching resource storage.
Can a server support only some FHIR resources?
Yes. FHIR servers choose which resource types and interactions they support and declare that surface in their CapabilityStatement. Ilavrita currently declares a subset rather than treating storage capability as FHIR support.
Is Ilavrita ready for patient data because authorization and audit are implemented?
No. The project remains a developer preview. There has been no external security review, important operational and protocol gaps remain, and Ilavrita makes no HIPAA, GDPR, EHDS, certification, or production-readiness claim. Use synthetic data only.
The practical rule
A useful CapabilityStatement is intentionally boring.
It should not surprise the router.
It should not contain the roadmap.
It should not become more impressive because a release announcement needs stronger copy.
It should be a machine-readable boundary around behavior the running server can defend with evidence.
For Ilavrita, that is the standard we want every capability claim—on the homepage, in documentation, in release notes, and in this blog—to inherit.
If the implementation moves, the declaration moves.
If the tests do not prove it, we do not get to claim it.
Evidence used for this post
- Ilavrita documentation
- Known limitations
- Roadmap
- HL7 FHIR R4 CapabilityStatement
- HL7 FHIR R4 RESTful API
Primary query: FHIR CapabilityStatement
Meta description: FHIR R4 requires servers to declare what they actually support. See how Ilavrita turns CapabilityStatement into an evidence boundary, what it proves, and what it cannot prove.
