Detected country: US
logo
Sign InGet Early Access
GuideRecipesDeveloper
‌
‌
‌
logo

Powered by

  • Home
  • Developer Docs
  • Developer foundations
  • How do I wake a suspended run with reply or resume?

How do I wake a suspended run with reply or resume?

2min read

Share

TL;DR: When a run is suspended, continue the same runId with POST /api/runs/:id/reply (HITL / waitForInput) or POST /api/runs/:id/resume (operator pause). Copy requestId from state.resume or the SSE input_request event — never invent it. Slack and email are notifiers that call the same /reply contract.

Who this is for

Developers building Studio clients, Slack/email notifiers, or backend workers that continue parked runs.

How do I discover a parked run?

  1. Keep the runId from execute / webhook.
  2. Poll GET /api/runs/:runId or listen on the run SSE stream until status === "suspended".
  3. Read state.resume (or the SSE input_request event):

Studio History shows the same fields and an API curl snippet. See also How do I stream playbook run events?.

How do I wake HITL (waitForInput)?

Prefer reply when reason is input_request:

POST /api/runs/:runId/reply
Authorization: Bearer <token>
Content-Type: application/json

{
  "requestId": "<from state.resume.requestId>",
  "reply": { "id": "approve", "label": "Approve" }
}

Effect: wakes the same run and injects reply so waitForInput returns that value in the re-entered step. Later steps do not see the reply.

Operator pause must not use /reply — you get 409 reply_not_accepted. Use /resume instead.

# Discover
curl -s -H "Authorization: Bearer $BALLET_API_TOKEN" \
  "$API/runs/$RUN_ID" | jq '.status, .state.resume'

# Continue (HITL)
curl -s --request POST \
  --url "$API/runs/$RUN_ID/reply" \
  --header "Authorization: Bearer $BALLET_API_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"requestId":"<state.resume.requestId>","reply":{"id":"approve","label":"Approve"}}'

How do I wake an operator pause?

POST /api/runs/:runId/resume
Authorization: Bearer <token>
Content-Type: application/json

{}

Optional body: { "fromStepId": "...", "input": {} }.

For operator pause, no reply is injected (so a later waitForInput is not auto-answered). /resume also continues failed/cancelled runs from a step.

Common error codes

Treat request_mismatch / wake_conflict as already-answered for idempotent UX.

How do Slack / email notifiers work?

Notifiers are delivery UIs for the same park:

  1. Detect park (input_request SSE, webhook, or poll until suspended).
  2. Read runId, requestId, prompt, details, schema.options.
  3. Post Approve/Reject (embed server-issued ids in the button or link).
  4. On click → POST /api/runs/:runId/reply with { requestId, reply }.

Do not invent a custom pause API inside step code, and do not busy-wait on Slack from the sandbox.

How does this compare to Temporal and UiPath?

Same idea: stable run identity + server-issued wait token + payload.

Ballet's requestId is the UiPath taskId / Temporal signal-correlation equivalent.

Related articles

  • How do I pause and resume a playbook run?
  • How do I stream playbook run events?
  • How do I run a playbook over the REST API?
  • How do I authenticate with the Ballet API?
  • Ballet vs Temporal
  • Ballet vs UiPath

Share