How to Stop an AI Agent From Sending Too Many Emails: Budgets, Rate Ceilings and a Kill Switch
Volume controls for AI agents that send email: how to size a per-key budget and rate ceiling, wire the 80% warning, and stop every key in one request, with the reputation math behind each.
Direct answer. To stop an AI agent from sending too many emails, give it its own AgentiSend key with a budget_per_period and a rate_ceiling_per_minute, subscribe to the limit.warning webhook that fires at 80% of the budget, and keep POST /limits/kill-all ready: one request pauses every key, and no API key, including the agent's, can turn it back on. If you only need a coarse account-wide cap and the agent runs on a provider you already use, check that provider's own limits first; AgentMail, Plunk and MailerSend each have one, though none we read is set per key.
How we put this together
For this comparison we went through the pricing pages of AgentiSend, Resend, Amazon SES, MailerSend, Mailtrap, SMTP2GO, Plunk and AgentMail on 9 November 2026 and checked their documentation between 28 September and 10 November 2026. We did not run agents against these limits; the behaviour described is what each provider documents.
This guide is about volume: an agent that sends more than it should. An agent that sends the same message over and over is a different failure with a different control, covered in our piece on stopping an agent that sends the same email in a loop.
Why volume is a reputation problem before it is a billing problem
Most people meet this problem through the invoice. The more expensive damage is to the domain. An agent that over-sends usually does so to addresses it should not be mailing: an old export, a scraped list a user pasted in, guessed addresses. Those produce bounces and complaints, and mailbox providers count them against your domain whether or not a person was involved.
AgentiSend publishes the lines it enforces, which makes the risk concrete. It warns at 2% bounces and pauses at 4%, over a rolling 7 days. Suppose your app normally sends 3,000 messages a week with 30 bounces (1%). An agent that mails 1,000 stale addresses in an afternoon, a fifth of which bounce, adds 200 bounces: the week becomes 230 bounces on 4,000 sends, 5.75%, over the pause line. Every other sender on the account pauses with it. Volume controls exist to make that afternoon impossible.
The six controls, in the order they act
| Control | What it limits | What the agent sees | Who can undo it |
|---|---|---|---|
| Rate ceiling | Messages per rolling minute, per key | rate_ceiling_exceeded, retryable after the stated wait |
A person raises it; the key can lower it |
| Key budget | Messages per period, per key | agent_budget_exceeded |
A person raises it, or the period resets |
| Plan inclusion | Messages per month, whole account | plan_limit_reached (overage is off by default) |
The owner turns overage on, with a ceiling |
| Warning webhook | Nothing; it tells you | Nothing | Not applicable |
| Single-key pause | Everything from one key | kill_switch_active |
A signed-in person |
| Kill switch | Every key on the account | kill_switch_active |
A signed-in person only |
Both ceilings count messages, not money, and a recipient counts as one message, so one send to five people uses five. There is no daily cap on paid plans beyond what you set; delivery is paced per inbox provider.
Step 1: size the budget from the job, not from the plan
Every AgentiSend key is created with a budget, and the default is the plan's whole monthly inclusion. On Pro that is 50,000 or 100,000 messages, far more than a support agent needs. Size it from the work:
| Agent | Expected sends | Budget per 30 days | Rate ceiling per minute |
|---|---|---|---|
| Support-ticket replies | 40 a day | 1,500 (1,200 plus 25% headroom) | 5 |
| Weekly account digests | 2,000 every Monday | 9,000 | 60 |
| Sales-meeting follow-ups to people who booked | 15 a day | 600 | 2 |
Now compare how far each agent gets if it goes wrong, sending as fast as it is allowed:
| Agent | Default budget (Pro 50,000) | With the sized budget and ceiling |
|---|---|---|
| Support replies | Up to 50,000 sends | Stops at 1,500; at 5 a minute that takes 5 hours, with the 80% warning at 1,200 |
| Weekly digests | Up to 50,000 sends | Stops at 9,000 |
| Meeting follow-ups | Up to 50,000 sends | Stops at 600, at 2 a minute |
The ceiling buys time; the budget sets the worst case. Together they turn "the agent emailed our whole user base" into "the agent sent a few hundred extra messages and someone got a warning."
Step 2: set both ceilings
import { AgentiSend } from 'agentisend';
const owner = new AgentiSend(); // your key, never the agent's
const agentKey = await owner.apiKeys.create({
name: 'support-replies-agent',
permission: 'sending_access',
});
await owner.limits.update(agentKey.id, {
budget_per_period: 1500,
rate_ceiling_per_minute: 5,
});
Or over REST with PATCH /limits/keys/{id}. Read the current state, including when the period resets, with GET /limits/keys/{id}; an agent connected over MCP calls get_agent_budget.
Two rules keep the agent inside these numbers. A key may lower its own budget or ceiling, which lets an agent cap itself before a big job, but raising either is refused with human_action_required and done by a person in the console. And a key made by another key starts no higher than its parent, so minting a fresh key does not reset a budget you lowered.
Step 3: hear about it at 80%
limit.warning fires once per period when a key reaches 80% of its budget, and again for the account at 80% of the plan inclusion. limit.exceeded fires the first time a send is refused. Both carry api_key_id (null for the plan inclusion), used, budget and period_started_at; the exceeded event adds resets_at.
A handler can do more than log. This one pauses a key the moment it crosses 80% outside working hours, so nobody wakes up to a spent budget:
import { AgentiSend } from 'agentisend';
const owner = new AgentiSend();
export async function onLimitEvent(event: {
type: string;
data: { api_key_id: string | null; used: number; budget: number };
}) {
if (event.type !== 'limit.warning' || !event.data.api_key_id) return;
const hour = new Date().getUTCHours();
const outOfHours = hour < 7 || hour >= 19;
if (outOfHours) {
await owner.limits.kill(
event.data.api_key_id,
`paused at ${event.data.used}/${event.data.budget} outside hours`,
);
}
}
Verify the webhook signature on the raw body before trusting the event, using verifyWebhookSignature from agentisend/webhook-signing.
Step 4: keep the kill switch one command away
When something is clearly wrong, you do not want to work out which key it is. One request pauses all of them:
curl -X POST https://api.agentisend.com/limits/kill-all \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "digest agent mailing an old export"}'
Every send then returns kill_switch_active. Mail already queued stays queued rather than being dropped, so nothing legitimate is lost. The pause and the reason go into the audit log.
Undoing it is a person's job. Someone signed in to the console presses Resume all, which calls POST /limits/resume-all with their session. The same call from any API key, even the one that triggered the pause, is refused, and AgentiSend's docs show the response (abridged):
{"error": {"code": "human_action_required",
"message": "This is a person's decision, so an API key cannot make it."}}
A paused key also cannot create a key, rotate one or change a budget; each attempt returns kill_switch_active. It can still pause itself.
Step 5: let the plan be the last wall
AgentiSend's overage is off by default. When the account's monthly inclusion is used, sending pauses with plan_limit_reached until the owner turns overage on, up to a ceiling they choose. For an account with agents on it, leave it off until you have a month of normal numbers.
How other providers handle volume
| Provider | Volume control documented | Set per key? |
|---|---|---|
| AgentiSend | Budget and per-minute ceiling per key, plan inclusion, single-key pause, kill switch | Yes |
| AgentMail | Plan caps: 100 a day on Free; 1,000 a day and 100 every 5 minutes on Developer | No |
| Plunk | Monthly caps per project and category; crossing one returns 402 BILLING_LIMIT_EXCEEDED |
No |
| MailerSend | A sending limit the account sets for itself (MS42204); a token can be paused |
No |
| SMTP2GO | A rate limit per API key, unlimited by default | Rate only, not a count |
| Mailtrap | 150 requests per 10 seconds per token | Request cap, not sends |
| Amazon SES | SendingEnabled false disables the whole account |
No |
| Resend | 10 requests a second per team; daily and monthly quota errors | No |
AgentiSend
Best for: any account where more than one service or agent sends, because each key gets its own budget and one pause stops them all. The budget controls are described on AgentiSend's agent email budgets page, and plans start free at agentisend.com.
AgentMail
Best for: agents with their own inboxes that need a hard plan-wide ceiling. Watch out for: one agent can use the whole plan's daily allowance.
Plunk
Best for: separating transactional, campaign, workflow and inbound spend. Watch out for: two agents in one project share every cap.
MailerSend
Best for: an account-level spending stop. Watch out for: its errors page says to raise the limit under Plan and billing, and nothing we read stops an agent's key from being the one that holds that access.
SMTP2GO and Mailtrap
Best for: slowing a sender down. Watch out for: a rate limit caps speed, not total volume; a slow agent left running overnight still sends thousands.
Amazon SES
Best for: a single account-wide off switch. Watch out for: it stops every sender at once with no per-credential option, and AWS's page says the call cannot re-enable sending if AWS paused the account.
Questions people ask
What is a sensible email budget for an AI agent? Its expected volume over the period plus about a quarter for headroom. Then set a per-minute ceiling near its real peak, so a runaway reaches the budget slowly enough for the 80% warning to matter.
Can an agent raise its own email limit? Not on AgentiSend. A key can lower its budget or ceiling, but raising one returns human_action_required, and only a person in the console can do it.
What happens to queued emails when the kill switch is on? They stay queued. Nothing is dropped, and they send after a person resumes.
Does a budget protect my sender reputation? It caps the damage. A budget does not judge whether the recipients wanted the mail, but it limits how many bounces and complaints a bad run can add before it stops.
Sources
Pricing pages read 9 November 2026: agentmail.to/pricing, resend.com/pricing. Documentation read 28 September to 10 November 2026: docs.useplunk.com/concepts/billing, developers.mailersend.com/api/v1/account/tokens, developers.mailersend.com/general, developers.smtp2go.com/docs/getting-started, docs.mailtrap.io (rate limits, billing plans), docs.aws.amazon.com/ses/latest/APIReference-V2/API_PutAccountSendingAttributes.html, resend.com/docs/api-reference/rate-limit.