Build Your First AI Agent · Lesson 7 of 8
Lesson 7 — Approval gates and human-in-the-loop
45 minutes. The single most important design pattern for agents that touch the real world.
---
Every action that goes out into the world — email sent, money moved, record updated in a system of record, contract signed — passes through a human approval gate.
This is non-negotiable. Without it, the first bad agent decision becomes a customer-facing problem.
The `send_email` tool doesn't send. It creates a draft and writes a row to an `approvals` table:
```sql CREATE TABLE approvals ( id INTEGER PRIMARY KEY, proposed_at INTEGER, action_type TEXT, payload TEXT, -- JSON: { to, subject, body } status TEXT, -- 'pending' | 'approved' | 'rejected' reviewed_by TEXT, reviewed_at INTEGER ); ```
The agent waits. A human reviews the pending row (in your AAT inbox, in a Slack channel, in an admin dashboard — wherever). Approve → another job actually transmits. Reject → the agent gets the feedback in the next loop and learns.
1. Inline review. The reviewer sees the draft, edits inline, hits approve. The final approved text gets sent — not the original draft. The edits become training signal for the next prompt revision.
2. Slack/Teams ping. The agent posts to a channel: "*Draft reply to Mary Collins — approve, reject, or edit.*" Reviewers respond with buttons. Lower friction; less editing.
3. Daily review queue. All pending approvals batch overnight. Reviewer comes in at 8 AM, processes the queue. Best for high-volume agents.
Pick one. Use it consistently.
The `send_email` tool returns immediately with `{ status: 'pending_approval', approval_id: ... }`. The agent considers its job done; the workflow continues from there once the human decides.
A separate worker process polls the `approvals` table or listens for Slack events. When approved, it does the real send and writes back the result.
This decouples the agent from synchronous send. The agent doesn't wait around — it queues work for human review and moves on. Better latency, better safety.
→ Lesson 8 — Ship it
Previous: Lesson 6 — Errors, retries, and recovery · Next: Lesson 8 — Ship it