Human approval in n8n: pausing a workflow with the Wait node and Slack buttons
Published 18 July 2026 · 5 min read
Some workflows should never run without a human giving the green light: sending a sensitive email to a client, approving an expense, publishing AI-generated content, or triggering an irreversible action on a production system. Fully automating these is reckless — but going back to all-manual defeats the point of n8n. The right answer is human-in-the-loop: the workflow runs normally, stops at the sensitive step, waits for a human decision, then resumes exactly where it left off. n8n has everything needed for this natively, with no third-party tool.
The Wait node: four ways to resume a workflow
The Wait node (n8n-nodes-base.wait) pauses an execution and persists it to the database — it consumes no resources while waiting. It offers four resume modes:
- After a time interval: resume after a fixed number of seconds, minutes, or hours.
- At a specified time: resume at a given date/time, useful for aligning an action with business hours.
- On form submitted: resume when someone fills in an auto-generated n8n form.
- On webhook call: resume when an HTTP request hits a unique URL generated for that specific execution.
It's this last mode that makes Slack-button approval possible. When the Wait node is set to On Webhook Call, n8n exposes the resume URL through the expression {{ $execution.resumeUrl }} — a unique, unguessable, single-use link: the first request that calls it resumes the workflow with its request body as input data; any subsequent request to the same URL fails.
Building the Slack approval circuit
The most robust design separates the notification from the resume, so you keep control over what happens in between:
- A Slack node (Send Message) right before the Wait node: it posts a message with Slack
blockscontaining two interactive buttons ("Approve" / "Reject"), each with a distinctaction_id. The message includes the context needed to decide (amount, recipient, an excerpt of the AI-generated content). - A Wait node set to
On Webhook Call, right after. This generates theresumeUrlfor this specific execution. - A dedicated Slack interactions webhook (a plain Webhook node, or Slack Trigger if your plan supports it) receives the
block_actionspayload Slack sends when someone clicks a button. Slack requires an HTTP 200 response within 3 seconds: reply immediately with an empty body, then process the rest asynchronously in the same workflow or a sub-workflow. - This interactions webhook then calls the paused execution's
resumeUrl(HTTP Request node, POST method), passing the decision (approveorreject) in the request body. This call is what wakes the original workflow. - The original workflow resumes after the Wait node with the decision available in
$json, and branches on an IF or Switch node to execute the action if approved, or notify a rejection otherwise.
Splitting this into two workflows — the business process that waits, and a small "Slack interactions router" workflow that handles every button click — avoids duplicating Slack signature verification logic every time you add a new approval gate.
A simpler variant, good enough for low-stakes internal use: put $execution.resumeUrl directly (with a ?decision=approve or ?decision=reject query parameter appended) as the link on two url-type buttons in the Slack message. Clicking opens a link that calls the resume webhook directly — no second workflow needed, but you lose control over validating and logging the interaction.
Never skip the timeout
A Wait node with no time limit waits up to 366 days (n8n's technical maximum) — plenty for a forgotten approval to block an execution indefinitely. Always configure the node's "Limit Wait Time" option with a sensible duration (a few hours for an expense, 24-48 hours for content approval), and wire the "limit reached" output to a fallback path: escalate to a second approver, auto-reject with a notification, or a simple Slack reminder.
Without this safeguard, a growing team always ends up with dozens of paused, invisible executions that never finish — a classic problem also covered in our general guide to n8n failure handling, Retry, Continue on Error and Error Workflow.
Securing the circuit
Three concrete points of attention:
- Verify the Slack signature on the interactions webhook (the
X-Slack-Signatureheader and the Slack app's signing secret) — without it, anyone who knows the webhook URL could forge fake approvals. - Never log the
resumeUrlin an external monitoring tool or an unrestricted Slack channel: it's single-use, but until it's been consumed, whoever holds it can resume the execution in your place. - Restrict the Slack channel where approval requests are posted to people actually authorized to approve — an exposed webhook is only as safe as the access upstream of it, as covered in our guide to securing a publicly exposed n8n webhook.
A natural fit: compliance
Human-in-the-loop is especially useful whenever you need to prove, after the fact, that a sensitive action was validated by an identified person before it happened — exactly the requirement behind a GDPR audit trail. Combined with an append-only Supabase logging table like the one described in our GDPR audit trail with n8n and Supabase article, the Wait + Slack pattern becomes a real governance mechanism: each approval decision (who, when, what) is logged automatically the moment the workflow resumes. That's exactly what our Compliance & Audit Pack covers, with ready-to-import approval and logging workflows.
Going further: sub-workflows and resume data
If your approval circuit lives in a sub-workflow called via Execute Workflow from a parent process, make sure you're on a recent n8n version: older releases had a bug where data returned by a sub-workflow after a Wait resume (webhook, form, or approval) wasn't correctly passed back to the calling workflow. This has been fixed, but if you're running an older self-hosted instance — see our self-hosted vs. cloud comparison for the maintenance implications — test this exact scenario before rolling it out to critical nested processes.
Wrapping up
The Wait node turns n8n into a genuine human-machine orchestration engine: no third-party tool, no polling, no cron job checking every five minutes whether someone replied. A resume webhook, a well-configured timeout, and signature verification are enough to build a reliable approval gate for your most sensitive actions. If the stakes are compliance and proof of human validation, our Compliance & Audit Pack and our Complete FlowKit Bundle ship the approval, logging and audit building blocks already assembled and ready to import.
FAQ
Frequently asked questions
Is the resumeUrl safe if it leaks?
The link is long, random and single-use: once called, it's invalidated, even against a second legitimate request. The real risk isn't guessing (the URL isn't guessable) but leakage — a screenshot of a public Slack channel, an application log. Never post it in an unrestricted channel and never log it in your monitoring tools.
What happens if nobody responds to the Slack message?
With no limit configured, the Wait node waits indefinitely (up to n8n's technical maximum of 366 days) and the execution stays open in your database. Always enable the Wait node's time-limit option and wire a fallback output: auto-reject, escalate to a second approver, or an automatic reminder.
Can I do the same thing with Microsoft Teams or Telegram?
Yes, the mechanism doesn't depend on the messaging tool: the Wait node and its resumeUrl carry the pause logic. Swap the Slack node for a Microsoft Teams or Telegram node to send the message, and adapt the interactive button format to that platform's API.
Does the Wait node consume resources while waiting?
No. The execution is persisted to the database and frees the n8n worker's memory; it only consumes resources again at resume time. That's what makes waiting for hours or days perfectly viable, even on a small VPS.
Does this work inside a sub-workflow called via Execute Workflow?
Yes, on recent n8n versions, which fixed how data returned by a sub-workflow after a Wait resume (webhook, form, or HITL) gets passed back to the calling workflow. On an older version, test this exact scenario first before rolling it out to nested sub-workflows.
Bundle FlowKit Complet
€269