Care Plan Service — API Contracts
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
Conventions
- Base path:
/api/v1/care-plans(Kong route prefix) - Auth:
Authorization: Bearer <JWT>— Keycloak-issued,tenantIdfrom JWT claims - Tenant isolation: enforced server-side; clients cannot override
tenantId - Pagination:
?page=1&limit=20; response envelope:{ data, meta: { total, page, limit } } - Concurrency: every mutating response includes
version;PATCH/POSTstate changes requireversionin body - IDs: ULIDs with prefix (e.g.,
cp_01...,cpg_01...) - Content-Type:
application/json
Resource: Care Plans
POST /api/v1/care-plans
Create a new care plan.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
| Idempotency-Key | Optional header; recommended for retry safety |
Request body:
{
"patientId": "pat_01...",
"encounterId": "enc_01...",
"title": "Diabetes Management Plan",
"category": { "coding": [{ "system": "http://snomed.info/sct", "code": "734163000", "display": "Care plan" }] },
"description": "Quarterly HbA1c monitoring and lifestyle coaching"
}
Response 201:
{
"data": {
"id": "cp_01...",
"patientId": "pat_01...",
"status": "draft",
"version": 1,
"title": "Diabetes Management Plan",
"createdAt": "2026-04-18T10:00:00Z"
}
}
Errors: 400 VALIDATION_ERROR, 403 MODULE_NOT_LICENSED, 409 (duplicate Idempotency-Key)
GET /api/v1/care-plans
List care plans for a patient.
| Field | Value |
|---|---|
| Auth scope | care_plan:read |
| Query params | patientId (required), status (optional), page, limit |
Response 200:
{
"data": [
{ "id": "cp_01...", "patientId": "pat_01...", "title": "...", "status": "active", "version": 3 }
],
"meta": { "total": 1, "page": 1, "limit": 20 }
}
GET /api/v1/care-plans/:id
Get a single care plan with goals, activities, and care team.
| Field | Value |
|---|---|
| Auth scope | care_plan:read |
Response 200:
{
"data": {
"id": "cp_01...",
"patientId": "pat_01...",
"status": "active",
"version": 2,
"title": "Diabetes Management Plan",
"goals": [{ "id": "cpg_01...", "description": "Reduce HbA1c below 7%", "status": "in_progress", "dueAt": "2026-09-01" }],
"activities": [{ "id": "cpa_01...", "description": "Monthly dietitian visit", "status": "not_started", "assigneeId": "prac_01..." }],
"careTeam": [{ "id": "ctm_01...", "practitionerId": "prac_01...", "role": { "coding": [{ "code": "112247003", "display": "Medical doctor" }] } }]
}
}
Errors: 404 CARE_PLAN_NOT_FOUND
PATCH /api/v1/care-plans/:id
Update care plan fields. Requires current version.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
Request body:
{
"version": 2,
"title": "Updated Plan Title",
"description": "Updated description"
}
Response 200: Updated care plan object with incremented version.
Errors: 404, 409 VERSION_CONFLICT, 409 CARE_PLAN_CLOSED
POST /api/v1/care-plans/:id/activate
Transition plan from draft to active.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
| Body | { "version": <current> } |
Response 200: Updated plan with status: active.
POST /api/v1/care-plans/:id/review
Record a formal care plan review.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
Request body:
{ "version": 2, "reviewNote": "Goals on track; no changes needed", "nextReviewDue": "2026-07-01" }
Response 200: { "carePlanId": "cp_01...", "lastReviewedAt": "2026-04-18T10:00:00Z", "version": 3 }
POST /api/v1/care-plans/:id/close
Close (complete or revoke) a care plan.
| Field | Value |
|---|---|
| Auth scope | care_plan:admin |
| Body | { "version": 3, "closeReason": "completed" } — closeReason: completed or revoked |
Response 200: Plan with terminal status.
Resource: Goals
POST /api/v1/care-plans/:id/goals
Add a goal to an active care plan.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
Request body:
{
"description": "Achieve HbA1c < 7%",
"targetDetail": { "measure": { "coding": [{ "system": "http://loinc.org", "code": "4548-4", "display": "HbA1c" }] }, "detailQuantity": { "value": 7, "unit": "%" } },
"dueAt": "2026-09-01",
"status": "proposed"
}
Response 201: { "data": { "id": "cpg_01...", "carePlanId": "cp_01...", "status": "proposed", ... } }
PATCH /api/v1/care-plans/:id/goals/:goalId
Update goal status or target.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
| Body | { "status": "in_progress", "dueAt": "2026-10-01" } |
Response 200: Updated goal.
Resource: Activities
POST /api/v1/care-plans/:id/activities
Add an activity to a care plan.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
Request body:
{
"description": "Monthly dietitian consultation",
"assigneeId": "prac_01...",
"scheduleDetail": { "repeat": { "frequency": 1, "period": 1, "periodUnit": "mo" } },
"status": "not_started"
}
Response 201: { "data": { "id": "cpa_01...", ... } }
POST /api/v1/care-plans/:id/activities/:activityId/complete
Mark an activity as complete.
| Field | Value |
|---|---|
| Auth scope | care_plan:write |
| Body | { "completionNote": "Patient attended; goals discussed" } |
Response 200: Updated activity with status: completed, completedAt.
Resource: CareTeam
PUT /api/v1/care-plans/:id/care-team
Replace the care team for a plan.
| Field | Value |
|---|---|
| Auth scope | care_plan:admin |
Request body:
{
"version": 2,
"members": [
{ "practitionerId": "prac_01...", "role": { "coding": [{ "code": "446050000", "display": "Primary care physician" }] }, "effectiveFrom": "2026-04-01" }
]
}
Response 200: Updated plan with new team.
FHIR R4 Read Surface
Served via fhir-gateway routing to care-plan-service FHIR adapter. Read/search only — no FHIR write interactions supported.
| Endpoint | FHIR resource | Parameters |
|---|---|---|
GET /fhir/R4/CarePlan/:id | CarePlan | — |
GET /fhir/R4/CarePlan?patient=Patient/:patientId | Bundle<CarePlan> | status, _count, _offset |
GET /fhir/R4/Goal/:id | Goal | — |
GET /fhir/R4/Goal?patient=Patient/:patientId | Bundle<Goal> | lifecycle-status |
GET /fhir/R4/CareTeam?patient=Patient/:patientId | Bundle<CareTeam> | — |
Common Error Codes
| HTTP | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing or invalid field |
| 403 | MODULE_NOT_LICENSED | Tenant lacks ehr.care_plans entitlement |
| 403 | INSUFFICIENT_SCOPE | Missing required RBAC scope |
| 403 | TENANT_ISOLATION_VIOLATION | Cross-tenant access attempt detected |
| 404 | CARE_PLAN_NOT_FOUND | Plan ID not found for tenant |
| 409 | VERSION_CONFLICT | Stale version in request |
| 409 | CARE_PLAN_CLOSED | Attempt to mutate completed/revoked plan |
| 422 | INVALID_STATUS_TRANSITION | Transition violates state machine |
| 422 | INVALID_CODING | Coding system/code invalid per terminology-service |