Skip to content
Research
Skill

/fhir-developer

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

BOOST
From plugin
healthcare
42510 skills3 agents7 MCP
Install
$ npx -y skills add anthropics/healthcare --skill fhir-developer --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/fhir-developer

Context preview

The summary Claude sees to decide when to auto-load this 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

SKILL.md

fhir-developer.SKILL.md
name: fhir-developer-skill
description: >
  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.

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

Read more
Ships withhealthcare

One plugin for healthcare work: skills for payer, provider, pharma, and general engineering, with hosted MCP servers connected. Skills only load when relevant, so the bundle stays cheap.

Get the whole plugin

Other skills on healthcare.