Skip to content

Sending signals to workflows

A signal advances a workflow that is already running and waiting. It resolves the first non-terminal WAIT task in the target execution, so the caller only needs the workflow ID. A signal never starts a new execution, cannot target an arbitrary task reference, and does not resolve HUMAN tasks.

Workflow signal flow A caller sends an output payload to a signal endpoint. It finds the first blocked Wait, including in a running sub-workflow, then the workflow continues. Callerdecision output Signal APIfind first WAIT Blocked WAITworkflow or runningsub-workflowthen continue

Define a workflow that waits for a signal

This workflow records an approval request, then waits until another system supplies the decision.

{
  "name": "order_approval",
  "description": "Wait for an external order approval signal",
  "version": 1,
  "schemaVersion": 2,
  "inputParameters": ["orderId"],
  "tasks": [
    {
      "name": "wait_for_approval",
      "taskReferenceName": "approval",
      "type": "WAIT"
    }
  ],
  "outputParameters": {
    "orderId": "${workflow.input.orderId}",
    "approval": "${approval.output}"
  }
}

Register it with the workflow metadata API:

curl -X POST '<YOUR-CLUSTER-URL>/api/metadata/workflow' \
  -H 'Content-Type: application/json' \
  -d @order_approval.json

Start and wait for the blocking task

The synchronous execution endpoint starts the workflow and waits for a terminal state or a blocked WAIT task. waitForSeconds defaults to 10; use waitUntilTaskRef when a terminal task reference should also end the wait.

curl -X POST '<YOUR-CLUSTER-URL>/api/workflow/execute/order_approval/1?requestId=approval-demo-42&waitForSeconds=30&returnStrategy=BLOCKING_TASK_INPUT' \
  -H 'Content-Type: application/json' \
  -d '{"input":{"orderId":"order-42"}}'

returnStrategy controls the shape of the response:

Value Returns
TARGET_WORKFLOW The workflow requested by ID. This is the default.
BLOCKING_WORKFLOW The workflow that contains the current blocker; it can be a sub-workflow.
BLOCKING_TASK The current blocking task.
BLOCKING_TASK_INPUT The input of the current blocking task.

Signal the wait asynchronously

Use the asynchronous signal endpoint when the caller only needs to submit the decision. It completes the currently blocked WAIT task and returns immediately.

curl -X POST '<YOUR-CLUSTER-URL>/api/tasks/<workflow-id>/COMPLETED/signal' \
  -H 'Content-Type: application/json' \
  -d '{"approved":true,"approvedBy":"manager@example.com","reason":"Within policy"}'

The signal target is the first non-terminal WAIT task in the workflow, including a currently running sub-workflow. It does not target HUMAN tasks or an arbitrary task reference. A signal does not name a task reference; use this endpoint only when that current blocking-wait behavior is what you want. When exact task targeting is required, use the task-update endpoint (POST /api/tasks/{workflowId}/{taskRefName}/{status}) instead.

Signal and wait for the next workflow state

Use the synchronous variant when the caller needs the resulting workflow state in the same response. It accepts the same returnStrategy values and waits up to timeoutMillis (default: 5000).

curl -X POST '<YOUR-CLUSTER-URL>/api/tasks/<workflow-id>/COMPLETED/signal/sync?returnStrategy=TARGET_WORKFLOW&timeoutMillis=5000' \
  -H 'Content-Type: application/json' \
  -d '{"approved":true,"approvedBy":"manager@example.com"}'

If the workflow reaches another WAIT task, the response represents that next blocking state. If it completes first, the response represents the completed workflow. A synchronous signal returns 404 when there is no blocked task to signal; the asynchronous route returns after submitting the signal and does not provide that state in its response.

Reject or fail the wait

Choose the task status from the URL to record a different decision. For example, signal FAILED when an approval is rejected and you want the workflow's failure path to run:

curl -X POST '<YOUR-CLUSTER-URL>/api/tasks/<workflow-id>/FAILED/signal' \
  -H 'Content-Type: application/json' \
  -d '{"reason":"Order exceeds the approval limit"}'

The payload you send is stored as the WAIT task's output. Downstream tasks can reference it with expressions such as ${approval.output.approved} or ${approval.output.reason}.

Next steps