Most n8n AI workflows handle sensitive actions one of two ways: they run fully automated and hope nothing goes wrong, or they send a Slack message asking someone to approve and then proceed regardless. Neither is real oversight.
This post shows how to wire genuine human approval into an n8n AI workflow — where the workflow actually pauses, a human sees the exact arguments the action will use, they approve or deny, and the workflow only continues if the approved arguments match what the node will actually execute.
What you need
Install the TrustLoop community node from n8n Settings → Community Nodes, search for n8n-nodes-trustloop. You will also need a free TrustLoop account from trustloop.live — the API key is what connects the node to your governance layer.
The basic pattern
Add a TrustLoop Intercept node before any sensitive action in your workflow. Give it the tool name and the arguments the next node would receive. Something like:
Tool name: send_bulk_email
Arguments: {
"recipients": {{ $json.recipient_list }},
"subject": {{ $json.subject }},
"body": {{ $json.body }}
}
The node returns one of three decisions: ALLOWED, BLOCKED, or ESCALATED.
ALLOWED and BLOCKED are straightforward — branch on those with an IF node and either continue or stop. ESCALATED is where the human approval flow begins.
Handling ESCALATED
When the decision is ESCALATED, TrustLoop has paused the action and sent an approval email to the address configured in your governance rule. The node response includes an approval_id and a payload_hash. Store both of these — you will need them for the retry.
approval_id: {{ $json.approval_id }}
payload_hash: {{ $json.payload_hash }}
At this point your workflow should stop and wait. The simplest pattern is a Wait node set to a reasonable window — 15 minutes, an hour, whatever makes sense for your use case — followed by a second Intercept call that passes the approval_id and approved_hash back along with the original arguments.
Tool name: send_bulk_email
Arguments: (same as before)
approval_id: {{ $('Store Approval').item.json.approval_id }}
approved_hash: {{ $('Store Approval').item.json.payload_hash }}
TrustLoop checks that the approval is genuine, that the hash matches what the human actually saw, and that the arguments being submitted right now are identical to what was approved. If all three pass, the decision comes back ALLOWED and your workflow continues. If the arguments have changed at all since approval, it comes back BLOCKED.
That last check matters more than it might seem. It means the approval is tied to the specific action, not just the category of action. A human approving “send email to 50 recipients” cannot inadvertently authorise “send email to 50,000 recipients” if something upstream changed the list between the approval request and the retry.
What the approver sees
The approval email shows the full tool name and the complete arguments — not a summary, the actual JSON the workflow will execute with. The approver clicks Approve or Deny. If they deny, your retry call comes back BLOCKED and you handle it accordingly.
A note on complex topologies
If your workflow has parallel branches that each call sensitive tools, each branch gets its own approval record. Approvals are independent — approving one branch does not affect another. State lives entirely in TrustLoop’s database, not inside n8n, so branching and looping workflows work the same as linear ones.
We are also building a webhook-based resume so TrustLoop can call back into n8n directly when an approval is decided, rather than requiring a wait-and-poll pattern. Will post an update when that is ready.
Getting started
Node: n8n-nodes-trustloop in Community Nodes
Free account: trustloop.live
Full API reference: trustloop.live/technical
Happy to answer questions on the retry pattern or anything else in the thread.