Approvals
Human-in-the-loop for irreversible tool calls. What triggers them, how to clear the queue, email notifications.
Approvals are Tenet's pause-button for irreversible actions. When your agent tries to call a tool we've classified irreversible (github.repos.delete, stripe.refunds.create, etc.), tenet.execute() returns ESCALATE instead of running the call. A human approves or rejects the action in the dashboard; only after approval does the downstream call execute.
What triggers an escalation
- A locked service. Every newly connected service starts Locked: every call escalates until you relax the trust level at
/protections. If you're seeing escalations on read-only calls, this is why. - Any tool classified as
irreversiblein the built-in catalog — at every trust level, even Trusted; see the policies guide. - Stripe charges or payment_intents over $100.
- A per-function override you've set to
escalate.
You can't edit which tools are classified irreversible, but you can set per-function overrides (allow / escalate / block) at /protections; see the current classification there.
The approval flow
Your agent calls tenet.execute() →
Engine evaluates → outcome: ESCALATE →
Review row created at /reviews →
Email sent to your notification email (if email_mode = per_event) →
[you approve in dashboard] →
Pending execution fires the original call →
Audit log gets the final decision
The execute response on ESCALATE looks like:
{
"decisionId": "dec_abc...",
"outcome": "ESCALATE",
"reviewId": "rev_xyz...",
"reasonCodes": ["irreversible-tool"]
}
Your agent should NOT proceed as if the call ran. You can poll /v1/executions/{requestId} for the final state, or just rely on the email/dashboard for the human to act.
Approving / rejecting
Open /reviews in the dashboard. Each pending row shows:
- The tool being called and its arguments
- The agent making the call
- The risk score (if calculated)
- The reason codes (why this policy fired)
Click Approve: the call runs with your downstream credentials, returns the result, and the audit log gets updated. Click Reject: the call is permanently refused, no side effects.
Email notifications
By default, escalations trigger emails to your notification email (set at /settings; falls back to your account email) with a 30-minute rate limit. Three modes at /settings:
- Per-event (default): first escalation in a 30-min window sends immediately. Subsequent events in the window batch into a digest at the next 30-min mark.
- Digest only: suppress immediate emails; only the 30-min digest fires.
- Off: no emails. You'll need to check
/reviewsmanually.
Emails currently link to /reviews for action. One-click approve/reject from email is not yet available (it requires signed magic-link tokens we haven't built yet).
Concurrency
If your agent makes multiple irreversible calls in parallel, each gets its own review row. They're independent; approving one doesn't approve the others. The agent has to wait on each.
Tradeoffs
- Latency. ESCALATE adds human-loop time, by design. Don't put approvals on the hot path of a real-time agent.
- Throughput. You can't approve 1000 escalations a minute. If your agent legitimately does that many operations, set a per-function override or an amount condition at
/protectionsto auto-approve the safe subset, and keep the gate on the rest. - Unreviewed escalations. A review left pending never auto-executes. It expires after 24 hours; an expired review behaves like a rejection (the call never runs).
What's next
- The policies guide explains which tools count as irreversible.
- The audit log guide shows how the escalation decision lands.