Human-in-the-Loop
Some workflows shouldn't proceed without a person's sign-off — sending a high-value email, deploying code, refunding a customer. OrcFlows supports this natively with approval steps that pause a running workflow indefinitely (durably, via Temporal signals) until a human approves or rejects.
Approval node types
type | Behavior |
|---|---|
human-approval | Generic approval gate — you choose the notification channel via notify_type. |
approval-slack | Convenience alias — pre-fills notify_type: "slack". |
approval-telegram | Convenience alias — pre-fills notify_type: "telegram". |
Example:
{
"id": "review_email",
"type": "human-approval",
"depends_on": ["draft_email"],
"config": {
"message": "Send this email to {{steps.draft_email.output.to}}?\n\n{{steps.draft_email.output.body}}",
"notify_type": "slack",
"notify_to": "#approvals",
"notify_token": "{{ secret.SLACK_TOKEN }}"
}
}
message, notify_to, and notify_token are resolved as expressions against the full execution context, so the approval message can embed any prior step's output.
How it works under the hood
- When the workflow execution reaches an approval step, it marks the step as waiting in the database (
MarkStepWaitingActivity) and sends a notification (Slack message, Telegram message, etc.) containing the renderedmessageand one-click Approve/Reject links. - The Temporal workflow then blocks indefinitely on a signal channel named
approve-<step_id>—workflow.GetSignalChannel(ctx, signalName).Receive(...). This wait is fully durable: it survives worker restarts and deploys. - A human responds via:
- One-click links —
GET /api/v1/executions/{id}/approve/{stepId}?decision=approve|reject(public, token-less; the execution ID + step ID combination provides sufficient entropy for buttons embedded in Slack/Telegram messages). - The dashboard — the Pending Approvals view (
GET /api/v1/approvals) lists every execution currently waiting, across all workflows. - The API —
POST /api/v1/executions/{id}/signalwith{"signal": "approve-<step_id>", "data": {"approved": true, "comment": "..."}}.
- One-click links —
- Once the signal is received,
MarkApprovalCompletedActivityrecords the decision and the workflow continues — or, ifapproved: false, the step's output contains the rejection so downstream steps (orcontinue_on: "error") can branch accordingly.
If the execution is cancelled while waiting, the approval step is marked as cancelled (comment: "cancelled") and the workflow stops cleanly.
Retrying past an approval
If a workflow execution is retried (POST /api/v1/executions/{id}/retry) after an already-approved step, the retry does not re-prompt — the prior approval decision is replayed from PreloadedOutputs, so the workflow proceeds straight through.
Pending approvals view
GET /api/v1/approvals returns every step currently in the waiting state across the tenant — surfaced in the dashboard as a single inbox so reviewers don't need to dig through individual executions.
Next
- Executions & Monitoring — signals and execution detail.
- AI Agents & Tools — combine approval gates with autonomous agents for "agent proposes, human approves" patterns.