Build your bridge to FHIR.
Deploy the verification-first foundation, validate FHIR R4 resources, and understand what FHIR at Will implements today.
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
OperationOutcomeand 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 fhiratwillThe 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, voice2fhirDeploy 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.
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.
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.
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.
Verify readiness
Open the generated API domain and check /livez, then /readyz. Interactive OpenAPI documentation is available at /docs.
One project, four services, private dependencies, and generated secrets.
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 .envOn 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-password2. Bootstrap and start
docker compose --profile setup run --rm bootstrap
docker compose up -d
docker compose ps3. Confirm readiness
curl http://localhost:8000/livez
curl http://localhost:8000/readyz
curl http://localhost:8000/versionThe 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.
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).
| Header | Purpose |
|---|---|
X-LLM-Provider | LLM gateway to route through (currently openrouter) |
X-LLM-Model | Model identifier, e.g. z-ai/glm-5.2:free |
X-LLM-API-Key | Your OpenRouter key; per-request only, never persisted |
X-PHI-Egress-Acknowledged | Confirms you accept note text leaving your deployment boundary |
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.
Operational endpoints
/livezProcess liveness/readyzDependency and row-level-security readiness/metricsPrometheus metricsThe 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.
| Layer | Question | Status |
|---|---|---|
| L1 · Structural | Is this a parseable, allowed FHIR R4 resource? | Implemented |
| L2 · Profile | Does it conform to declared or requested profiles? | Implemented |
| L3 · Terminology | Are codes valid and in their bound ValueSets? | Implemented |
| L4 · Invariants | Do applicable FHIRPath invariants hold? | Implemented |
| L5 · Plausibility | Is the value physiologically or temporally possible? | Implemented |
| L6 · Fidelity | Is each generated element supported by source spans? | M3 |
| L7 · Coverage | Which clinical mentions were omitted? | M3 |
| L8 · Routing | Can this auto-accept or does it require review? | Validation mode |
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
| Variable | Purpose |
|---|---|
DATABASE_URL | Least-privileged PostgreSQL connection |
REDIS_URL | Redis connection for future jobs |
VALIDATOR_URL | Private validator sidecar URL |
TERMINOLOGY_URL | FHIR terminology server |
DEFAULT_IG_PACKAGES | IG coordinates stamped into reports |
FHIRBRIDGE_ENV | Development, staging, or production mode |
REQUIRE_RLS_ENFORCEMENT | Refuse 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.
Roadmap
| Milestone | Goal | Status |
|---|---|---|
| M0 | Config, storage, auth, errors, OpenAPI, health, containers | Implemented |
| M1 | Validation cascade, terminology, and plausibility | Implemented |
| M2 | BYOK/BYOM provider gateway and qualification probes | Preview |
| M3 | Narrative ingestion, generation, fidelity, and coverage | Preview |
| M4 | Human review workflows | Planned |
| M5–M6 | Calibrated routing, integrations, and hardening | Planned |
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 mypyIntegration tests require real PostgreSQL, Redis, validator, and terminology dependencies:
uv run pytest -m integrationSee the main repository → for contributing rules, full API examples, and source layout.