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" \
-F "[email protected]"
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 aSubscriptionso an application is notified over a webhook, or polls, when Tasks become due. The server creates and owns theSubscriptionon the caller's behalf, so the caller needs only thesubscribepermission, not the right to writeSubscriptionresources. 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 noSubscriptionand no webhook endpoint. They use a separatematerializepermission and are intended for privileged or service-to-service callers that need to start or stop materialization irrespective of anySubscription. 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:
| Name | Type | Required | Description |
|---|---|---|---|
channelType | string | no | Delivery channel: rest-hook (default), message, or nop. Use nop for a polling-only setup that materializes Tasks without delivering webhooks. |
endpoint | url | conditional | The webhook URL that receives notifications when Tasks become due. Required for rest-hook and message; ignored for nop. |
header | string | no | Repeating parameter. Each value is an HTTP header sent with every webhook delivery. Ignored for nop. |
end | dateTime | no | Requested subscription end. Capped at the server's maximum subscription TTL; a value in the past is rejected. Defaults to now + max-ttl. |
_synchronous | boolean | no | When 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.
$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:
| Name | Type | Required | Description |
|---|---|---|---|
end | dateTime | no | New 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-eventsrequire thesubscribeoperation onCarePlan, plusreadon the instance.$materializeand$dematerializerequire thematerializeoperation onCarePlan.$materializealso reads and updates the instance, so the caller needs a CarePlanreadrule; grantingmaterializealone returns403 Forbidden. Wildcard rules (resource: "*") are rejected at startup formaterialize, as for token-generation rules.