Governance

Handler is built around a simple principle: agents should be powerful, but every action must be tied to a profile, and every profile plays by your rules. Every call is evaluated against your governance rules before it executes.

How It Works

When your agent makes a call through Handler, the request passes through your governance rules before anything happens. Based on those rules, Handler does one of three things:

  1. Auto-approves the call and executes it immediately.
  2. Holds the call and notifies you for approval.
  3. Blocks the call outright and tells the agent why.

This happens in milliseconds. Your agent gets an instant response in all three cases — it never hangs waiting for an unknown outcome.

How you set the rules

Governance is about which actions run automatically, which need your approval, and which are off-limits — not dollar thresholds. Handler classifies every action as a read, a write, or a destructive operation, and you decide what happens to each class. You control this through a few layers, from broad to precise:

1. Templates (risk classes)

The fastest way to set a profile's posture. A template maps the three action classes to an outcome — auto run immediately, approve hold for you, or block refuse. Handler ships three:

A profile with no template applied falls back to Balanced behaviour, so a new agent can never silently perform a write without approval.

2. Per-action overrides

Need an exception to the template? Set a specific service or action to allow, approve, or block. A per-action rule wins over the template — for example, keep a Balanced profile but always require approval for handler_communicate sends, or block a single destructive action outright.

3. Service pins

Pin a whole service to require owner approval. A pin is a floor: it forces a hold even when the template would auto-approve — but it can't override an explicit block. Useful for "this agent can use Gmail, but I want to see every send."

4. Approval policies

For conditional routing, create an approval policy. Policies match by scope (action, service, profile, group, or org) and can carry conditions — including a cost condition (cost_above / cost_below) if you do want dollar-based holds — and route the hold to a chosen approver. Policies are how you express "hold anything over $1, and send it to Alice."

5. Spend caps & balance

Independently of the approve/block chain, you can set a monthly spend cap at the org, profile, or instance level, and your org runs on a prepaid balance. Calls fail cleanly when a cap is reached or the balance hits zero — this is a spending guardrail, separate from the action-class rules above.

How a decision is made (precedence)

When a call comes in, Handler resolves it top-down and stops at the first layer that decides:

  1. Explicit per-action rule — an allow/approve/block set on that exact service/action wins over everything.
  2. Template — otherwise the profile's template maps the action's class (read/write/destructive) to auto / approve / block.
  3. Approval routing — if the outcome is "approve", the profile's routing (see below) decides who gets the hold.
  4. Approval policies — if routing is left at its default, matching policies (scope-ranked, then by priority) decide.
  5. Default — with nothing else matching, the call auto-approves.

Two guardrails apply on top: a service pin forces a hold (but never overrides a block), and spend caps / org suspension can fail a call regardless of the above.

Where approvals go

Each profile has an approval routing setting that decides who a held call is sent to:

Tip: Start a new agent on Balanced, watch what it does in the activity log, then loosen specific services (per-action allow) as you build trust — or tighten sensitive ones with a pin.

Approval Channels

When a call is held for approval, Handler notifies you through your configured channel. You can approve or deny with a single tap — no need to log into a dashboard.

Approve or deny with one tap from your phone — even at 2am. Your agent is held in a pending state until you respond. No timeout, no auto-approval. You stay in control.

Pending request lifetime

Pending requests do not expire automatically. A request can sit in the queue for minutes, hours, or days — it will remain actionable until you explicitly approve or reject it. This is intentional: Handler never makes a call on your behalf without your decision.

What this means for your agent: if your agent calls a tool that gets held and the owner goes offline for a weekend, the agent will continue receiving status: "pending" for that request indefinitely. Well-designed agents should:

Handler does not currently send a "request expired" signal to agents. If you need time-bounded workflows, reject the request manually from the dashboard and the agent will receive status: "rejected" on its next poll.

Risk classes (templates)

Not sure where to start? Pick the template that matches your comfort level. You can change it, or override individual services, at any time.

Template Reads Writes Destructive Best for
Permissive auto auto auto Fully trusted agents on non-sensitive work
Balanced auto approve approve Most production agents — the default
Restrictive auto block block Read-only agents; sensitive environments

When you create a new agent you pick a template, which sets smart defaults you can customize any time. A profile with no template applied behaves like Balanced, so writes always require approval until you decide otherwise. Want dollar-based holds on top? Add an approval policy with a cost_above condition (see How you set the rules above).

Example Scenario

Here's a real-world walkthrough of governance in action.

Agent makes a call

Your agent decides to send an outbound email:

handler_communicate({
  "task": "send",
  "to": "ceo@bigcorp.com",
  "subject": "Partnership proposal",
  "body": "Hi, I'd like to discuss a potential partnership..."
})

Handler evaluates the rules

Sending an email is a write action. This profile is on the Balanced template, so writes require approval — regardless of the tiny $0.005 cost. (A service pin on handler_communicate, or a per-action approve rule, would force the same hold.)

You get notified

Handler holds the request and sends you a Slack message:

Your agent wants to send an email
To: ceo@bigcorp.com
Subject: Partnership proposal

Approve   Deny

You approve

You tap Approve. Handler sends the email and returns the result to your agent. The entire flow — from agent call to your approval to execution — takes seconds.

Agent gets the result

Your agent receives the confirmation and continues its workflow:

{
  "status": "executed",
  "result": {
    "message_id": "msg_a1b2c3",
    "sent_to": "ceo@bigcorp.com"
  },
  "cost": { "charged": 0.005 }
}

What Agents See

Your agent always gets an immediate, structured response. It never hangs or times out. Here are the three possible outcomes:

Executed

The call was auto-approved and completed successfully.

{
  "status": "executed",
  "result": {
    "results": [...],
    "query": "AI agent frameworks 2026"
  },
  "cost": { "charged": 0.005 },
  "budget": { "remaining": 9.995 }
}

Pending

The call is held for owner approval. The agent should acknowledge and wait or move on to other tasks.

{
  "status": "pending",
  "message": "Held for owner approval. Your owner has been notified.",
  "request_id": "req_x7y8z9"
}

Blocked

The action isn't permitted — the profile's template blocks that action class (e.g. a write on a Restrictive profile), a rule blocks that service, or a spend cap / zero balance stopped it. The agent should inform the user or try an alternative approach.

{
  "status": "blocked",
  "message": "Writes are not allowed for this agent (Restrictive template)."
}
Tip: Well-designed agents handle all three statuses gracefully. A pending response means the agent can continue with other work while waiting. A blocked response means the agent should explain the constraint to the user.