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.
Best use case
fhir-developer-skill is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
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.
Teams using fhir-developer-skill should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/fhir-developer-skill/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How fhir-developer-skill Compares
| Feature / Agent | fhir-developer-skill | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
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.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agents for Coding
Browse AI agent skills for coding, debugging, testing, refactoring, code review, and developer workflows across Claude, Cursor, and Codex.
Cursor vs Codex for AI Workflows
Compare Cursor and Codex for AI coding workflows, repository assistance, debugging, refactoring, and reusable developer skills.
SKILL.md Source
# 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:**
```python
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:**
```typescript
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`):
```python
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`:
```json
{"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB"}
```
**CodeableConcept** - Used by `Observation.code`, `Condition.code`:
```json
{"coding": [{"system": "http://loinc.org", "code": "8480-6"}], "text": "Systolic BP"}
```
### Reference
```json
{"reference": "Patient/123", "display": "John Smith"}
```
### Identifier
```json
{"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](references/resource-examples.md)**.
Quick reference for error responses:
```json
{
"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](references/resource-examples.md)**: Complete JSON structures for Patient, Observation, Encounter, Condition, MedicationRequest, OperationOutcome, CapabilityStatement
- **[SMART on FHIR Authorization](references/smart-auth.md)**: OAuth flows, scope syntax (v1/v2), backend services, scope enforcement
- **[Pagination](references/pagination.md)**: Search result pagination, `_count`/`_offset` parameters, link relations
- **[Bundle Operations](references/bundles.md)**: Transaction vs batch semantics, atomicity, processing order
---
## Implementation Checklist
1. Set `Content-Type: application/fhir+json` on all responses
2. Return `meta.versionId` and `meta.lastUpdated` on resources
3. Return `Location` header on create: `/Patient/{id}`
4. Return `ETag` header: `W/"{versionId}"`
5. Use OperationOutcome for all error responses
6. Validate required fields → 422 for missing
7. Validate enum values → 422 for invalid
8. Search returns Bundle with `type: "searchset"`
---
## Quick Start Script
To scaffold a new FHIR API project with correct Pydantic v2 patterns:
```bash
python scripts/setup_fhir_project.py my_fhir_api
```
Creates a FastAPI project with correct models, OperationOutcome helpers, and Patient CRUD endpoints.Related Skills
zinc-database
Access ZINC (230M+ purchasable compounds). Search by ZINC ID/SMILES, similarity searches, 3D-ready structures for docking, analog discovery, for virtual screening and drug discovery.
zarr-python
Chunked N-D arrays for cloud storage. Compressed arrays, parallel I/O, S3/GCS integration, NumPy/Dask/Xarray compatible, for large-scale scientific computing pipelines.
xlsx
Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or from other data sources; or convert between tabular file formats. Trigger especially when the user references a spreadsheet file by name or path — even casually (like "the xlsx in my downloads") — and wants something done to it or produced from it. Also trigger for cleaning or restructuring messy tabular data files (malformed rows, misplaced headers, junk data) into proper spreadsheets. The deliverable must be a spreadsheet file. Do NOT trigger when the primary deliverable is a Word document, HTML report, standalone Python script, database pipeline, or Google Sheets API integration, even if tabular data is involved.
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
writing-plans
Use when you have a spec or requirements for a multi-step task, before touching code
wikipedia-search
Search and fetch structured content from Wikipedia using the MediaWiki API for reliable, encyclopedic information
wellally-tech
Integrate digital health data sources (Apple Health, Fitbit, Oura Ring) and connect to WellAlly.tech knowledge base. Import external health device data, standardize to local format, and recommend relevant WellAlly.tech knowledge base articles based on health data. Support generic CSV/JSON import, provide intelligent article recommendations, and help users better manage personal health data.
weightloss-analyzer
分析减肥数据、计算代谢率、追踪能量缺口、管理减肥阶段
<!--
# COPYRIGHT NOTICE
verification-before-completion
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
vcf-annotator
Annotate VCF variants with VEP, ClinVar, gnomAD frequencies, and ancestry-aware context. Generates prioritised variant reports.
vaex
Use this skill for processing and analyzing large tabular datasets (billions of rows) that exceed available RAM. Vaex excels at out-of-core DataFrame operations, lazy evaluation, fast aggregations, efficient visualization of big data, and machine learning on large datasets. Apply when users need to work with large CSV/HDF5/Arrow/Parquet files, perform fast statistics on massive datasets, create visualizations of big data, or build ML pipelines that do not fit in memory.