Service Request Lifecycle
Service Requests (FHIR ServiceRequest) are the core workflow resource in CareLaunch. They represent requests for healthcare services — referrals, service orders, and registration workflows that may require scheduling, payment, or both.
This document describes every status a ServiceRequest can hold, what triggers each transition, and what side effects occur.
Statuses
CareLaunch uses the standard FHIR R4 RequestStatus values:
| Status | Display Name | Description |
|---|---|---|
draft | Draft | Created but not yet submitted. Used during registration workflows while the patient completes scheduling and/or payment steps. |
active | Active (Open) | Open and awaiting fulfillment by a provider. The request has been submitted and is ready to be acted upon. |
on-hold | On Hold | Temporarily paused by an administrator. The request is not being actively worked but has not been cancelled. |
completed | Completed | The requested service has been fulfilled. This is a terminal state — no further status changes are allowed from the UI. |
revoked | Cancelled | The request has been cancelled or withdrawn by the patient or system. Associated appointments are automatically cancelled. |
entered-in-error | Error | Recorded by mistake. This is a terminal state — no further status changes are allowed from the UI. |
Status Flow Diagram
stateDiagram-v2
[*] --> draft : Patient starts registration/workflow
[*] --> active : Direct creation (no workflow steps)
draft --> active : Payment completed (auto)\nOR workflow completed\nOR admin manual update
draft --> revoked : Patient dismisses workflow\nOR workflow expires
active --> on_hold : Admin pauses request
active --> completed : Admin marks fulfilled
active --> revoked : Admin cancels request
active --> entered_in_error : Admin marks as error
on_hold --> active : Admin resumes request
completed --> [*]
revoked --> [*]
entered_in_error --> [*]
state "draft" as draft
state "active" as active
state "on-hold" as on_hold
state "completed" as completed
state "revoked" as revoked
state "entered-in-error" as entered_in_error
Transition Details
Creation → draft
Trigger: Patient initiates a registration workflow (portal or microsite) that includes scheduling and/or payment steps.
- A
ServiceRequestis created withstatus: draftandintent: order. - A
workflow-metadataFHIR extension is attached containing:requiresScheduling— whether the patient must book an appointmentrequiresPayment— whether the patient must complete paymentworkflowCreatedAt— timestamp of workflow initiationworkflowExpiresAt— expiration timestamp (auto-dismissed after this time)workflowType— type of workflow (e.g.,service_request)paymentProductId,healthcareServiceId,healthcareServiceName— context for resumption
- The patient's managing organization is set as the
requesterfor automation filtering.
Creation → active
Trigger: Direct creation via the admin UI or API without workflow steps, or when requiresScheduling and requiresPayment are both false.
- Default status is
activewhen created through thePOST /api/ServiceRequestendpoint without an explicit status. - The
intentdefaults toorderandauthoredOnis set to the current UTC time.
draft → active
Triggers:
- Payment completed (automatic): When a Stripe checkout session completes and the linked ServiceRequest has
status: draft, the payment status extension is set topaidand the status is automatically promoted toactive. - Workflow completed: When a patient finishes all required workflow steps (scheduling + payment), the registration controller sets status to
active. - Admin manual update: An administrator can change status to
activevia the admin UI (PUT endpoint, requires admin role).
Side effects:
- Automation triggers configured for "Resource Update" on ServiceRequest will fire.
draft → revoked
Triggers:
- Patient dismisses workflow: The patient clicks "Dismiss" on an incomplete workflow in the portal or during registration. Calls
POST /api/ServiceRequest/{id}/dismissorPOST /api/Registration/workflow/{id}/dismiss. - Workflow expires: When a patient returns to the portal and the
workflowExpiresAttimestamp has passed, the system automatically sets the status torevokedduring the incomplete-workflow check.
Side effects:
- All non-terminal appointments linked via
based-onreference are cancelled with reasonservice-request-dismissed. - Scheduled reminders and late/no-show checks for those appointments are cancelled.
- Automation triggers for "Resource Update" on ServiceRequest will fire.
active → on-hold
Trigger: Administrator manually changes status via the admin UI.
Side effects: None beyond the status change. The request remains visible but is not actively being worked.
active → completed
Trigger: Administrator marks the service request as fulfilled via the admin UI.
Side effects:
- Status becomes terminal — no further changes allowed from the UI.
- Automation triggers for "Resource Update" on ServiceRequest will fire.
active → revoked
Trigger: Administrator cancels the request via the admin UI.
Side effects:
- Automation triggers for "Resource Update" on ServiceRequest will fire.
active → entered-in-error
Trigger: Administrator marks the request as entered in error via the admin UI.
Side effects:
- Status becomes terminal — no further changes allowed from the UI.
on-hold → active
Trigger: Administrator resumes a paused request via the admin UI.
Deletion
Trigger: Administrator deletes a ServiceRequest via DELETE /api/ServiceRequest/{id} (requires admin role).
- The resource is permanently removed from the FHIR data store.
- Automation triggers for "Resource Delete" on ServiceRequest will fire.
Payment Status (Extension)
In addition to the FHIR status, ServiceRequests that involve payment carry a custom extension tracking payment progress:
Extension URL: http://orbital.health/fhir/extension/payment-status
| Payment Status | Description |
|---|---|
paid | Payment successfully completed via Stripe. Automatically promotes draft → active. |
unpaid | Payment has not yet been made (default when payment is required). |
pending | Payment is being processed. |
refunded | Payment was refunded via Stripe. Set automatically on charge.refunded or charge.dispute.created webhook events. |
failed | Payment attempt failed. Set on checkout.session.async_payment_failed or payment_intent.payment_failed webhook events. |
expired | Checkout session expired before payment was completed. Set on checkout.session.expired webhook event. |
not-required | The service does not require payment (determined by requiresPayment: false in workflow metadata). |
Payment Status Transitions
stateDiagram-v2
[*] --> unpaid : ServiceRequest created with payment required
[*] --> not_required : ServiceRequest created without payment
unpaid --> paid : checkout.session.completed
unpaid --> expired : checkout.session.expired
unpaid --> failed : payment_intent.payment_failed
paid --> refunded : charge.refunded / charge.dispute.created
expired --> paid : Patient retries and succeeds
failed --> paid : Patient retries and succeeds
state "unpaid" as unpaid
state "paid" as paid
state "refunded" as refunded
state "failed" as failed
state "expired" as expired
state "not-required" as not_required
Payment Side Effects
When payment status changes to paid:
- The ServiceRequest status is promoted from
drafttoactive(if currently draft). - If the ServiceRequest has a linked appointment with a stored
appointmentStatusAfterPaymentvalue, the appointment status is updated accordingly. - Post-payment appointment notifications are sent.
When payment status changes to refunded:
- The related Invoice status is updated.
- No automatic change to the ServiceRequest FHIR status.
When payment status changes to failed or expired:
- No automatic change to the ServiceRequest FHIR status.
- The patient can retry payment from the portal.
Automation Integration
ServiceRequests participate in the CareLaunch automation engine:
- Resource Creation trigger: Fires when a new ServiceRequest is created. Can be configured to create follow-up resources (tasks, communications, observations) automatically.
- Resource Update trigger: Fires when a ServiceRequest is updated (including status changes). Useful for notifications when requests move to
activeorcompleted. - Resource Delete trigger: Fires when a ServiceRequest is deleted.
Automations can filter on the ServiceRequest's requester field (populated with the patient's managing organization) for organization-scoped automation rules.
Workflow Recovery
When a patient abandons a registration workflow mid-process, the draft ServiceRequest persists with its workflow metadata. When the patient returns:
- The portal checks for incomplete workflows via
GET /api/ServiceRequest/incompleteorGET /api/Registration/workflow. - Draft ServiceRequests with
workflow-metadataextensions are returned, along with completion status for each step (scheduling, payment). - Expired workflows (past
workflowExpiresAt) are automatically dismissed (status set torevoked). - The patient can resume from where they left off or dismiss the workflow.
Access Control
| Action | Who Can Perform |
|---|---|
| Create | Any authenticated user (patient or admin) |
| Read (own) | Patient who is the subject, or proxy via RelatedPerson |
| Read (any) | Platform Admin, Organization Admin, Instance Admin |
| Update status | Platform Admin, Organization Admin, Instance Admin, Base Admin |
| Update subject (draft only) | Patient who is the current subject (for dependent scheduling) |
| Dismiss workflow | Patient who is the subject (or proxy) |
| Complete workflow | Patient who is the subject (or proxy) |
| Delete | Platform Admin, Organization Admin, Instance Admin, Base Admin |
Edge Cases
State-Based Service Restrictions
When a HealthcareService has coverageStates configured, ServiceRequest creation is blocked if the patient's home address state is not in the allowed list.
Unpaid Invoice Enforcement
When the requirePaidInvoices query parameter is set to true during creation, the system checks for outstanding invoices (status draft or issued) and blocks creation if any exist.
Race Conditions
The payment processing system handles race conditions between checkout session completion and expiration events. If a ServiceRequest already has payment status paid or refunded, expiration updates are skipped to prevent incorrect status regression.
Subject Updates
Only draft ServiceRequests can have their subject (patient) updated. This supports dependent scheduling workflows where a parent starts the workflow and then selects a dependent as the actual patient.