Skip to main content

CarePlan Events

Fire Arrow Server can automatically schedule CarePlan activities into Task resources and notify your application when those tasks become due. This enables server-side care plan orchestration -- define a care plan once, and the server handles the timeline, creates the tasks, and calls your webhook when it's time for action.

Overview

In many digital health applications, care plans include recurring activities: daily medication reminders, weekly check-ins, monthly assessments. Traditionally, the application has to manage the scheduling logic itself, which is complex, error-prone, and difficult to scale.

Fire Arrow Server's CarePlan event system solves this by:

  1. Materializing CarePlan activities into individual Task resources up to a configurable time horizon
  2. Transitioning tasks to "ready" status when they become due
  3. Notifying your application via webhooks when tasks are ready

Enabling CarePlan Scheduling

CarePlan scheduling is opt-in per CarePlan. Tag a CarePlan with the scheduling meta tag to tell Fire Arrow Server to manage its activities:

{
"resourceType": "CarePlan",
"meta": {
"tag": [{
"system": "https://firearrow.io/fhir/careplan-scheduling",
"code": "scheduled"
}]
},
"status": "active",
"intent": "plan",
"subject": { "reference": "Patient/123" },
"activity": [
{
"detail": {
"description": "Daily blood pressure check",
"status": "scheduled",
"scheduledTiming": {
"repeat": {
"frequency": 1,
"period": 1,
"periodUnit": "d"
}
}
}
},
{
"detail": {
"description": "Weekly symptom questionnaire",
"status": "scheduled",
"scheduledTiming": {
"repeat": {
"frequency": 1,
"period": 1,
"periodUnit": "wk"
}
}
}
}
]
}

Once this CarePlan is created (or updated with the tag), Fire Arrow Server begins materializing Task resources for each activity according to its schedule.

How Materialization Works

The server maintains a scheduling horizon -- a rolling time window (e.g., 30 days into the future) during which it creates Task resources. As time passes, the server continues to materialize new tasks to fill the horizon.

For each activity occurrence within the horizon:

  1. A Task resource is created with status draft
  2. The Task references the CarePlan via basedOn
  3. The Task has an executionPeriod indicating when it should be performed
  4. When the executionPeriod.start is reached, the server transitions the Task to ready status
  5. This status change triggers any active Subscriptions matching Task?status=ready
note

The server only materializes tasks within the horizon window. For a 30-day horizon, tasks more than 30 days in the future won't exist yet. They will be created as the horizon advances.

Subscribing to Due Events

When tasks become due (transition to ready), you'll want to be notified so your application can take action. There are two ways to set up these notifications.

Easy Way: $subscribe-due-events

The $subscribe-due-events operation is a convenience endpoint that creates a properly configured Subscription for a specific CarePlan:

curl -X POST http://localhost:8080/fhir/CarePlan/789/\$subscribe-due-events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Parameters",
"parameter": [
{ "name": "endpoint", "valueUrl": "https://your-app.example.com/webhooks/careplan-tasks" }
]
}'

The server creates a Subscription with:

  • Criteria: Task?status=ready&based-on=CarePlan/789
  • Channel: REST hook to your specified endpoint
  • End date: Automatically set based on the server's maximum subscription TTL

Advanced: Create a Subscription Directly

For more control, create a FHIR Subscription resource yourself:

curl -X POST http://localhost:8080/fhir/Subscription \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Subscription",
"status": "requested",
"criteria": "Task?status=ready&based-on=CarePlan/789",
"channel": {
"type": "rest-hook",
"endpoint": "https://your-app.example.com/webhooks/careplan-tasks",
"payload": "application/fhir+json"
},
"end": "2026-04-28T00:00:00Z"
}'
Subscription End Date

The end field is required on all Subscriptions. The server enforces a maximum TTL -- if the end date exceeds the maximum, it will be clamped to the allowed maximum.

Webhook Payload

When a Task transitions to ready, Fire Arrow Server sends a minimal notification to your webhook endpoint. The payload is a lightweight hint that does not contain Protected Health Information (PHI):

{
"resourceType": "Task",
"id": "task-456",
"status": "ready",
"basedOn": [{ "reference": "CarePlan/789" }]
}

Your application should then fetch the full Task resource via the API to get all details:

curl http://localhost:8080/fhir/Task/task-456 \
-H "Authorization: Bearer <your-token>"

This two-step pattern keeps webhook payloads small and avoids transmitting sensitive data over webhook channels.

Completing Tasks

When the patient or practitioner completes an activity, update the Task status:

curl -X PUT http://localhost:8080/fhir/Task/task-456 \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Task",
"id": "task-456",
"status": "completed",
"basedOn": [{ "reference": "CarePlan/789" }],
"executionPeriod": {
"start": "2026-03-28T08:00:00Z",
"end": "2026-03-28T08:15:00Z"
}
}'

Nop Channel: Materialization Without Webhooks

If you don't need real-time webhook notifications, you can still benefit from CarePlan materialization by using the nop channel (no-operation). The server creates and transitions Task resources on schedule, and your application polls for ready tasks:

curl "http://localhost:8080/fhir/Task?status=ready&based-on=CarePlan/789" \
-H "Authorization: Bearer <your-token>"

Or via GraphQL:

{
TaskList(status: "ready", based_on: "CarePlan/789") {
id
code { text }
executionPeriod { start end }
}
}

This approach is simpler to set up and avoids the need for a publicly accessible webhook endpoint.

Configuration

Configure CarePlan events under the fire-arrow.careplan-events key in your application.yaml:

fire-arrow:
careplan-events:
enabled: true
scheduling:
horizon-duration: "P30D" # Materialize tasks 30 days ahead
check-interval: "PT1H" # Check for new materializations every hour
subscriptions:
max-ttl: "P90D" # Maximum subscription lifetime
delivery:
max-retries: 3 # Retry failed webhook deliveries
retry-interval: "PT30S" # Wait between retries
failure-threshold: 5 # Disable subscription after N consecutive failures
cleanup:
interval: "P1D" # Run cleanup daily
completed-task-retention: "P90D" # Keep completed tasks for 90 days
task:
auto-transition-to-ready: true # Automatically transition tasks to ready when due
PropertyDescriptionDefault
enabledEnable CarePlan event processingfalse
scheduling.horizon-durationHow far ahead to materialize tasks (ISO 8601 duration)P30D
scheduling.check-intervalHow often to check for new materializationsPT1H
subscriptions.max-ttlMaximum allowed subscription lifetimeP90D
delivery.max-retriesNumber of webhook delivery retry attempts3
delivery.retry-intervalDelay between retry attemptsPT30S
delivery.failure-thresholdConsecutive failures before disabling a subscription5
cleanup.intervalHow often the cleanup job runsP1D
cleanup.completed-task-retentionHow long to keep completed tasksP90D
task.auto-transition-to-readyAutomatically move tasks to ready when duetrue

End-to-End Example

Here's a complete walkthrough: create a CarePlan, subscribe to events, receive a webhook, and fetch the task.

1. Create a CarePlan with Scheduling

curl -X POST http://localhost:8080/fhir/CarePlan \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "CarePlan",
"meta": {
"tag": [{
"system": "https://firearrow.io/fhir/careplan-scheduling",
"code": "scheduled"
}]
},
"status": "active",
"intent": "plan",
"subject": { "reference": "Patient/123" },
"period": {
"start": "2026-03-28",
"end": "2026-06-28"
},
"activity": [{
"detail": {
"description": "Daily blood pressure measurement",
"status": "scheduled",
"scheduledTiming": {
"repeat": {
"frequency": 1,
"period": 1,
"periodUnit": "d",
"timeOfDay": ["08:00:00"]
}
}
}
}]
}'

The server returns the created CarePlan with an id (e.g., CarePlan/789).

2. Subscribe to Due Events

curl -X POST http://localhost:8080/fhir/CarePlan/789/\$subscribe-due-events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Parameters",
"parameter": [
{ "name": "endpoint", "valueUrl": "https://your-app.example.com/webhooks/bp-check" }
]
}'

3. Receive Webhook Notification

When the daily task becomes due (at 08:00), your endpoint receives:

{
"resourceType": "Task",
"id": "task-001",
"status": "ready",
"basedOn": [{ "reference": "CarePlan/789" }]
}

4. Fetch Full Task Details

curl http://localhost:8080/fhir/Task/task-001 \
-H "Authorization: Bearer <your-token>"

5. Complete the Task

After the patient takes their measurement:

curl -X PUT http://localhost:8080/fhir/Task/task-001 \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Task",
"id": "task-001",
"status": "completed",
"basedOn": [{ "reference": "CarePlan/789" }],
"executionPeriod": {
"start": "2026-03-28T08:00:00Z",
"end": "2026-03-28T08:10:00Z"
}
}'

The server continues materializing and transitioning tasks according to the CarePlan schedule for the duration of the care plan's period.