FHIR R4 · Documentation

Build your bridge to FHIR.

Deploy the verification-first foundation, validate FHIR R4 resources, and understand what FHIR at Will implements today.

Current release: M0–M1, plus an experimental NAR2FHIR preview. The validation cascade is implemented and stable. Narrative → FHIR conversion is live as a bring-your-own-key preview in the playground → — a preview, not a production capability.

Project status

FHIR at Will is the project; fhiratwill is its reusable Python package, while fhirbridge is the self-hosted HTTP service. The current platform provides an authenticated FHIR R4 validation service and the infrastructure required to operate it safely.

Available today

  • FHIR R4 resource and Bundle validation
  • Profile checks against preloaded implementation guides
  • Terminology validation inside the verification cascade
  • FHIRPath invariants and configurable plausibility rules
  • FHIR OperationOutcome and structured validation reports
  • API-key authentication, tenant isolation, metrics, and tracing hooks

Experimental preview

  • Narrative → FHIR conversion (NAR2FHIR): a clinical note in, an assembled FHIR R4 Bundle out
  • Bring-your-own-key LLM routing via OpenRouter — your key, your model, per request

Planned

  • Clinical extraction repair, fidelity, and coverage scoring
  • Managed (server-held) provider keys and additional LLM providers
  • Human review queues and delivery workflows

Install the Python library

The framework-neutral fhiratwill package is available from PyPI ↗ for Python 3.11 and newer.

pip install fhiratwill

The lightweight base installation includes deterministic de-identification, local FHIR validation, assembly, terminology interfaces, context rebinding, and write planning. Install the optional provider integration for text and voice conversion:

pip install "fhiratwill[all]"
from fhiratwill import deidentify, text2fhir, validate, voice2fhir
Library or service? Use the Python package inside your own application when you want direct control over adapters and policy. Deploy the HTTP service when you need authentication, tenant isolation, persistence, operational controls, and a network API.

Deploy on Railway

Deploy the complete FHIR at Will sandbox as one Railway project. The template creates four services and keeps every dependency except the API on Railway’s private network.

01

Launch the template

Sign in to Railway, choose a region, and review the generated configuration. Railway creates the API, validator, PostgreSQL, and Redis services together.

02

Wait for bootstrap

The validator image takes longer to build because it verifies the pinned validator binary and caches US Core. The API then migrates PostgreSQL and provisions its least-privileged runtime role.

03

Save the first API key

Open the API service’s first Pre-deploy logs and save the printed api_key. Only its Argon2id hash is stored, so the plaintext cannot be recovered later.

04

Verify readiness

Open the generated API domain and check /livez, then /readyz. Interactive OpenAPI documentation is available at /docs.

FHIR at Will on Railway

One project, four services, private dependencies, and generated secrets.

Template link publishing…
Sandbox by default. The one-click deployment uses the public tx.fhir.org/r4 terminology endpoint and runs with FHIRBRIDGE_ENV=staging. Use synthetic data. Before production, configure an authenticated terminology service you operate, supply FHIRBRIDGE_EPHEMERAL_KEY, review egress and retention controls, then switch the environment to production.

What the template protects

  • Only the API receives a public domain; the unauthenticated validator is never exposed.
  • PostgreSQL migrations run as the schema owner, while API traffic uses a separate role subject to row-level security.
  • The application-role password is generated and sealed by Railway rather than committed to source.
  • LLM use remains bring-your-own-key and requires explicit PHI egress acknowledgement.

Quickstart with Docker

You need Docker with Compose v2, at least 4 GB of available memory, and network access during the validator image build.

1. Configure the stack

Clone the main project, then create your environment file.

git clone https://github.com/Safwanmahmoud/FHIR-It-Will.git
cd FHIR-It-Will
cp .env.example .env

On Windows PowerShell, use Copy-Item .env.example .env. Set distinct owner and application database passwords:

POSTGRES_PASSWORD=choose-an-owner-password
APP_DB_PASSWORD=choose-a-different-app-password

2. Bootstrap and start

docker compose --profile setup run --rm bootstrap
docker compose up -d
docker compose ps
Save the API key. Bootstrap prints it once. Only its Argon2id hash is stored, so a lost key cannot be recovered.

3. Confirm readiness

curl http://localhost:8000/livez
curl http://localhost:8000/readyz
curl http://localhost:8000/version

The API runs at http://localhost:8000. Interactive OpenAPI documentation is available at /docs.

Validate a resource

Every compute endpoint requires a Bearer API key. A non-conformant resource returns a successful validation report; it is not treated as an HTTP transport error.

curl -X POST http://localhost:8000/v1/validate \
  -H "Authorization: Bearer fhirb_..." \
  -H "Content-Type: application/json" \
  -d '{
    "resource": {
      "resourceType": "Patient",
      "id": "example",
      "name": [{"family": "Shaw", "given": ["Amy"]}],
      "gender": "female"
    }
  }'

Requests may select profiles, validation layers, severity overrides, and terminology-check limits. A bare resource is also accepted with Content-Type: application/fhir+json.

See it applied. The playground → converts a clinical narrative into a FHIR Bundle using the experimental NAR2FHIR preview described below.

Narrative → FHIR (experimental)

POST /v1/NAR2FHIR takes a free-text clinical note and returns an assembled FHIR R4 Bundle. It is bring-your-own-key: you supply an OpenRouter API key and model per request, and the service routes the extraction through them. Keys are used for the request only — never stored or logged.

curl -X POST http://localhost:8000/v1/NAR2FHIR \
  -H "Authorization: Bearer fhirb_..." \
  -H "Content-Type: application/json" \
  -H "X-LLM-Provider: openrouter" \
  -H "X-LLM-Model: z-ai/glm-5.2:free" \
  -H "X-LLM-API-Key: sk-or-v1-..." \
  -H "X-PHI-Egress-Acknowledged: true" \
  -d '{
    "text": "62-year-old male seen for follow-up. BP 128/82 mmHg, heart rate 74 bpm. Type 2 diabetes; on metformin 500 mg twice daily."
  }'

The request body is the narrative. There is no profiles field: assembly validates nothing, so it cannot honor a profile. Pass profiles to POST /v1/validate with the returned Bundle. The response includes that unvalidated Bundle, PHI-free assembly and binding notes, and model metadata (model id, token usage, latency, and cost).

HeaderPurpose
X-LLM-ProviderLLM gateway to route through (currently openrouter)
X-LLM-ModelModel identifier, e.g. z-ai/glm-5.2:free
X-LLM-API-KeyYour OpenRouter key; per-request only, never persisted
X-PHI-Egress-AcknowledgedConfirms you accept note text leaving your deployment boundary
This is a preview. A model extracts grounded facts; assembly of the Bundle is deterministic and explicitly unvalidated. Fidelity and coverage (L6–L7) are not scored yet. Submit the Bundle to POST /v1/validate before trusting it. Free OpenRouter models work without credits; rate limits apply.

API reference

This list is generated from the deployed API’s OpenAPI contract and updates automatically when its public interface changes. The machine-readable contract → is available through the landing service.

Loading the current API contract…

Operational endpoints

GET/livezProcess liveness
GET/readyzDependency and row-level-security readiness
GET/metricsPrometheus metrics

The validation cascade

Every report includes all eight layers. A layer that did not run is explicitly marked skipped or not_applicable; absence never looks like a pass.

LayerQuestionStatus
L1 · StructuralIs this a parseable, allowed FHIR R4 resource?Implemented
L2 · ProfileDoes it conform to declared or requested profiles?Implemented
L3 · TerminologyAre codes valid and in their bound ValueSets?Implemented
L4 · InvariantsDo applicable FHIRPath invariants hold?Implemented
L5 · PlausibilityIs the value physiologically or temporally possible?Implemented
L6 · FidelityIs each generated element supported by source spans?M3
L7 · CoverageWhich clinical mentions were omitted?M3
L8 · RoutingCan this auto-accept or does it require review?Validation mode
Conformant does not mean correct. A heart rate of 44,000/min can be valid FHIR and still be impossible. Plausibility checks address that gap without flagging values merely because they are clinically abnormal.

Architecture

Clients authenticate to a FastAPI service. The service orchestrates typed FHIR models, the private HL7 validator sidecar, a terminology service, versioned plausibility rules, routing decisions, and tenant-aware PostgreSQL storage.

  • The API uses a least-privileged database role subject to row-level security.
  • The validator remains private because it has no authentication and can fetch referenced resources.
  • Readiness fails closed when a required verification dependency is unavailable.
  • JSON logs, Prometheus metrics, and OpenTelemetry hooks support operations.

Configuration

VariablePurpose
DATABASE_URLLeast-privileged PostgreSQL connection
REDIS_URLRedis connection for future jobs
VALIDATOR_URLPrivate validator sidecar URL
TERMINOLOGY_URLFHIR terminology server
DEFAULT_IG_PACKAGESIG coordinates stamped into reports
FHIRBRIDGE_ENVDevelopment, staging, or production mode
REQUIRE_RLS_ENFORCEMENTRefuse readiness if tenant isolation does not apply

Production mode rejects insecure transport and unsafe dependency defaults. For the complete settings reference, see .env.example in the main repository.

Security and privacy

  • API keys are stored as Argon2id hashes.
  • Tenant-scoped tables enforce PostgreSQL row-level security.
  • Submitted validation resources are scored and dropped rather than persisted.
  • BYOK LLM keys are forwarded per request and never stored or logged.
  • Validation responses use Cache-Control: no-store.
  • Logs record decisions and counts, not resource bodies or clinical values.
  • Secrets and known sensitive fields pass through centralized redaction.
Self-hosting is not compliance by itself. Operators remain responsible for access controls, encryption, backups, retention, licensing, agreements, incident response, and the infrastructure handling PHI.

Roadmap

MilestoneGoalStatus
M0Config, storage, auth, errors, OpenAPI, health, containersImplemented
M1Validation cascade, terminology, and plausibilityImplemented
M2BYOK/BYOM provider gateway and qualification probesPreview
M3Narrative ingestion, generation, fidelity, and coveragePreview
M4Human review workflowsPlanned
M5–M6Calibrated routing, integrations, and hardeningPlanned

Development

Python 3.12 and uv are the supported development path.

uv sync
uv run pytest -q -m "not integration"
uv run ruff check .
uv run ruff format --check .
uv run mypy

Integration tests require real PostgreSQL, Redis, validator, and terminology dependencies:

uv run pytest -m integration

See the main repository → for contributing rules, full API examples, and source layout.