Send limits, reputation & budgets

Two safety valves that stop one runaway agent from sinking everyone's delivery — an automatic brake and a hard cap you set in advance.

When many agents send on your behalf, one bad actor — a loop that blasts a stale list, a prompt that emails the wrong people — can drag down delivery for everyone. Dairo gives you two ways to contain it: a budget is the hard cap you set in advance, and reputation is the automatic brake that reacts to bounces and complaints. Use either or both.

Both are part of the optional agent layer. They use separate scopes: reading reputation uses agents:read and clearing a tripped agent uses agents:write; reading budgets uses budgets:read and setting or deleting one uses budgets:write.

Reputation

Reputation watches each agent's bounces and complaints and gives you a simple shouldSend signal to check before you dispatch. If an agent goes off the rails, it's contained automatically.

Check before you send

The fastest use: read one agent's state and gate on shouldSend (or verdict === "allow") before a batch.

curl https://api.dairo.app/v1/agents/agt_7h2k9q4m3xeyrt6bv8wn0pcd/reputation \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Each agent reports a state, a verdict, its live bounce and complaint rates over the window, and a shouldSend boolean:

stateverdictMeaning
healthyallowSending normally.
throttledthrottleRate-limited after warning-level signals.
quarantinedblockTripped — sends are paused.
reviewreviewHeld for a human decision.

overrideLocked flags an agent an organization owner or admin pinned from the dashboard so it won't auto-trip; such an override lasts 1 hour by default and at most 24 hours (overrideLockedUntil), and an API key cannot set one.

See your whole fleet

GET /v1/agents/reputation returns every agent's state, newest-tripped first, with the window and thresholds alongside the data.

curl https://api.dairo.app/v1/agents/reputation \
  -H "Authorization: Bearer $DAIRO_API_KEY"
{
  "agents": [
    {
      "agentId": "agt_7h2k9q4m3xeyrt6bv8wn0pcd",
      "state": "healthy",
      "verdict": "allow",
      "shouldSend": true,
      "window": { "sent": 1200, "bounces": 5, "complaints": 0 },
      "rates": { "bounceRate": 0.004, "complaintRate": 0.0001 },
      "lastReason": null,
      "lastTrippedAt": null,
      "overrideLocked": false
    }
  ],
  "window": { "hours": 24 },
  "thresholds": { "complaintWarn": 0.001, "complaintTrip": 0.002, "bounceWarn": 0.03, "bounceTrip": 0.04 }
}

Clear a tripped agent

DELETE /v1/agents/{id}/reputation is an operator override: it lifts a quarantine or resolves a review back to healthy, and returns the reset state. It uses agents:write and is recorded in your audit log.

curl -X DELETE https://api.dairo.app/v1/agents/agt_7h2k9q4m3xeyrt6bv8wn0pcd/reputation \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Send budgets

A budget is a hard ceiling on sending that Dairo enforces for you. Set one on your whole account, a single API key, or an individual agent, and Dairo stops the next send the moment it would cross the line — so one bad loop can never quietly torch your sender reputation. If a human triggers every send, you may never need it. If code or an AI agent sends on its own, a budget is the seatbelt.

Pick what a budget covers

A budget attaches to one of three things. Set the ceiling where it makes sense — on the whole account, on the key a service uses, or on a specific agent.

CoversUse it forWhat you pass
AccountA single ceiling across everything you send.scope: "account"
KeyOne service or integration, isolated from the rest.scope: "key" + the key's id
AgentOne agent, so a misbehaving bot can't spend the whole account.scope: "agent" + the agent's id

Set a budget

One call creates or updates a budget. Pass the scope, the thing it covers, and at least one limit. You get back the budget with its live usage so far.

curl -X PUT https://api.dairo.app/v1/budgets \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "scope": "agent",
        "scopeId": "agt_7h2k9q4m3xeyrt6bv8wn0pcd",
        "limits": { "maxSendsPerDay": 500, "maxNewRecipientsPerHour": 100, "hardStopOnComplaint": true }
      }'
{
  "budget": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "scope": "agent",
    "scopeId": "agt_7h2k9q4m3xeyrt6bv8wn0pcd",
    "enabled": true,
    "limits": { "maxSendsPerDay": 500, "maxNewRecipientsPerHour": 100, "hardStopOnComplaint": true },
    "usage": { "sendsLast24h": 18, "newRecipientsLastHour": 3, "accountHasComplaint": false },
    "createdAt": "2026-06-12T10:00:00Z",
    "updatedAt": "2026-06-12T10:00:00Z"
  }
}

The limits you can set

Set any combination of these. A budget needs at least one.

LimitWhat it caps
maxSendsPerDayTotal sends in a rolling 24-hour window.
maxNewRecipientsPerHourFirst-time recipients per hour — the classic shape of a runaway loop blasting a fresh list.
hardStopOnComplaintWhen true, the moment a recipient reports spam, every further send under this budget stops until you intervene.

An account budget also takes maxSpendPerDayCents, a cap on metered spend (calls, letters, AI) over a rolling day. It applies to account budgets only; on a key or agent budget it is a 400. See spend caps.

What happens at the ceiling

When a send would cross a budget, Dairo refuses it with a 429 carrying the budget_exceeded error code — whether the ceiling it hit was a send cap, a new-recipient cap, or the complaint hard-stop. The code lets an agent branch cleanly: back off, queue for later, or alert a human, instead of retrying into a wall.

{
  "error": {
    "code": "budget_exceeded",
    "message": "Daily send budget reached for this agent."
  }
}

An agent that reacts to its own ceiling should catch budget_exceeded and pause the loop rather than retry. See Errors for the full list of codes and how to handle them.

Check usage against a cap

Every budget carries live usage, so you can see how close a scope is to its ceiling before it gets there. List all budgets, or fetch one.

# Every budget
curl https://api.dairo.app/v1/budgets \
  -H "Authorization: Bearer $DAIRO_API_KEY"

# Only the account-wide one
curl https://api.dairo.app/v1/budgets/account \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Fetch one budget by passing account, or a key or agent id as the scopeId.

Remove a budget

Deleting a budget removes the cap entirely — sending under that scope is no longer ceilinged.

curl -X DELETE https://api.dairo.app/v1/budgets/agt_7h2k9q4m3xeyrt6bv8wn0pcd \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Setting or deleting a budget is a privileged action. A key that can only send mail can't raise its own ceiling — so a leaked sending key stays boxed in by the limits you set.

Next steps

On this page