Skip to main content

Custom Operations

Fire Arrow Server extends the standard FHIR REST API with several custom operations. These follow the FHIR extended operations pattern, using the $ prefix.

$me - Current User Identity

Returns the FHIR resource linked to the currently authenticated user. This is the quickest way to verify that authentication and identity resolution are working correctly.

Request:

curl http://localhost:8080/fhir/\$me \
-H "Authorization: Bearer <your-token>"

Response: The resolved FHIR resource (Patient, Practitioner, RelatedPerson, or Device) as JSON.

$binary-upload - Upload Binary Files

Uploads a binary file and stores it in Azure Blob Storage. The returned firearrow:// URL can be embedded in any FHIR resource field. See Binary Storage for the full guide.

Multipart upload:

curl -X POST http://localhost:8080/fhir/\$binary-upload \
-H "Authorization: Bearer <your-token>" \
-F "resourceReference=Patient/123" \

JSON upload (base64):

curl -X POST http://localhost:8080/fhir/\$binary-upload \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceReference": "Patient/123",
"data": "<base64-encoded-content>",
"contentType": "image/jpeg"
}'

Response: A FHIR Parameters resource containing the storage URL, content type, and file size:

{
"resourceType": "Parameters",
"parameter": [
{ "name": "url", "valueUrl": "firearrow://container/abc123-def456.jpg" },
{ "name": "contentType", "valueString": "image/jpeg" },
{ "name": "size", "valueInteger": 204800 }
]
}

$generate-durable-token - Create a Long-Lived API Token

Generates a durable API token for programmatic access to a specific FHIR resource. Durable tokens are prefixed with fa_ and remain valid until explicitly revoked. See API Tokens for the full lifecycle, security model, and use cases.

Request:

curl -X POST http://localhost:8080/fhir/Patient/123/\$generate-durable-token \
-H "Authorization: Bearer <your-token>"

Response:

{
"resourceType": "Parameters",
"parameter": [
{ "name": "token", "valueString": "fa_abc123..." }
]
}

Use the token in subsequent requests:

curl http://localhost:8080/fhir/Patient/123 \
-H "Authorization: Bearer fa_abc123..."

$generate-one-time-token - Create a Single-Use Token

Generates a one-time API token that is invalidated after its first use. One-time tokens are prefixed with fo_ and are ideal for secure handoffs like magic links or single-use downloads.

Request:

curl -X POST http://localhost:8080/fhir/Patient/123/\$generate-one-time-token \
-H "Authorization: Bearer <your-token>"

Response:

{
"resourceType": "Parameters",
"parameter": [
{ "name": "token", "valueString": "fo_xyz789..." }
]
}

$redeem-token - Exchange a Token for a Session

Redeems a durable or one-time API token, returning the identity associated with it. This is useful for validating tokens from external systems.

Request:

curl -X POST http://localhost:8080/fhir/\$redeem-token \
-H "Content-Type: application/json" \
-d '{
"resourceType": "Parameters",
"parameter": [
{ "name": "token", "valueString": "fa_abc123..." }
]
}'

Response: The FHIR resource (Patient, Practitioner, etc.) associated with the token.

CarePlan Scheduling Operations

Fire Arrow Server exposes five CarePlan instance operations that control server-side scheduling and Task materialization. They fall into two groups with different permissions and audiences:

  • Client subscriptions ($subscribe-due-events, $renew-due-events, $unsubscribe-due-events): manage a Subscription so an application is notified over a webhook, or polls, when Tasks become due. The server creates and owns the Subscription on the caller's behalf, so the caller needs only the subscribe permission, not the right to write Subscription resources. These also opt the CarePlan into scheduling. This is the path client apps use.
  • Direct materialization control ($materialize, $dematerialize): toggle the scheduling opt-in tag with no Subscription and no webhook endpoint. They use a separate materialize permission and are intended for privileged or service-to-service callers that need to start or stop materialization irrespective of any Subscription. Available since 1.11.0.

All five require CarePlan Events to be enabled in server configuration (fire-arrow.careplan-events.enabled: true). See that guide for the full model, including when each path needs a HAPI subscription channel enabled.

$subscribe-due-events - Subscribe to CarePlan Events

Opts the CarePlan into scheduling and creates a Subscription that fires when scheduled CarePlan activities become due. This is a convenience wrapper around the FHIR Subscription resource, see CarePlan Events for the full guide.

Request:

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" }
]
}'

Parameters:

NameTypeRequiredDescription
channelTypestringnoDelivery channel: rest-hook (default), message, or nop. Use nop for a polling-only setup that materializes Tasks without delivering webhooks.
endpointurlconditionalThe webhook URL that receives notifications when Tasks become due. Required for rest-hook and message; ignored for nop.
headerstringnoRepeating parameter. Each value is an HTTP header sent with every webhook delivery. Ignored for nop.
enddateTimenoRequested subscription end. Capped at the server's maximum subscription TTL; a value in the past is rejected. Defaults to now + max-ttl.
_synchronousbooleannoWhen true, the operation blocks until immediate Task materialization has completed and the response includes a task part for each materialized Task. Bounded at 10 seconds; on timeout, the response carries synchronousTimedOut=true and the asynchronous materialization continues in the background. Default false. Available since 1.10.0.

Response: A FHIR Parameters resource describing the created (or renewed) subscription:

{
"resourceType": "Parameters",
"parameter": [
{ "name": "subscriptionId", "valueId": "Subscription/123" },
{ "name": "effectiveEnd", "valueInstant": "2026-06-01T00:00:00Z" },
{ "name": "action", "valueString": "created" }
]
}

action is created for a new subscription or renewed when an existing subscription for the same caller and CarePlan was updated. When _synchronous=true, the response additionally lists the materialized Tasks, see Waiting for Initial Materialization for the full payload shape.

Subscription activation requires a channel

$subscribe-due-events always writes the Subscription row, but synchronous materialization and webhook delivery only run once HAPI activates the subscription, which requires the matching channel to be enabled (for example hapi.fhir.subscription.resthook_enabled: true, or message_enabled for the nop channel). With no channel enabled, the subscription stays in requested status and _synchronous=true returns synchronousTimedOut=true. See CarePlan Events. A privileged backend that does not need a Subscription can use $materialize (1.11+) to schedule Tasks without this requirement.

$renew-due-events - Extend an Existing Subscription

Extends the end date on the caller's own due-event subscriptions for the CarePlan and re-triggers materialization. Use it to keep a subscription alive before it reaches the maximum TTL.

Request:

curl -X POST http://localhost:8080/fhir/CarePlan/789/\$renew-due-events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Parameters",
"parameter": [
{ "name": "end", "valueDateTime": "2026-09-01T00:00:00Z" }
]
}'

Parameters:

NameTypeRequiredDescription
enddateTimenoNew requested end date. Capped at the server's maximum subscription TTL. Defaults to now + max-ttl.

Response: A FHIR Parameters resource listing each renewed subscription:

{
"resourceType": "Parameters",
"parameter": [
{
"name": "subscription",
"part": [
{ "name": "subscriptionId", "valueId": "Subscription/123" },
{ "name": "effectiveEnd", "valueInstant": "2026-09-01T00:00:00Z" }
]
},
{ "name": "count", "valueInteger": 1 }
]
}

If the caller has no subscriptions for the CarePlan, the operation returns 404 Not Found.

$unsubscribe-due-events - Stop Notifications

Deletes the caller's own due-event subscriptions for the CarePlan. When no subscriptions remain for the CarePlan across all callers, the server also removes the scheduling tag and deletes pending (non-terminal) Tasks; Tasks in a terminal status are kept.

Request:

curl -X POST http://localhost:8080/fhir/CarePlan/789/\$unsubscribe-due-events \
-H "Authorization: Bearer <your-token>"

Response: A FHIR Parameters resource with status=success. The operation does not return counts or IDs. Repeat calls are safe and also return status=success.

{
"resourceType": "Parameters",
"parameter": [
{ "name": "status", "valueString": "success" }
]
}

$materialize - Start Materialization Directly

Opts the CarePlan into server-side Task materialization by adding the scheduling tag (https://firearrow.io/fhir/careplan-scheduling / scheduled). It does not create a Subscription and does not need a webhook endpoint or a HAPI subscription channel. It is a privileged, service-to-service control that uses the materialize permission, not the path a client app uses to receive due events. Client apps should use $subscribe-due-events (which creates a Subscription on their behalf). Available since 1.11.0.

Request:

curl -X POST http://localhost:8080/fhir/CarePlan/789/\$materialize \
-H "Authorization: Bearer <your-token>"

Response: A FHIR Parameters resource with status=success:

{
"resourceType": "Parameters",
"parameter": [
{ "name": "status", "valueString": "success" }
]
}

Calling $materialize again on a CarePlan that is already opted in is a no-op and still returns status=success. The operation does not support _synchronous and returns no Task IDs. After calling it, poll for due Tasks:

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

$dematerialize - Stop Scheduling

Removes the scheduling tag from the CarePlan. Materialization of new Tasks stops. Existing Tasks and any Subscriptions already in place are left untouched, so it does not stop a client subscription created by $subscribe-due-events; use $unsubscribe-due-events for that. Available since 1.11.0.

Request:

curl -X POST http://localhost:8080/fhir/CarePlan/789/\$dematerialize \
-H "Authorization: Bearer <your-token>"

Response: A FHIR Parameters resource with status=success. Removing the tag from a CarePlan that is not opted in also returns status=success.

PlanDefinition $apply - Apply with Timezone Support

Fire Arrow Server enhances the standard FHIR PlanDefinition/$apply operation with timezone-aware scheduling. When applying a PlanDefinition that contains timing-based activities, you can specify the patient's timezone to ensure activities are scheduled at the correct local times.

Request:

curl -X POST http://localhost:8080/fhir/PlanDefinition/plan-1/\$apply \
-H "Content-Type: application/fhir+json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"resourceType": "Parameters",
"parameter": [
{ "name": "subject", "valueString": "Patient/123" },
{ "name": "timezone", "valueString": "Europe/Zurich" }
]
}'

Response: The resulting CarePlan resource with activities scheduled according to the PlanDefinition, adjusted for the specified timezone.

Key behaviors of the enhanced $apply:

  • Timezone-aware scheduling - timing-based activities respect the patient's local timezone
  • Extension propagation - extensions on PlanDefinition actions are carried through to the resulting CarePlan activities
  • Activity detail mapping - PlanDefinition action definitions are mapped to CarePlan activity details

Authorization for Custom Operations

Custom operations require specific authorization rules. Add rules for the operations your clients need:

fire-arrow:
authorization:
validation-rules:
- client-role: "Patient"
resource: "Patient"
operation: "me"
validator: "PatientCompartment"
- client-role: "Patient"
resource: "Binary"
operation: "binary-upload"
validator: "PatientCompartment"
- client-role: "Practitioner"
resource: "Patient"
operation: "generate-durable-token"
validator: "LegitimateInterest"
- client-role: "Practitioner"
resource: "CarePlan"
operation: "subscribe"
validator: "LegitimateInterest"
- client-role: "Practitioner"
resource: "CarePlan"
operation: "materialize"
validator: "LegitimateInterest"

The CarePlan scheduling operations map to two authorization operations:

  • $subscribe-due-events, $renew-due-events, and $unsubscribe-due-events require the subscribe operation on CarePlan, plus read on the instance.
  • $materialize and $dematerialize require the materialize operation on CarePlan. $materialize also reads and updates the instance, so the caller needs a CarePlan read rule; granting materialize alone returns 403 Forbidden. Wildcard rules (resource: "*") are rejected at startup for materialize, as for token-generation rules.