FHIR R6 API Incubator, published by HL7 International / FHIR Infrastructure. This guide is not an authorized publication; it is the continuous build for version 0.1.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/api-incubator-ig/ and changes regularly. See the Directory of published versions
| Page standards status: Trial-use |
Implementation Note: The FHIR Asynchronous Interaction Request Pattern is under active development. This page replaces the Bundle-based pattern published in FHIR R5; see the Migration Notes below. Participate in design discussions at chat.fhir.org.
All interactions defined in the RESTful API are synchronous by default. For interactions that may require significant processing time, servers can offer an asynchronous mode so the client does not need to wait for a response.
This pattern, based on RFC 7240, applies to Operations and Defined Interactions that a server elects to process asynchronously.
For example, an operation like $reindex, which might be used to process millions of Observation resources
against a new SearchParameter, is an ideal candidate for this pattern. A synchronous request would likely time
out, but an asynchronous request allows the client to submit the job and check on its status later. The final
result of a successful $reindex operation is typically a Parameters resource summarizing the outcome.
For exporting bulk/large result sets (e.g., all Patient resources), see the
Asynchronous Bulk Data Request pattern. When the _outputFormat parameter is present in a
request, the Bulk Data pattern SHALL be used. (See Design Questions Under Discussion
regarding this disambiguation rule.)
This diagram shows the complete lifecycle of an asynchronous request, including the paths for a job that is cancelled versus one that runs to completion, and the outcomes for a completed job that either succeeds or fails.
sequenceDiagram
participant Client
participant Server
Note over Client,Server: Step 1: Kick-off
Client->>Server: POST /.../$reindex (Prefer: respond-async)
Server->>Client: 202 Accepted (Content-Location: /whatever/path/1ab7162f-status)
alt Job is Cancelled by Client
Note over Client,Server: Step 3: Delete (Cancel)
Client->>Server: DELETE /whatever/path/1ab7162f-status
Server->>Client: 202 Accepted (Cancellation acknowledged)
Note right of Client: Subsequent polls would result in 404 Not Found.
else Job Runs to Completion
loop Polling Loop (Step 2)
Client->>Server: GET /whatever/path/1ab7162f-status
Server->>Client: 202 Accepted (Retry-After: 60)
end
Client->>Server: GET /whatever/path/1ab7162f-status (Final poll)
Server->>Client: 303 See Other (Location: /whatever/path/1ab7162f-result)
Note over Client,Server: Job execution is complete. Now fetch the final result.
alt Job Succeeded (Step 4)
Client->>Server: GET /whatever/path/1ab7162f-result
Server->>Client: 200 OK (Body: Parameters)
else Job Failed (Step 4)
Client->>Server: GET /whatever/path/1ab7162f-result
Server->>Client: 500 Internal Server Error (Body: OperationOutcome)
end
end
The kick-off request uses the same HTTP method, URL, and body as the corresponding synchronous interaction, but
adds a Prefer header to signal the asynchronous request.
Note on URLs in Examples: Throughout this specification, example URLs use paths like
/whatever/path/1ab7162f-statusand/whatever/path/1ab7162f-resultfor clarity. Servers may choose any URL structure for status and result endpoints. Clients must treat all URLs returned by the server (inContent-LocationandLocationheaders) as opaque values and should not parse or construct them.
Accept (string)
OperationOutcome). A client SHOULD provide this header. If omitted, the server MAY choose a
format or return an error.Prefer (string, required)
respond-async to request asynchronous processing.Headers on each request in this pattern apply only to that request's response: the kick-off request's headers govern the kick-off response, a status request's headers govern that status response, and the final result request's headers govern the result response. This separation allows different content negotiation for status versus result responses and eliminates ambiguity about which response a header applies to.
$reindex OperationHere, the client asks the server to re-index all Observation resources for a new search parameter.
POST /fhir/Observation/$reindex HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
Prefer: respond-async
Content-Type: application/fhir+json
{
"resourceType": "Parameters",
"parameter": [
{
"name": "url",
"valueUrl": "http://example.org/fhir/SearchParameter/observation-special-code"
}
]
}
If the server accepts the job, it returns 202 Accepted with a Content-Location header pointing to a status
polling URL.
202 AcceptedContent-Location: An absolute URL for polling the job's status.OperationOutcome or other resource with informational details.$reindex Kick-offHTTP/1.1 202 Accepted
Content-Location: https://fhir.example.com/whatever/path/1ab7162f-status
Content-Type: application/fhir+json
{
"resourceType": "OperationOutcome",
"issue": [{
"severity": "information",
"code": "informational",
"details": { "text": "Re-indexing job accepted and is now in progress." }
}]
}
If the server rejects the request (e.g., due to invalid parameters or lack of permissions), it returns a
standard 4XX or 5XX error.
4XX or 5XXOperationOutcome explaining the error.Clients poll the URL from the Content-Location header using an HTTP GET to check the job's status. Clients
SHOULD implement an exponential backoff strategy.
Servers SHOULD include a Retry-After
header (in seconds or as an HTTP-date) to guide the client's polling frequency. If a client polls too
frequently, the server SHOULD return 429 Too Many Requests.
While the job is still running, the server responds with 202 Accepted.
202 AcceptedRetry-After: (Optional) Suggests when to make the next request.X-Progress: (Optional) A short string describing the current progress (e.g., "55% complete").Parameters or other resource with informational details. Semantics of interim
bodies are implementation-defined; standardized interim/partial result reporting is under discussion as a
future extension.$reindexGET /whatever/path/1ab7162f-status HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
HTTP/1.1 202 Accepted
Retry-After: 60
X-Progress: Indexed 550,000 of 1,200,000 resources.
When the job is finished (whether it succeeded or failed), the server responds with 303 See Other. The
Location header points to the final result.
303 See OtherLocation: The absolute URL of the final result resource or endpoint.Location URL.Note that the status endpoint reports only on the polling machinery, not on the outcome of the job: a job that
failed during execution still completes, so the status endpoint still returns 303 See Other, and the failure
is conveyed by the result endpoint (see Fetch the Result). A status code other than
202 or 303 from the status endpoint indicates a problem with the status endpoint itself (e.g., a transient
infrastructure error), which clients MAY retry.
$reindexGET /whatever/path/1ab7162f-status HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
HTTP/1.1 303 See Other
Location: https://fhir.example.com/whatever/path/1ab7162f-result
Servers SHOULD support cancellation. The client MAY send an HTTP DELETE to the polling URL to request
cancellation of the job. On success, the server SHOULD clean up any associated data. Subsequent polls to this
URL MUST return 404 Not Found.
202 AcceptedOperationOutcome.$reindex JobDELETE /whatever/path/1ab7162f-status HTTP/1.1
Host: fhir.example.com
HTTP/1.1 202 Accepted
Content-Type: application/fhir+json
{
"resourceType": "OperationOutcome",
"issue": [{
"severity": "information",
"code": "informational",
"details": { "text": "Job cancellation requested." }
}]
}
After receiving a 303 See Other from the polling URL with a Location header, the client performs a final
GET against that Location URL to retrieve the outcome of the operation.
The response from this final GET is exactly what the synchronous interaction would have returned. This
includes the status code (e.g., 200 OK for success, 422 Unprocessable Entity for a business rule error),
standard headers (ETag, Last-Modified), and the body (e.g., a Parameters resource for an operation, a
resource for a create, a Bundle for a search).
Servers SHOULD document or communicate how long results remain available after completion (e.g., via an
Expires header on the result response or in server documentation). Operation definitions and implementation
guides MAY impose minimum retention requirements.
$reindex ResultThe client fetches the result and receives the final Parameters resource indicating success.
GET /whatever/path/1ab7162f-result HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
HTTP/1.1 200 OK
Content-Type: application/fhir+json; charset=utf-8
ETag: W/"1ab7162f-final"
Last-Modified: Fri, 01 Mar 2024 14:05:10 GMT
{
"resourceType": "Parameters",
"parameter": [
{
"name": "status",
"valueCode": "completed"
},
{
"name": "durationInSeconds",
"valueDecimal": 742.5
},
{
"name": "resourcesScanned",
"valueUnsignedInt": 1200000
},
{
"name": "indexEntriesCreated",
"valueUnsignedInt": 1457890
},
{
"name": "processingSummary",
"part": [
{
"name": "resourcesIndexedSuccessfully",
"valueUnsignedInt": 1198540
},
{
"name": "resourcesSkippedNoMatchingPath",
"valueUnsignedInt": 1458
},
{
"name": "resourcesSkippedWithError",
"valueUnsignedInt": 2
}
]
},
{
"name": "message",
"valueString": "Re-indexing complete. 2 resources failed validation and were skipped."
}
]
}
$reindex ResultIf the re-indexing job failed internally, the final result is an error. The polling response would still be
303 See Other (because the job has completed), but fetching the result reveals the failure.
GET /whatever/path/1ab7162f-result HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
HTTP/1.1 500 Internal Server Error
Content-Type: application/fhir+json; charset=utf-8
{
"resourceType": "OperationOutcome",
"issue": [{
"severity": "error",
"code": "exception",
"details": { "text": "Job failed due to an unexpected database connection error during indexing." }
}]
}
The pattern is not limited to operations that return Parameters. Because the result fetch preserves the full
response semantics of the corresponding synchronous interaction, the same flow works for any interaction type.
The kick-off and polling steps are identical to the $reindex example above; only the final result differs.
A client submits a create with Prefer: respond-async:
POST /fhir/Observation HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
Prefer: respond-async
Content-Type: application/fhir+json
{
"resourceType": "Observation",
"status": "final",
"code": { "text": "Body weight" },
"valueQuantity": { "value": 72.5, "unit": "kg" }
}
After the 202 → poll → 303 flow, fetching the result returns what the synchronous create would have
returned — including the 201 Created status and the Location and ETag headers:
GET /whatever/path/9c41d3a2-result HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
HTTP/1.1 201 Created
Location: https://fhir.example.com/fhir/Observation/123/_history/1
ETag: W/"1"
Last-Modified: Fri, 01 Mar 2024 14:05:10 GMT
Content-Type: application/fhir+json; charset=utf-8
{
"resourceType": "Observation",
"id": "123",
"meta": { "versionId": "1", "lastUpdated": "2024-03-01T14:05:10Z" },
"status": "final",
"code": { "text": "Body weight" },
"valueQuantity": { "value": 72.5, "unit": "kg" }
}
A search result is the same searchset Bundle the synchronous search would have produced — note that it is
not nested inside a wrapper Bundle:
GET /whatever/path/d52e88b0-result HTTP/1.1
Host: fhir.example.com
Accept: application/fhir+json
HTTP/1.1 200 OK
Content-Type: application/fhir+json; charset=utf-8
{
"resourceType": "Bundle",
"type": "searchset",
"total": 2,
"entry": [
{ "fullUrl": "https://fhir.example.com/fhir/Observation/123", "resource": { "resourceType": "Observation", "id": "123" } },
{ "fullUrl": "https://fhir.example.com/fhir/Observation/456", "resource": { "resourceType": "Observation", "id": "456" } }
]
}
Status and result URLs identify a specific job and its output, which may include sensitive data. Even though these URLs are opaque, servers SHALL apply the same access control to status and result requests as they would to the corresponding synchronous interaction, and SHOULD limit access to the client (or authorization context) that initiated the job.
This pattern replaces the Bundle-based Asynchronous Interaction Request Pattern published in
FHIR R5, in which a completed job returned 200 OK with the result
wrapped in a batch-response Bundle. The redirect-based design avoids two ambiguities in the earlier pattern:
Accept could ambiguously
apply to the status response or to the wrapped final result. Here, each request's headers govern only that
request's response.It also removes the Bundle wrapper, so results that are not naturally Bundles (e.g., a Parameters resource
from $reindex) are returned directly, and a result that is a Bundle is not nested inside another Bundle.
Systems that require the R5 behavior can continue to cite the published R5 pattern. A Prefer extension token
(e.g., async-mode=bundle|redirect) has been proposed to let servers offer both behaviors during migration;
this is under discussion and not yet part of this specification.
4XX/5XX at
kick-off. Otherwise the request is accepted with 202 and runtime failures surface at result time.Prefer: respond-async; if unsupported, the server returns 400.
In FHIR R6, OperationDefinition.synchronicity
(synchronous | asynchronous | either) declares an operation's behavior, and operation definitions can
describe asynchronous support in human-readable documentation._outputFormat should remain the trigger that selects the
Asynchronous Bulk Data Request pattern (FHIR-50598).202 polling responses (e.g., marking
a subset of output parameters as available before completion).Task resource (e.g., returning a task ID at kick-off so running
jobs can be discovered by search) is deferred to future work.next, etc.) in an asynchronously returned searchset
Bundle are followed (e.g., synchronously, as in regular search) needs implementation experience.