Resend · Email agents · API key safety

Resend AI agent API key: rate-capping email sends before the loop runs wild

Giving an AI agent a Resend API key is straightforward. Making sure a stuck loop doesn't send 50,000 password-reset emails in an hour is the part most developers don't think about until after the incident. This page covers Resend's native domain scoping, the three gaps it leaves for agent use cases, and the vault-key pattern that fills all three.

TL;DR

Resend API keys are account-scoped by default — they can send from any verified domain in your account. Resend lets you restrict a key to a single domain, which is useful. What it doesn't provide is a per-day send cap, a per-agent rate limit, or a mid-run revoke that doesn't break other consumers. A vault-key proxy closes all three: the agent holds a scoped vault key, the proxy enforces a send cap and domain restriction, and you can revoke the agent's access in one click without rotating your production Resend key.

How agents typically use Resend

The most common pattern is a transactional email tool the agent calls when it needs to notify a user — a welcome email, a payment receipt, a status update. The tool wraps Resend's emails.send endpoint:

import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

async function sendEmailTool({ to, subject, html }) {
  const result = await resend.emails.send({
    from: 'agent@yourdomain.com',
    to,
    subject,
    html,
  });
  return result.id;
}

Hand that function to an agent as a tool and it works in a demo. The production risk is that a reasoning loop can call the same tool multiple times — either because it doesn't see the first call succeeded, or because of a bug in the workflow that triggers the same node twice. Resend has account-level rate limits (2 requests/second; daily send limits that scale with plan), but those are shared across your entire account. One runaway agent can exhaust the daily limit for every other service that shares the key.

What Resend's native controls give you

Resend lets you create API keys with two useful scopes: Full access (can send, read, manage domains) and Sending access (can only send emails, cannot manage account). You can also restrict a key to a specific domain — so a key restricted to notifications.yourdomain.com cannot send from billing.yourdomain.com.

That domain restriction is genuinely useful for agent use cases. But three gaps remain:

GapWhat happensWhy it matters for agents
No per-agent send cap A stuck loop sends until the account daily limit is hit One runaway agent blacks out email for your entire application
No mid-run revoke Revoking the key breaks every service using it The only stop for a runaway agent is a production key rotation
No per-call audit with agent context Resend logs deliveries by email address; no agent_run_id Post-incident forensics require manual Resend dashboard + application log correlation

Three failure modes that slip past domain scoping

1. The idempotent re-trigger. An agent sends a welcome email as part of onboarding. The workflow has a bug that re-enters the welcome node if the next step fails. The agent sends 12 identical welcome emails to the same user before someone notices. Domain restriction doesn't help — all sends are from the allowed domain.

2. The list-blast loop. An agent is given a list of subscribers and told to send a weekly digest. A bug in the loop logic causes it to iterate over the full list for each subscriber. 2,000 subscribers × 2,000 sends = 4,000,000 sends attempted. Domain restriction doesn't help; a per-day cap on the vault key would have stopped it at, say, 5,000.

3. The compromised sub-agent in a multi-agent system. In a CrewAI or LangGraph system, one sub-agent handles communications. If that agent's context is poisoned via prompt injection (a user inputs a payload that tells the agent to "send a summary to all users"), the entire account's daily email budget can be exhausted before any other system detects the anomaly. Per-agent caps mean the blast radius is bounded to one agent's daily quota.

The vault-key pattern for Resend

The vault-key pattern puts a proxy between your agent and Resend. The agent holds a vault_key_xxx instead of the real Resend API key. The proxy looks up the real key, enforces the policy you configured, forwards the request to Resend, and logs the call with your agent_run_id.

import { Resend } from 'resend';

// One change: baseURL points to the proxy; api_key is the vault key
const resend = new Resend(process.env.VAULT_KEY, {
  baseURL: 'https://proxy.keybrake.com/resend/v1',
});

async function sendEmailTool({ to, subject, html }) {
  const result = await resend.emails.send({
    from: 'agent@yourdomain.com',
    to,
    subject,
    html,
  });
  return result.id;
}

The vault key policy you configure on the Keybrake dashboard:

{
  "vendor": "resend",
  "daily_send_cap": 500,
  "allowed_from_domains": ["notifications.yourdomain.com"],
  "expires_in": "4h",
  "agent_run_id": "onboarding_run_abc"
}

With this policy, the agent can send at most 500 emails in a 24-hour window, only from your notifications domain, and the vault key automatically expires after 4 hours. Revoking it takes effect on the next API call — no production key rotation needed.

How Keybrake fits

Resend is one of Keybrake's three v1 vendors (alongside Stripe and Twilio). The cost per email is a fixed rate parseable from Resend's response, so Keybrake can enforce dollar-denominated caps as well as count-based caps. The Free tier covers 1,000 proxied requests/month across one vendor; the Hobby tier ($29/month) adds all three vendors and 30-day audit log retention.

Get early access

Related questions

Does the baseURL override work with the Resend Node.js SDK?

Yes. The Resend SDK accepts a baseURL option in the constructor. The proxy speaks the Resend HTTP wire protocol exactly — the SDK doesn't know it's talking to a proxy. All Resend SDK methods (emails.send, emails.get, contacts.create) route through the proxy transparently. Webhook events from Resend still come directly from Resend's servers and use your real signing secret.

What happens when the agent hits the daily send cap?

The proxy returns an HTTP 429 with a Retry-After header set to midnight UTC. The Resend SDK surfaces this as an error; the agent receives "daily send cap reached" in the tool result and can decide to abort or notify the user. You can also configure a webhook alert that fires before the 429 — on the send that would exceed the cap — so you get notified before the agent is blocked.

Can I restrict the vault key to a specific "to" domain as well as the "from" domain?

Yes. The vault key policy supports an allowed_to_domains list. Useful for agents that should only send to customers of a specific tenant, or to internal addresses only during testing. Any send to a domain outside the list returns a policy violation error — logged in the audit trail — rather than a Resend API error.

Further reading