ModulesLabQuizAll courses

Controls I: Human-in-the-Loop Design

Decide which actions an automation may take on its own, which need a person, and build the approval gate and audit trail that make that decision stick.

Module 10 of 14 · Day 5 · Session 10 · AI Administrator: Agentic Workflows & Automation

Module 10 ~50 min read + lab No code

What you will learn

Prerequisites: Module 9: Multi-agent workflows, and the triage flow you built in Module 6.

1. Why controls come after capability

Modules 5 to 9 taught you to make things happen automatically. This module teaches you to decide, on purpose, what should not happen without a person. That order is deliberate. Until you have watched an automation act on its own, the need for a gate feels theoretical. After the first wrong email goes to a customer, it feels obvious.

"Human-in-the-loop" (HITL) is the general name for any design where a person can see, approve, change or stop what an automated system does. It is not a single feature. It is a set of decisions about where the person sits, what they see and how much they are asked to do. Get those decisions right and the automation is trusted and used. Get them wrong and one of two things happens: the gate is ignored because it fires too often, or the gate is missing where it mattered.

The administrator's question

For every action in a workflow, ask: If this ran wrongly a hundred times before anyone noticed, what would it cost? The answer decides the tier, and the tier decides the control.

2. Risk tiering: five kinds of action

Every tool an agent can call falls into one of five tiers. The tier is about the consequence of a wrong call, not about how clever the step is.

Five horizontal bands from Tier 1 read-only at the bottom to Tier 5 money, people, legal at the top, each with examples and the control it needs
The five tiers, set by the cost of a wrong call. Tier the tool, not the workflow; lower the tier when you can (delete becomes archive, send becomes draft).
TierWhat it doesExamplesControl needed
1. Read-onlyLooks something up, changes nothingSearch a sheet, read a calendar, fetch a ticketNone beyond logging. Let it run.
2. Reversible writeChanges internal data that can be undoneAdd a row, set a label, create a draft, move a cardReview after. Spot-check a sample weekly.
3. Irreversible internalCannot be undone, but stays inside the organisationDelete a record, overwrite a file, close a ticketApprove before, or convert to reversible (archive instead of delete).
4. External / customer-facingLeaves the organisationSend an email, post to a customer portal, reply on social mediaApprove before, or edit-then-approve. Always.
5. Money, people, legalCommits funds, affects a person's status, or creates legal exposureIssue a refund, approve leave, sign a contract, change payThe system prepares; a named person decides. No auto-run, ever.

Two habits make tiering easy. First, tier the tool, not the workflow: an "email" tool is Tier 4 wherever it appears. Second, look for a cheaper tier that gives the same result. "Delete" becomes "archive" (Tier 3 to Tier 2). "Send email" becomes "create draft in the sender's mailbox" (Tier 4 to Tier 2), with the human pressing send. Most approval gates can be avoided by choosing a lower-tier tool.

The tier is set by the worst case

An agent that "usually" only reads but "occasionally" sends is a Tier 4 agent. Controls follow the highest tier the agent can reach, not the average.

3. The four human-in-the-loop patterns

Once you know the tier, pick the pattern. There are only four, and each answers a different question: before or after, and every time or only when unsure.

A two-by-two grid: approve before and edit-then-approve on the top row (every time), escalate on low confidence and review after on the bottom row (only some runs)
The four patterns: before or after the action, every time or only when unsure.

Approve before action

The workflow pauses and shows the person exactly what it is about to do. Nothing happens until they say yes. Use for Tier 3 and 4 actions. Cost: one click per run, so volume must be manageable.

Review after

The action runs, and the person sees a digest (daily or weekly) of what happened, with an undo path. Use for Tier 2 writes at high volume. Cost: near zero per run, but mistakes live for a while before they are caught.

Edit-then-approve

The system drafts, the person adjusts and sends. The best pattern for customer-facing text: the person stays the author, the machine removes the blank page. Cost: a minute per item, so it suits tens per day, not thousands.

Escalate on low confidence

The workflow runs alone when the model is sure, and routes to a person when it is not. Use when most cases are easy and a few are hard. Requires the AI step to report a confidence or a category such as "unsure".

Real workflows combine them. The Module 6 triage flow, for example, can run alone for labelling (Tier 2), escalate anything the model marks "unsure", and use edit-then-approve for every outgoing reply (Tier 4).

4. Interrupt and resume: how a gate actually works

Under every approval gate is the same mechanism, whatever tool you use. The workflow reaches a checkpoint, saves its state (everything it knows so far), stops, and waits for a signal. When a person sends the signal, the workflow resumes from exactly where it stopped, with the person's decision added to what it knows.

Draft reply, checkpoint, pause and send the request, a person approves or rejects or does not answer, resume with the decision, then send and log or label and log; the three gate rules below
Interrupt and resume, and the three rules of a gate that works: show the actual action, time out safely, carry the decision forward.

In developer frameworks this is explicit. LangGraph, for example, lets a designer mark a step as "interrupt before": the graph pauses just before that step, a checkpoint is stored, and a human call is needed to continue. The same idea appears in every no-code tool under a friendlier name:

ToolHow the pause is builtHow the human resumes it
n8nWait node set to "resume on webhook call" or "on form submission"Clicks an approve or reject link in Slack or email; the link calls the resume address
n8n (simpler)Gmail or Slack node with "Send and wait for approval"Presses Approve or Decline in the message
Power AutomateApprovals action ("Start and wait for an approval")Responds in Teams, Outlook or the Approvals app
Make / ZapierSplit into two scenarios: one ends by sending the request, a second starts from the approval webhookClicks a link that fires the second scenario

Three design details decide whether a gate is any good:

An e-mail titled Approve this reply? Re: [S10 test] Refund for unused licences, with the recipient, subject, category, the full reply between reply and end markers, the time limit, and two buttons: Reject and Approve and send
Rule 1 in practice: the real approval e-mail the n8n gate sent on 23 October 2026 (links disabled in the picture). Everything needed to decide is in the message.

Deep dive

If you want to see the mechanism in code, the LangGraph tutorial on this site walks through checkpointing and interrupt-before step by step. You do not need it for the labs; it is there for the curious.

5. Limits and audit trails

Approval gates control which actions run. Two more controls limit how much can go wrong between gates.

Two panels: warning signs of approval fatigue on the left (approvals under five seconds, above 98% approved, one person approves everything) and fixes on the right (lower the tier, batch, escalate only the uncertain, rotate, review monthly)
Approval fatigue: a gate that is rubber-stamped is worse than none. The Approval log is where you measure it.

Iteration and budget limits

An agent (Module 8) loops until it decides it is done. Give it a ceiling: at most 10 tool calls per task, at most a fixed model spend per run, at most 50 runs per hour for the whole workflow. In n8n's AI Agent node this is the "max iterations" option; in most builders it is a similar setting. When the ceiling is hit the agent must stop and hand the task to a person with what it has so far, not silently give up.

Audit trails

An audit trail answers, months later, "what did the system do, and who said it could?" At minimum log one row per gated action:

timestamp | workflow | run id | action proposed | tier | approver | decision (approved / rejected / edited / timed out) | final content sent | notes

A Google Sheet or Airtable base is enough to start. The important properties are that the log is append-only (nobody edits history), complete (every gated action, including rejections and timeouts), and readable by someone who was not there.

Approval fatigue

A gate that fires 200 times a day gets rubber-stamped by lunchtime, and a rubber-stamped gate is worse than none, because it creates false confidence. Warning signs: median approval time under five seconds, approval rate above 98%, the same person approving everything. Fixes: lower the tier of the tool (draft instead of send), batch approvals into a digest, escalate only low-confidence cases, rotate approvers, and review the numbers monthly.

6. Demo: one gate, three e-mails

Everything below was built and tested on n8n Cloud (2.43.6) on 23 October 2026: the Module 6 triage flow with a drafted reply behind an approval gate, Gemini 2.5 Flash on the trial's Gateway credits, temperature 0. The three customers in the test e-mails are fictional; Gmail addresses are blurred.

n8n canvas: New email, Classify email, Clean label, Draft reply, Keep reply, Ask for approval, Decision, Approved?; the true branch Send reply and Log sent, the false branch Label needs human and Log not sent
The finished workflow. Everything before Ask for approval is Tier 1: nothing has left the run. Only Send reply is Tier 4, and it is behind the gate.
The Gmail node Ask for approval: operation Send and Wait for Response, To the approver, Subject Approve this reply?, the message with To, Subject and the reply, Response Type Approval
The gate: Gmail, Send and Wait for Response, Response Type Approval, Approve and Disapprove, a time limit under Options. Free Text and Custom Form are the building blocks of edit-then-approve.
The Edit Fields node Decision with the expression: approved if data.approved is true, rejected if false, timed out otherwise
Decision names the three outcomes and copies what the send and the log need from Keep reply: the gate's output replaces the item.
Test e-mailClassifiedDecisionWhere it ended
Khaled: cannot log in after a password resettechnicalapprovedReply sent; Log sent
Lina: refund for unused licences, "today"billingrejectedLabel needs-human; Log not sent
Nadia: can we book a demo next week?salestimed out (3-minute limit)Label needs-human; Log not sent; approver "nobody (timed out)"

Why reject Lina's reply? It reads well, but anything that sounds like a refund is Tier 5: money. A person should answer that one personally.

Video, recorded live: the three test e-mails through the gate. The approval e-mail is the real message, its buttons clicked for real; the 3-minute wait is cut (narrated, with captions).
The Approval log read back in n8n: rows with timestamp, workflow, run_id, action_proposed and the decision
The Approval log as the workflow wrote it: one row per gated action, including the rejection and the timeout.

Practical lab

You will add an approval gate to the e-mail triage workflow from Module 6 so that no reply leaves without a person seeing it, and every decision, including rejections and timeouts, is logged. Every setting below was verified on n8n Cloud 2.43.6 on 23 October 2026; the three trap boxes are what went wrong on the way.

Files for this lab

0

Before you build

Upload the Approval log template and Save as Google Sheets; create the Gmail label needs-human; duplicate your M6 Inbox triage as M10 Triage with approval gate; import the test sender and paste your address into Send to myself.

Trap 1: an empty sheet writes the wrong header

Our first Approval log was a new, empty sheet. The Append node ignored the nine mapped columns and wrote the incoming item's own fields as the header: id, threadId, labelIds. On a sheet that already has its header row the mapping works. Start from the template.

1

Tier every node

Fill the first tab of the tier worksheet: the trigger, Classify and Draft reply are Tier 1 (nothing leaves the run), the label and the log are Tier 2, Send reply is Tier 4. Only one node needs a gate.

2

The trigger: test e-mails only, never the workflow's own mail

New email (Gmail Trigger), Simplify off, Filters, Search:

subject:"[S10 test]" -subject:"Re:" -subject:"Approve this reply"

Trap 2: the trigger hears the workflow's own mail

Without the two exclusions the trigger also matches the approval request ("Approve this reply? Re: [S10 test] ...") and the reply the workflow sends ("Re: [S10 test] ..."). Our second test run picked up the sent reply. Published, the flow would draft replies to its own replies.

3

Draft reply

From Clean label, a Basic LLM Chain named Draft reply, prompt in Expression mode, the same Gemini model:

You draft replies to customer emails for the support team of a small software company. Write a short, polite reply (under 120 words) to the email below. Category: {{ $json.label }}. Do not promise refunds, discounts, dates or fixes; say what happens next and that a colleague will follow up if needed. Sign off as "Customer Support". Reply with the email body only: no subject line, no commentary. Email subject: {{ $('New email').item.json.subject }} Email body: {{ $('New email').item.json.text }}
4

Keep reply (the hand-off)

An Edit Fields node, Include Other Input Fields off: message_id = {{ $('New email').item.json.id }}, to = {{ $('New email').item.json.from.text }}, subject = Re: {{ $('New email').item.json.subject }}, label = {{ $('Clean label').item.json.label }}, reply = {{ $json.text.trim() }}, approver = your Gmail address.

5

Ask for approval

Gmail, operation Send and Wait for Response: To {{ $json.approver }}, Subject Approve this reply? {{ $json.subject }}, Response Type Approval, Approval Options Approve and Disapprove (labels Approve and send, Reject), Options, Limit Wait Time 3 minutes for the lab (24 hours in real use). Message, in Expression mode:

The triage workflow wants to send this reply. Nothing is sent until you approve. To: {{ $json.to }} Subject: {{ $json.subject }} Category: {{ $json.label }} --- reply --- {{ $json.reply }} --- end --- No answer within 3 minutes: nothing is sent and the e-mail is labelled needs-human.
6

Decision and Approved?

An Edit Fields node Decision: decision =

{{ $json.data?.approved === true ? 'approved' : ($json.data?.approved === false ? 'rejected' : 'timed out') }}

plus message_id, to, subject, reply from $('Keep reply'), and approver = {{ $json.data?.approved === undefined ? 'nobody (timed out)' : $('Keep reply').item.json.approver }}. Then an IF Approved?: {{ $json.decision }} is equal to approved.

Trap 3: a timeout is not a "no"

Measured on 23 October 2026: approved gives {data: {approved: true, respondedAt}}, rejected gives approved: false, and when the time limit runs out the node passes its input item through unchanged, with no data, on the same output. An IF that tests "is not false" would send the reply when nobody answered. Always test for approved.

7

Act, and log both branches

True: Gmail, Reply to {{ $json.message_id }} with {{ $json.reply }}, then Google Sheets Append Row to the Approval log. False: Gmail, Add Label needs-human, then Append Row. Map the nine columns (timestamp, workflow, run_id, action_proposed, tier, approver, decision, final_content, notes) from $('Decision'), Cell Format Let n8n format. Rejections and timeouts are rows too.

8

Three test e-mails, one at a time

In the test sender set const i = 0;, Execute workflow; in M10, Execute workflow; open the approval e-mail and Approve and send. Then i = 1 and Reject. Then i = 2 and do nothing for 3 minutes. The Approval log should show approved, rejected and timed out.

Video, recorded live: the finished workflow node by node, as a walkthrough for this lab (narrated, with captions).
Video, recorded live: lowering the tier (Session 9's draft instead of send), and reading approval fatigue in the log (narrated, with captions).

The gate records a click, not a person

The node's output is approved and the time of the answer; it does not say who clicked, and anyone who has the e-mail can press the button. Send approval requests to one person's own mailbox, never a shared one or a forwarding list, and use a channel with sign-in (Slack, Teams, a form behind login) for Tier 5 decisions. Module 11 picks this up.

Deliverable

An export of the gated workflow, a screenshot of the approval message showing the full draft, and the Approval log sheet with at least one approved, one rejected and one timed-out row. Save as M10-approval-gate.

Knowledge check

Pick one answer per question, then check your score. These mirror the style of the final exam.

1. An agent can read tickets, add labels, and occasionally send a customer an email. Which tier sets its controls?

Why: Controls follow the worst case an agent can reach. Sending customer email is Tier 4, so the agent is gated as Tier 4.

2. Which pattern best fits 30 customer replies a day where the wording matters?

Why: Customer-facing text is Tier 4. At tens per day, having the person adjust and send each draft is affordable and keeps them the author.

3. What should happen when an approval request is not answered within the time limit?

Why: The safe default on timeout is to not act. Acting on silence turns the gate into a delay, not a control.

4. Which of these is a sign of approval fatigue?

Why: Near-instant, near-universal approval means people are clicking without reading. The gate has stopped being a control.

5. The cheapest way to remove the need for a gate on a "send email" step is to...

Why: Lowering the tier of the tool (send becomes draft) keeps the benefit and removes the irreversible action, so no gate is needed.

Self-check

Answer in your own words first, then open the model answer.

1. List the five risk tiers and give one action from your own work for each.

Read-only (look up a record), reversible write (add a row or label), irreversible internal (delete or overwrite), external (email a customer), and money/people/legal (approve a refund or leave). Any concrete mapping is acceptable if the consequence matches the tier.

2. Describe interrupt-and-resume in three sentences without naming a product.

The workflow reaches a checkpoint and saves everything it knows. It stops and waits for a signal from a person. When the signal arrives, it continues from the checkpoint with the person's decision included.

3. What must an audit-trail row contain for a gated action, and why must the log be append-only?

Timestamp, workflow and run id, the proposed action and tier, the approver, the decision including rejections and timeouts, and the final content. Append-only so that history cannot be edited after the fact and the record is trusted.

Summary

Key takeaways

  • Tier each tool by the cost of a wrong call: read-only, reversible, irreversible, external, and money/people/legal. Controls follow the highest tier reachable.
  • Four HITL patterns cover every case: approve before, review after, edit-then-approve, escalate on low confidence.
  • A gate is an interrupt: save state, pause, wait for a person, resume. In n8n that is a Wait node or a send-and-wait-for-approval node.
  • Limits cap how much can go wrong between gates; an append-only audit log records who approved what and when.
  • A gate people rubber-stamp is worse than none. Lower the tier, batch, or escalate only the uncertain cases.

Further reading: LangGraph: checkpointing and human-in-the-loop · n8n guide: error handling and approval steps