Processing Asynchronous Bundles | Medplum

On this page

Feature flag required

Asynchronous batch processing requires the async-batch feature flag on your Medplum Project. For Medplum-hosted environments, contact support@medplum.com to have it enabled.

Medplum can process a FHIR batch Bundle in the background when you ask for the asynchronous request pattern. When the batch request includes the Prefer: respond-async HTTP header, the server will respond immediately with a 202 Accepted status code and a status URL in the Content-Location response header. You should poll that URL until the job finishes, reading the status of the AsyncJob resource tracking the operation on each response. When the job succeeds, you can download the resulting batch-response Bundle payload from a linked Binary resource.

When to use asynchronous batches

Use Prefer: respond-async when:

Avoid async when:

Step-by-step: submit, poll, read results

  1. Build a batch Bundle
    Same rules as a normal batch: resourceType: "Bundle", type: "batch", and one entry per operation. Details and examples live in FHIR Batch Requests.

  2. POST the bundle to the FHIR base URL with Prefer: respond-async
    Issue a POST to your FHIR base URL (for example https://api.medplum.com/fhir/R4) with:

    • Content-Type: application/fhir+json
    • Authorization: Bearer …
    • Prefer: respond-async

    Example:

    curl -X POST 'https://api.medplum.com/fhir/R4' \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/fhir+json" \
      -H "Prefer: respond-async" \
      -d @my-bundle.json
    
  3. Read 202 Accepted and the status URL
    A successful enqueue returns:

    • HTTP 202 Accepted
    • Response header Content-Location set to the status URL
    • A small OperationOutcome body whose first issue’s diagnostics field is the same absolute URL

    Example response body:

    {
      "resourceType": "OperationOutcome",
      "id": "accepted",
      "issue": [
        {
          "severity": "information",
          "code": "informational",
          "details": {
            "text": "Accepted"
          },
          "diagnostics": "https://api.medplum.com/fhir/R4/job/0192a3b4-c5d6-7890-abcd-ef1234567890/status"
        }
      ]
    }
    

    Save that URL (from diagnostics or Content-Location): it is the polling location for the rest of the workflow.

  4. Poll GET …/fhir/R4/job/{id}/status
    Medplum exposes the FHIR asynchronous status interaction at:

    GET [base]/fhir/R4/job/{job-id}/status
    

    Behavior:

    HTTP status AsyncJob.status (typical) Meaning
    202 accepted (or still in progress) Keep polling after a short backoff.
    200 completed Job finished successfully; see below for output.
    200 error Job failed; output usually contains an outcome or error details.

    When the job finishes successfully, the 200 response body is an AsyncJob with status: "completed". Its output is a Parameters resource with a parameter named results whose value is a Reference to a Binary (for example Binary/abc). That Binary stores the full batch-response Bundle as FHIR JSON—the same shape you would have received from a synchronous batch.

    {
      "resourceType": "AsyncJob",
      "id": "0192a3b4-c5d6-7890-abcd-ef1234567890",
      "status": "completed",
      "output": {
        "resourceType": "Parameters",
        "parameter": [
          {
            "name": "results",
            "valueReference": {
              "reference": "Binary/abc"
            }
          }
        ]
      }
    }
    
  5. GET the Binary and parse the response bundle
    GET the results reference from output (for example Binary/abc). Medplum follows the Binary read rules: the Accept header chooses the response shape.

    Accept header Response
    application/fhir+json (or starts with that value) The Binary resource as FHIR JSON, including base64-encoded data. Base64-decode data and parse the JSON as a Bundle with type: "batch-response".
    Omitted, or any value that does not start with application/fhir+json The stored file bytes directly. For async batch results, the stored content is FHIR JSON, so the body is the batch-response Bundle—parse it as JSON with no base64 step.

    Omitting Accept is fine and is often simpler for async batches: you receive the batch-response document directly. Use Accept: application/fhir+json only when you need the full Binary resource (for example to read contentType or other metadata).

    Example:

    # Recommended for async batch results: raw batch-response JSON
    curl 'https://api.medplum.com/fhir/R4/Binary/abc' \
      -H "Authorization: Bearer $ACCESS_TOKEN"
    
    # Optional: Binary resource wrapper (base64 data field)
    curl 'https://api.medplum.com/fhir/R4/Binary/abc' \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Accept: application/fhir+json"
    

    Inspect each entry.response as documented under Response structure.

Rate limits and quotas

Access control

Ensure the caller’s access policies allow:

If either is blocked, polling or downloading results will fail with an HTTP 403/404 error status even though the job ran.

Limitations