Home › Guides › Human approval step in n8n
Add a human approval step to an n8n workflow before it sends
Matching pack: Approval Gate, n8n pack (US$29), three importable workflows for self-hosted n8n that hold AI-written drafts until an allowlisted person approves them in Slack or by email. Read its test report (81 end-to-end checks on SQLite and 81 on PostgreSQL, all passing: the 3 workflows were imported into a real local n8n 2.43.7 and driven over HTTP; Slack and email were mocked).
Your workflow (or an AI agent inside it) writes an email or a webhook call, and you want a person to say yes before it goes out. n8n has three built-in ways to pause for that. Each one works, but each leaves a few gaps you have to close yourself.
The short answer
- Any channel: put a Wait node set to Resume: On Webhook Call before the send step, and send the approver a link or button that calls
$execution.resumeUrl. The URL is generated at runtime and is unique to each execution. - Slack: use the Slack node's Message > Send and Wait for Response operation with Response Type set to Approval. Turn on Capture Who Responded and Restrict Who Can Approve if only certain people may decide.
- AI Agent tools: add a human review step in the AI Agent's Tools panel and connect the risky tools (send email, update records) to it. The agent pauses until someone approves or denies that tool call.
- Whichever you use, set a time limit so a request nobody answers doesn't sit waiting forever.
Sources: n8n docs, "Wait" node (docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait); n8n docs, Slack node "Approvals" (docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/approvals); n8n docs, "Human-in-the-loop for tools" (docs.n8n.io/advanced-ai/human-in-the-loop-tools). Checked 27 Oct 2026.
The Wait node: settings that matter
When the Wait node pauses, n8n saves the execution to the database and reloads it when the resume URL is called. These settings decide who and what can resume it:
- HTTP Method: choose
POST. Email scanners and chat link previews open links withGET, and you don't want a preview to count as a decision. That means the email link should open a page with a button that POSTs, not hit the resume URL directly. - Ignore Bots (under the node's options): turn it on to ignore requests from link previewers and crawlers. Treat it as a second layer, not the only one.
- Authentication: the resume URL accepts Basic, Header or JWT auth. With None, anyone who gets hold of the URL can resume the workflow. IP(s) Whitelist can narrow it further when calls come from a known server.
- Limit Wait Time: turn it on so the execution resumes by itself after a set time. Then check on the resumed branch whether a real decision arrived, and send nothing if not.
- Same execution: n8n's docs warn that a partial execution changes the resume URL, so the node that sends the link has to run in the same execution as the Wait node. Test with a full run, not by executing nodes one at a time.
Slack's built-in approvals
With Send and Wait for Response, the default buttons are signed links: n8n's docs say no one can tamper with the URL or its action, but anyone who has the link can respond and n8n can't tell who clicked. Turning on Capture Who Responded switches to Slack interactive buttons. n8n then checks each callback with your Slack app's signing secret and checks the person against your approver list. That mode needs your n8n reachable from Slack over public HTTPS, Interactivity turned on in the Slack app with the Request URL n8n's docs give, and the signing secret in the credential's Signature Secret field. Without the secret, the buttons show up but clicks don't resume the workflow.
Gaps the built-in nodes leave
A pause is not yet a safe approval. These are the cases we check for in our own pack:
- The draft changes after approval. If the text lives in a table or comes from an earlier node that can be re-run, store a hash of exactly what the approver saw and compare it again just before sending.
- Two clicks, two sends. A double click, or two approvers clicking at once, can trigger the send twice if your own decision handling isn't guarded. Record the send as done before you send, and refuse a second decision.
- No record of who decided. Write who decided, when, through which channel, and the hash of what they decided on to a log you can check later.
- Nobody answers. Decide what happens on expiry (usually: mark it expired and send nothing), and send a reminder before then if the request matters.
The shape of a safe approval flow
Trigger (your agent or app POSTs a draft)
-> store the draft and its sha256 hash, state = Pending
-> ask the approver (Slack message or email with a review link)
-> Wait node: On Webhook Call, HTTP Method POST, Limit Wait Time on
resumed by a decision -> check approver is allowed
-> re-hash the stored draft, refuse if it changed
-> mark Sent BEFORE sending, so a second click does nothing
-> send
time limit reached -> mark Expired, send nothing
Waits of less than 65 seconds stay in memory instead of being saved to the database, and time limits use the n8n server's clock, not the workflow's timezone setting, so allow for that when you set an expiry.
Source: n8n docs, "Wait" node (docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait). Checked 27 Oct 2026.
Get the whole gate, already built
Approval Gate, n8n pack (US$29). The gate stores it with its sha256 hash, asks your approvers in Slack (Approve/Reject buttons) and by email (a review link), and waits. Nothing is sent until an allowlisted approver says yes, the hash is checked again just before sending, and a draft nobody decides on expires unsent. Self-hosted n8n only. Its tests imported the three workflows into a real local n8n 2.43.7 on SQLite and PostgreSQL; Slack and email were mocked, and real Slack and real email providers were not used.