Systems API
HMIS exposes a versioned REST JSON API at /api/v1. Express route handlers enforce RBAC, clinical branch geography, and record scope on every protected path. Canonical contract: docs/info/API-CONTRACT.md.
Auth
Bearer JWT access token; HttpOnly refresh cookie in production (USE_HTTPONLY_REFRESH=1).
Branch scope
Working-in campus via session activeBranchId / X-Working-Branch-Id on stamped ops.
Rate limits
Default 300 req/min/client; stricter auth and public buckets.
OpenAPI
Full Express inventory in api/openapi/route-descriptors.json (tiered schemas); committed openapi.json; drift fails on undocumented routes in npm run test:deploy.
Authentication flow
POST /api/v1/auth/login → access JWT (+ refresh cookie when configured) Authorization: Bearer <jwt> → all protected REST calls POST /api/v1/auth/refresh → rotate refresh token POST /api/v1/auth/mfa/* → TOTP verify when mfaRequired (see docs/info/MFA-AUTHENTICATION.md) Runtime authorization: allowed = modulePermission AND branchScope AND recordScope [AND orgUnit scope]
Machine integrations (lab/radiology analyzers) use OAuth client credentials where configured. Public patient share links use a separate PUBLIC_SHARE_JWT_SECRET, not the staff JWT.
Request & response conventions
Requests
- Content-Type:
application/json(multipart for uploads) - JSON body limit: 2 MB default (API_JSON_BODY_LIMIT)
- UUIDs: RFC 4122 strings in path and body
- Dates: ISO 8601 UTC
Paginated lists
{ "items": [], "total": 100, "page": 1, "pageSize": 25, "totalPages": 4 }Query: page, pageSize (per-route caps apply).
Error envelope
{ "error": "Human-readable message", "code": "OPTIONAL_MACHINE_CODE", "details": {} }Key route groups
Full surface in generated OpenAPI
/api/v1/authLogin, refresh, password, MFA/api/v1/patientsPatient chart, list/api/v1/billingInvoices, payments, advances/api/v1/governancePolicies, DR drills, training, helpdesk/api/v1/privacyDSR, erasure jobs/api/v1/report-builderDynamic SQL reports/api/v1/aiAI chat, SQL assistant/api/v1/publicBooking, share validation/api/v1/healthLiveness/api/v1/metricsPrometheus scrapeIdempotency & auditing
Financial and critical writes support client idempotency keys via POST /api/v1/governance/idempotency/claim. Retries with the same key return the original result. Mutations fire AuditLog entries; exports require explicit permissions.
Technical Architecture
| Field | Type | Institutional Role |
|---|---|---|
| Authorization | Header | Bearer JWT on protected routes. |
| x-hmis-step-up | Header | Step-up token for privileged mutations. |
| x-permissions | OpenAPI ext | Required grants per operation. |
| x-branch-scope | OpenAPI ext | Working-in / Register-at / chart exception notes. |
Note: Sensitive fields use AES-256 field-level encryption where applicable.
Governance & Power
settings:adminaudit:export- docs/info/API-CONTRACT.md
- docs/info/SECURITY-HARDENING.md
- docs/development/DEPRECATION-REGISTER.md
- docs/info/ADR-001-branch-vs-orgunit.md
- docs/info/ADR-002-chat-mongodb-persistence.md
- docs/info/threat-models/report-builder.md
Regenerate OpenAPI: node scripts/generate-openapi.mjs · Check drift: npm run check:openapi-drift · Live spec: GET /api/v1/openapi.json (settings:admin) · Swagger UI GET /api/v1/docs/ (settings:admin).
Related: Security & Access · System Architecture · Machine Integration
- Obtain service account; confirm Roles grants
- Set Working-in branch header for campus writes
- Handle 401 with refresh; respect pagination and export caps
- Use idempotency keys for invoice/payment creates
- Never embed PHI in GET query strings; TLS 1.2+ only