Skip to content

Task API

TORCH exposes job management as a FHIR Task-based control API under /fhir/Task. This is separate from the Asynchronous Bulk Data Request Pattern used to kick off and poll an extraction — the Task API is for listing jobs, inspecting a single job, changing its priority, or pausing / resuming / cancelling / deleting it. It is TORCH-specific and not as formalized as the bulk data API: there is no external specification it implements, only a mapping from TORCH's internal job model onto the FHIR Task resource.

Job Status ↔ Task Mapping

Every job has an internal JobStatus, surfaced on the Task resource both as the standard FHIR Task.status and, with the original TORCH status preserved verbatim, as Task.businessStatus (system https://medizininformatik-initiative.de/torch/job-status). Use businessStatus if you need to distinguish between the three different in-progress sub-states.

JobStatusTask.statusbusinessStatus codeMeaning
PENDINGrequestedPENDINGQueued, not yet claimed by a worker
RUNNING_GET_COHORTin-progressRUNNING_GET_COHORTFetching the cohort (CQL/Flare query running)
RUNNING_PROCESS_BATCHin-progressRUNNING_PROCESS_BATCHExtracting patient batches
RUNNING_PROCESS_COREin-progressRUNNING_PROCESS_COREExtracting core (non-patient) data
PAUSEDon-holdPAUSEDPaused via $pause
TEMP_FAILEDon-holdTEMP_FAILEDRecoverable failure; will be retried
COMPLETEDcompletedCOMPLETEDFinished successfully
FAILEDfailedFAILEDTerminal failure
CANCELLEDcancelledCANCELLEDCancelled via $cancel

A job's status is only final (no further transitions) for COMPLETED, FAILED, and CANCELLED.

Priority

Task.priority maps onto an internal two-level priority used for worker scheduling:

FHIR Task.priorityInternal JobPriority
asap, stat, urgentHIGH
routine (or absent)NORMAL

HIGH-priority jobs are picked up by workers ahead of NORMAL ones (see PUT /fhir/Task/{id} below to change it after creation).


Searching Tasks

GET /fhir/Task returns a FHIR Bundle (searchset) of matching tasks. Both parameters are optional and may be combined; each accepts a comma-separated list:

  • _id — job UUIDs to include
  • status — one or more of the JobStatus values from the table above

By default, unsupported or invalid parameter values are silently ignored. Send Prefer: handling=strict to instead get a 400 with an OperationOutcome listing exactly what was wrong:

sh
curl -s 'http://localhost:8080/fhir/Task?status=RUNNING_PROCESS_BATCH,RUNNING_PROCESS_CORE' \
  -H 'Prefer: handling=strict'

Reading a Single Task

sh
curl -s 'http://localhost:8080/fhir/Task/<jobId>'

Returns 404 with an OperationOutcome if the job doesn't exist.

Updating Priority

PUT /fhir/Task/{id} currently only supports changing priority — the request body must be a full Task resource, but only Task.priority is read.

This endpoint uses optimistic locking: the If-Match header must carry the current version as a weak ETag (W/"<version>", taken from Task.meta.versionId). Without it, the request fails with 428 Precondition Required; with a stale version, it fails with 409 Conflict (the underlying job version may have moved on, e.g. because a batch finished processing in the meantime).

sh
curl -s -X PUT 'http://localhost:8080/fhir/Task/<jobId>' \
  -H 'Content-Type: application/fhir+json' \
  -H 'If-Match: W/"3"' \
  -d '{"resourceType":"Task","id":"<jobId>","status":"in-progress","intent":"order","priority":"asap"}'

Lifecycle Operations

Three operations transition a job between states. All three return the updated Task, or 409 Conflict if the job isn't in a state the operation allows:

OperationEndpointValid fromResulting status
$pausePOST /fhir/Task/{id}/$pauseAny RUNNING_* statePAUSED
$resumePOST /fhir/Task/{id}/$resumePAUSEDThe state it was paused from
$cancelPOST /fhir/Task/{id}/$cancelAny non-terminal state (i.e. not already COMPLETED/FAILED/CANCELLED)CANCELLED
sh
curl -s -X POST 'http://localhost:8080/fhir/Task/<jobId>/$pause'
curl -s -X POST 'http://localhost:8080/fhir/Task/<jobId>/$resume'
curl -s -X POST 'http://localhost:8080/fhir/Task/<jobId>/$cancel'

Deleting a Task

DELETE /fhir/Task/{id} removes the job and its associated result data (NDJSON output, diagnostics report) from disk. It returns 204 No Content on success, 404 if the job doesn't exist, and 500 if the result files couldn't be removed.

sh
curl -s -X DELETE 'http://localhost:8080/fhir/Task/<jobId>' -o /dev/null -w '%{http_code}\n'

Example Walkthrough

sh
# 1. Find all currently running jobs
curl -s 'http://localhost:8080/fhir/Task?status=RUNNING_GET_COHORT,RUNNING_PROCESS_BATCH,RUNNING_PROCESS_CORE'

# 2. Inspect one of them
curl -s 'http://localhost:8080/fhir/Task/<jobId>'

# 3. Bump its priority (note the version from step 2's Task.meta.versionId)
curl -s -X PUT 'http://localhost:8080/fhir/Task/<jobId>' \
  -H 'Content-Type: application/fhir+json' \
  -H 'If-Match: W/"3"' \
  -d '{"resourceType":"Task","id":"<jobId>","status":"in-progress","intent":"order","priority":"asap"}'

# 4. Pause it, then resume it later
curl -s -X POST 'http://localhost:8080/fhir/Task/<jobId>/$pause'
curl -s -X POST 'http://localhost:8080/fhir/Task/<jobId>/$resume'

# 5. Or cancel it outright, and clean up its result data once you're done with it
curl -s -X POST 'http://localhost:8080/fhir/Task/<jobId>/$cancel'
curl -s -X DELETE 'http://localhost:8080/fhir/Task/<jobId>'