Why approval is what lets agents act
Most teams start with read-only agents because the risk of a wrong write is too high. Approval steps change that trade-off: the agent does the slow work (reading the case, gathering facts, preparing the action) and a person spends seconds saying yes or no. Over time, as the numbers hold, more cases can be approved automatically.
Eight patterns
- Approve actions, not conversations. Put the gate on specific tools, usually every write tool, so reading and drafting stay fast.
- Deny by default. If no approver answers, or the approver is unavailable, the action does not happen.
- Check eligibility before asking. Run the tool's own validation first (a dry run), so people are never asked to approve something the system would reject anyway.
- Show a preview. The approver should see what will change: the amount, the recipient, the record before and after, and the agent's reason.
- Route to the right person and channel, with a timeout. A refund goes to support in Slack; a contract change goes to legal by email. Time out to "denied".
- Use thresholds. Auto-approve low-risk actions, such as refunds under a set amount, once your evaluation results justify it. Keep people on everything above the line.
- Batch bulk operations. Fifty similar updates should be one review, not fifty interruptions.
- Log every decision and tell the agent. Record who approved what and when, and return a clear message on denial so the agent doesn't retry the same action.
A threshold approver in a few lines
In our agent harness template, an approver is just a function that receives the tool name and validated arguments and returns a decision. A threshold policy on top of it:
from harness import ApprovalDecision, ApprovalRequest
AUTO_APPROVE_REFUNDS_UP_TO = 25.00
def support_approver(request: ApprovalRequest) -> ApprovalDecision:
if request.tool == "issue_refund" and request.arguments["amount"] <= AUTO_APPROVE_REFUNDS_UP_TO:
return ApprovalDecision(True, "auto-approved under threshold")
# Everything else goes to a person; your function posts the preview to Slack
# or a dashboard and returns their decision, or a denial after the timeout.
return ask_support_team(request, timeout_seconds=900)ask_support_team stands in for your channel. The harness denies by default if no approver is set.Which actions need approval?
| Action | Approval? | Why |
|---|---|---|
| Look up, search, summarise | No | Read-only and easy to correct |
| Draft an email or document for review | No | A person sends it |
| Send a message to a customer | Yes, at first | Visible to customers and can't be unsent |
| Refund, pay or credit | Yes, or above a threshold | Moves money |
| Change or delete records | Yes | Hard to undo |
| Change permissions or settings | Always | Can widen the agent's own access |