Agent skill
fhir-developer-skill
FHIR API development guide for building healthcare endpoints. Use when: (1) Creating FHIR REST endpoints (Patient, Observation, Encounter, Condition, MedicationRequest), (2) Validating FHIR resources and returning proper HTTP status codes and error responses, (3) Implementing SMART on FHIR authorization and OAuth scopes, (4) Working with Bundles, transactions, batch operations, or search pagination. Covers FHIR R4 resource structures, required fields, value sets (status codes, gender, intent), coding systems (LOINC, SNOMED, RxNorm, ICD-10), and OperationOutcome error handling.
Install this agent skill to your Project
npx add-skill https://github.com/FreedomIntelligence/OpenClaw-Medical-Skills/tree/main/skills/fhir-developer-skill
SKILL.md
FHIR Developer Skill
Quick Reference
HTTP Status Codes
| Code | When to Use |
|---|---|
200 OK |
Successful read, update, or search |
201 Created |
Successful create (include Location header) |
204 No Content |
Successful delete |
400 Bad Request |
Malformed JSON, wrong resourceType |
401 Unauthorized |
Missing, expired, revoked, or malformed token (RFC 6750) |
403 Forbidden |
Valid token but insufficient scopes |
404 Not Found |
Resource doesn't exist |
412 Precondition Failed |
If-Match ETag mismatch (NOT 400!) |
422 Unprocessable Entity |
Missing required fields, invalid enum values, business rule violations |
Required Fields by Resource (FHIR R4)
| Resource | Required Fields | Everything Else |
|---|---|---|
| Patient | (none) | All optional |
| Observation | status, code |
Optional |
| Encounter | status, class |
Optional (including subject, period) |
| Condition | subject |
Optional (including code, clinicalStatus) |
| MedicationRequest | status, intent, medication[x], subject |
Optional |
| Medication | (none) | All optional |
| Bundle | type |
Optional |
Required vs Optional Fields (CRITICAL)
Only validate fields with cardinality starting with "1" as required.
| Cardinality | Required? |
|---|---|
0..1, 0..* |
NO |
1..1, 1..* |
YES |
Common mistake: Making subject or period required on Encounter. They are 0..1 (optional).
Value Sets (Enum Values)
Invalid enum values must return 422 Unprocessable Entity.
Patient.gender
male | female | other | unknown
Observation.status
registered | preliminary | final | amended | corrected | cancelled | entered-in-error | unknown
Encounter.status
planned | arrived | triaged | in-progress | onleave | finished | cancelled | entered-in-error | unknown
Encounter.class (Common Codes)
| Code | Display | Use |
|---|---|---|
AMB |
ambulatory | Outpatient visits |
IMP |
inpatient encounter | Hospital admissions |
EMER |
emergency | Emergency department |
VR |
virtual | Telehealth |
Condition.clinicalStatus
active | recurrence | relapse | inactive | remission | resolved
Condition.verificationStatus
unconfirmed | provisional | differential | confirmed | refuted | entered-in-error
MedicationRequest.status
active | on-hold | cancelled | completed | entered-in-error | stopped | draft | unknown
MedicationRequest.intent
proposal | plan | order | original-order | reflex-order | filler-order | instance-order | option
Bundle.type
document | message | transaction | transaction-response | batch | batch-response | history | searchset | collection
Validation Pattern
Python/FastAPI:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
def operation_outcome(severity: str, code: str, diagnostics: str):
return {
"resourceType": "OperationOutcome",
"issue": [{"severity": severity, "code": code, "diagnostics": diagnostics}]
}
VALID_OBS_STATUS = {"registered", "preliminary", "final", "amended",
"corrected", "cancelled", "entered-in-error", "unknown"}
@app.post("/Observation", status_code=201)
async def create_observation(data: dict):
if not data.get("status"):
return JSONResponse(status_code=422, content=operation_outcome(
"error", "required", "Observation.status is required"
), media_type="application/fhir+json")
if data["status"] not in VALID_OBS_STATUS:
return JSONResponse(status_code=422, content=operation_outcome(
"error", "value", f"Invalid status '{data['status']}'"
), media_type="application/fhir+json")
# ... create resource
TypeScript/Express:
const VALID_OBS_STATUS = new Set(['registered', 'preliminary', 'final', 'amended',
'corrected', 'cancelled', 'entered-in-error', 'unknown']);
app.post('/Observation', (req, res) => {
if (!req.body.status) {
return res.status(422).contentType('application/fhir+json')
.json(operationOutcome('error', 'required', 'Observation.status is required'));
}
if (!VALID_OBS_STATUS.has(req.body.status)) {
return res.status(422).contentType('application/fhir+json')
.json(operationOutcome('error', 'value', `Invalid status '${req.body.status}'`));
}
// ... create resource
});
Pydantic v2 Models (use Literal, not const=True):
from typing import Literal
from pydantic import BaseModel
class Patient(BaseModel):
resourceType: Literal["Patient"] = "Patient"
id: str | None = None
gender: Literal["male", "female", "other", "unknown"] | None = None
Coding Systems (URLs)
| System | URL |
|---|---|
| LOINC | http://loinc.org |
| SNOMED CT | http://snomed.info/sct |
| RxNorm | http://www.nlm.nih.gov/research/umls/rxnorm |
| ICD-10 | http://hl7.org/fhir/sid/icd-10 |
| v3-ActCode | http://terminology.hl7.org/CodeSystem/v3-ActCode |
| Observation Category | http://terminology.hl7.org/CodeSystem/observation-category |
| Condition Clinical | http://terminology.hl7.org/CodeSystem/condition-clinical |
| Condition Ver Status | http://terminology.hl7.org/CodeSystem/condition-ver-status |
Common LOINC Codes (Vital Signs)
| Code | Description |
|---|---|
8867-4 |
Heart rate |
8480-6 |
Systolic blood pressure |
8462-4 |
Diastolic blood pressure |
8310-5 |
Body temperature |
2708-6 |
Oxygen saturation (SpO2) |
Data Type Patterns
Coding (direct) vs CodeableConcept (wrapped)
Coding - Used by Encounter.class:
{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB"}
CodeableConcept - Used by Observation.code, Condition.code:
{"coding": [{"system": "http://loinc.org", "code": "8480-6"}], "text": "Systolic BP"}
Reference
{"reference": "Patient/123", "display": "John Smith"}
Identifier
{"system": "http://hospital.example.org/mrn", "value": "12345"}
Common Mistakes
| Mistake | Correct Approach |
|---|---|
Making subject or period required on Encounter |
Both are 0..1 (optional). Only status and class are required |
Using CodeableConcept for Encounter.class |
class uses Coding directly: {"system": "...", "code": "AMB"} |
| Returning 400 for ETag mismatch | Use 412 Precondition Failed for If-Match failures |
| Returning 400 for invalid enum values | Use 422 Unprocessable Entity for validation errors |
| Forgetting Content-Type header | Always set Content-Type: application/fhir+json |
| Missing Location header on create | Return Location: /Patient/{id} with 201 Created |
Resource Structures
For complete JSON examples of all resources, see references/resource-examples.md.
Quick reference for error responses:
{
"resourceType": "OperationOutcome",
"issue": [{"severity": "error", "code": "not-found", "diagnostics": "Patient/123 not found"}]
}
RESTful Endpoints
POST /[ResourceType] # Create (returns 201 + Location header)
GET /[ResourceType]/[id] # Read
PUT /[ResourceType]/[id] # Update
DELETE /[ResourceType]/[id] # Delete (returns 204)
GET /[ResourceType]?param=value # Search (returns Bundle)
GET /metadata # CapabilityStatement
POST / # Bundle transaction/batch
Conditional Operations
If-Match (optimistic locking):
- Client sends:
If-Match: W/"1" - Mismatch returns
412 Precondition Failed
If-None-Exist (conditional create):
- Client sends:
If-None-Exist: identifier=http://mrn|12345 - Match exists: return existing (200)
- No match: create new (201)
Reference Files
For detailed guidance, see:
- Resource Examples: Complete JSON structures for Patient, Observation, Encounter, Condition, MedicationRequest, OperationOutcome, CapabilityStatement
- SMART on FHIR Authorization: OAuth flows, scope syntax (v1/v2), backend services, scope enforcement
- Pagination: Search result pagination,
_count/_offsetparameters, link relations - Bundle Operations: Transaction vs batch semantics, atomicity, processing order
Implementation Checklist
- Set
Content-Type: application/fhir+jsonon all responses - Return
meta.versionIdandmeta.lastUpdatedon resources - Return
Locationheader on create:/Patient/{id} - Return
ETagheader:W/"{versionId}" - Use OperationOutcome for all error responses
- Validate required fields → 422 for missing
- Validate enum values → 422 for invalid
- Search returns Bundle with
type: "searchset"
Quick Start Script
To scaffold a new FHIR API project with correct Pydantic v2 patterns:
python scripts/setup_fhir_project.py my_fhir_api
Creates a FastAPI project with correct models, OperationOutcome helpers, and Patient CRUD endpoints.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
vcf-annotator
Annotate VCF variants with VEP, ClinVar, gnomAD frequencies, and ancestry-aware context. Generates prioritised variant reports.
chemist-analyst
Analyzes events through chemistry lens using molecular structure, reaction mechanisms, thermodynamics, kinetics, and analytical techniques (spectroscopy, chromatography, mass spectrometry). Provides insights on chemical processes, material properties, reaction pathways, synthesis, and analytical methods. Use when: Chemical reactions, material analysis, synthesis planning, process optimization, environmental chemistry. Evaluates: Molecular structure, reaction mechanisms, yield, selectivity, safety, environmental impact.
bio-alignment-io
Read, write, and convert multiple sequence alignment files using Biopython Bio.AlignIO. Supports Clustal, PHYLIP, Stockholm, FASTA, Nexus, and other alignment formats for phylogenetics and conservation analysis. Use when reading, writing, or converting alignment file formats.
sleep-analyzer
分析睡眠数据、识别睡眠模式、评估睡眠质量,并提供个性化睡眠改善建议。支持与其他健康数据的关联分析。
metabolomics-workbench-database
Access NIH Metabolomics Workbench via REST API (4,200+ studies). Query metabolites, RefMet nomenclature, MS/NMR data, m/z searches, study metadata, for metabolomics and biomarker discovery.
bio-hi-c-analysis-matrix-operations
Balance, normalize, and transform Hi-C contact matrices using cooler and cooltools. Apply iterative correction (ICE), compute expected values, and generate observed/expected matrices. Use when normalizing or transforming Hi-C matrices.
Didn't find tool you were looking for?