Approval
The approval step: pause the run and surface a decision to a human — in-app or via one-click email links — then continue down the approved or rejected branch.
Pause the run until a human decides. The workflow resumes down the approved or rejected port with the decision recorded — the human-in-the-loop primitive for anything with consequences.
Config
The decision prompt, and optionally who to email about it.
- Decision prompt — what the approver sees, template-rendered:
Approve refund of {{ payload.amount }} for {{ payload.email }}? - Slack connection + channel — post the prompt to Slack as a message with Approve / Reject buttons. A click decides, and the message rewrites itself to "Approved by @who" so the channel keeps the audit trail and nobody can click twice. Deciders never leave Slack.
- Approvers — comma-separated Slack user ids or account emails. Each approver gets a direct message with the same buttons the moment the run pauses — use it instead of (or alongside) the channel post when the decision belongs to specific people.
- Channel + Approvers — the provider-agnostic form: pick
slack,email, ortext, list the approvers (member ids or emails, addresses, or workspace-verified phone numbers), and each gets the prompt through that channel. Text approvers decide by replying YES or NO to the platform number. - Notify — an email address. The moment the run pauses, it gets the prompt with signed one-click Approve / Reject links (valid 7 days, no sign-in needed).
Every approval can also be decided in-app from the run's page (Runs → the waiting run → Approve / Reject) by any workspace member — so a step with no Slack or email configured is still decidable.
Output
{ "decision": "approved",
"decided_at": "2026-08-26T14:00:00Z",
"metadata": { "via": "email_link" } }
Note the payload is replaced by the decision record — carry forward anything downstream needs with set fields before the approval, or reference earlier steps by id.
Patterns
- Spend gate — if routes orders over $1,000 through approval; the rest skip it.
- Content review — AI drafts a reply, a human approves it, then the send fires.
Gotchas
- Wire both ports. An unwired rejected branch means a rejection just ends the run silently — usually you want at least a notification.
- The run stays paused until someone decides — write a prompt with enough context to decide from Slack or an inbox without opening MadMax.