Docs · Guides

Deliverability

How to keep your agents’ mail out of spam: warm-up, SPF/DKIM/DMARC alignment, content, our sending limits and why they exist, and the deliverability API.

View as Markdown

Whether a message lands in the inbox is decided by the receiving provider, per message, from the sender’s reputation, authentication, the content and how recipients react. We handle the infrastructure (our own mail server, DKIM signing, reverse DNS, blocklist monitoring, bounce handling). What your agents send, to whom and how often is up to you, and it matters more.

#Inbox, Promotions, spam

  • Spam means the provider doesn’t trust the sender or the message: failed authentication, a poor reputation, many complaints or bounces, or content that looks like fraud.
  • Promotions (and similar tabs) is not spam. It is where a provider files mail that looks like marketing: heavy HTML layouts, many images and links, tracking, discount language, sent to many people at once. It is a sorting decision, and you can’t switch it off.
  • Inbox is what a personal, one-to-one message gets: plain wording, few links, a reply to something the recipient wrote, from an address they have exchanged mail with before.

Agents that write one-to-one mail (support replies, scheduling, sign-ups, follow-ups) should look like a person writing: short plain text, or light HTML with a text part, one or two links, no tracking. That is also what we send: every message has a text/plain part, and we add no footer, tracking pixel or link rewriting.

#Authentication and alignment

Receivers check three things: SPF (is this server allowed to send for the envelope domain?), DKIM (is the message signed by a key published in DNS?) and DMARC (does one of them pass for the domain in the visible From address, and what should happen if not?). DMARC needs alignment: the domain that passes SPF or DKIM must match the From domain (or share its organizational domain).

  • On our shared domain (homingbox.net, or agents.agentboxd.com for inboxes created before the move) everything is set up for you: SPF, DKIM with our key, and DMARC all align.
  • On your own domain, connect it and publish the records we show. We sign with your domain’s own DKIM key, so DKIM aligns with your From address, and the SPF include:homingbox.net covers our server. Start DMARC at p=none, read the reports for a few weeks, then move to p=quarantine and p=reject.
  • Use a subdomain such as mail.yourcompany.com for agents, so their reputation is separate from your people’s mail on the main domain.
  • Rotate the DKIM key yearly (how); the domain page reminds you.

#Warm-up

Providers are suspicious of a new domain or address that suddenly sends a lot. Build volume over a few weeks:

  • Week 1: tens of messages a day, to people who expect them (colleagues, test accounts, customers who wrote first).
  • Then roughly double the daily volume every few days while bounces stay under 2 % and complaints near zero.
  • Prefer replies over cold mail. A conversation the recipient started is the strongest positive signal there is.
  • Keep volume steady. Spikes (a whole list in one hour) look like a compromised account.

#Content and recipients

  • Only write to people who expect it. Never buy or scrape lists; a hard bounce or a spam complaint costs far more than one message gains.
  • Stop at the first bounce. An address that hard-bounces is suppressed for your workspace (sends to it answer 422 recipient_suppressed), and a complaint suppresses the address too. Suppressions are per workspace: another customer’s bounce or complaint never stops you from writing to an address, and yours never stops them. If a suppressed address works again, you can remove it yourself (see Your suppression list).
  • Honour "stop" and unsubscribe requests at once, and make them easy: a line such as “reply STOP and we won’t write again” works for one-to-one mail.
  • Write a real subject and a greeting, keep links few and to your own domain, avoid URL shorteners and attachments nobody asked for.
  • Give the inbox a clear display name (“Acme Support”) and keep the same address for the same purpose.
  • Watch for replies: an agent that answers out-of-office messages or other agents can loop. Check Auto-Submitted headers and the ai:auto-reply label, and use pause as a kill switch.

#Our limits and why

Every workspace shares our sending IP and, unless it uses its own domain, our sending domain. One workspace’s spam would land everybody else’s mail in spam, so we enforce limits before providers do.

LimitValueWhy
Sends per dayFrom your plan (Free 100, Builder 2,000, Team 10,000); a new workspace sends up to 20 a day (10 per 5 minutes) for its first 3 days, on any plan including promo codes; 429 org_daily_limit_exceededCaps the damage a runaway or compromised agent can do in a day, and keeps a brand-new account from harming the shared sending reputation before it has any history.
Sends per 5 minutesFrom your plan (Free 20, Builder 200, Team 1,000); 429 rate_limited with Retry-AfterStops a loop within minutes, long before the daily cap.
Recipients per message50 (to + cc + bcc)One-to-one and small-group mail, not bulk campaigns.
Hard-bounce rateSending pauses above 5 % over 7 days (at least 20 sends); the owners are warned above 3 %. A bounce counts when the receiving server refuses the message while we deliver it. These bounces are recorded on the message but don’t count: a bounce email that arrives later, a bounce on a reply to addresses that mail you received gave (its From, Reply-To, To or Cc), and an address on a domain that doesn’t exist or takes no mail, or an unknown address on our own domains. GET /v1/deliverability counts them the same way.Around the level at which sending services put an account under review.
Complaint rateSending pauses above 0.1 % over 7 days (at least 20 sends); the owners are warned above 0.06 %, and at least 3 complaints at three times the limit suspend the workspace. A complaint that a mail provider reports to us by email is recorded on the message and suppresses the address; it doesn’t count, and a person reviews a workspace that would be over the limit with it.The threshold large mailbox providers publish for bulk senders.
Suppression listHard bounces and complaints, for the workspace that sent the mailMailing a dead or unwilling address again is the fastest way to lose reputation.

Over a limit, sending pauses (403 sending_paused): mail keeps arriving, reading and the API keep working, and queued mail waits. Clean up the recipient list, then press Request review on the dashboard banner; a person reads it and resumes sending. A second pause within 30 days, or clear abuse, suspends the workspace instead (403 org_suspended, queued mail is not sent). Sending health has every step, and Why was my send refused? every error.

#Your suppression list

The dashboard’s Deliverability page lists your workspace’s suppressed addresses with the reason and the date, and lets you remove one. The API does the same: GET /v1/suppressions lists them (permission contacts:read) and DELETE /v1/suppressions/:address removes one (contacts:write). Both need a workspace-wide key.

  • Remove an address only when you know it works again, for example a mailbox that was full or a person who asked you to write. Sending again to an address that bounced or complained hurts your deliverability and can get your sending paused.
  • So removing a bounce or a complaint asks you to confirm: in the dashboard, and with ?confirm=true in the API (409 confirmation_required without it).
  • Every removal is recorded as a suppression.removed event with who removed which address. If the address bounces again, it goes back on the list.
  • You only ever see and remove your own workspace’s entries. Sign-in links and other mail we send ourselves keep their own list.

#The deliverability API

GET /v1/deliverability (permission metrics:read, workspace keys only) summarises what affects your mail: your bounce and complaint rates over 7 and 30 days next to the thresholds at which sending pauses, how many of your contacts are suppressed, the SPF, DKIM and DMARC status of your domains, and whether our shared sending IP is on a blocklist.

curl
curl https://api.agentboxd.com/v1/deliverability \
  -H "Authorization: Bearer $AGENTBOXD_API_KEY"
response
{
  "rates": {
    "last_7_days":  { "sent": 412, "bounced": 3, "complained": 0, "bounce_rate": 0.0073, "complaint_rate": 0 },
    "last_30_days": { "sent": 1630, "bounced": 11, "complained": 1, "bounce_rate": 0.0067, "complaint_rate": 0.0006 }
  },
  "thresholds": { "bounce_rate": 0.05, "complaint_rate": 0.001, "min_sent": 20 },
  "suppressed_contacts": 9,
  "domains": [
    { "id": "…", "domain": "mail.acme.com", "status": "verified", "spf": "ok", "dkim": "ok", "dmarc": "ok", "dkim_rotation_due": false }
  ],
  "shared": {
    "sending_domain": "homingbox.net",
    "ip_reputation": {
      "status": "clean",
      "checked_at": "2026-09-25T09:00:00.000Z",
      "lists": [
        { "name": "Spamhaus ZEN", "status": "clean" },
        { "name": "Barracuda", "status": "clean" },
        { "name": "SpamCop", "status": "clean" },
        { "name": "UCEPROTECT L1", "status": "clean" }
      ]
    }
  }
}
FieldMeaning
ratesOutbound messages that left us (sent, delivered, bounced or complained) and the bounce and complaint rates, over the last 7 and 30 days.
suppressed_contactsYour contacts whose address is on the suppression list (hard bounce or complaint). Sends to them are refused.
domainsYour custom domains with the last check of their SPF, DKIM and DMARC records (ok, missing, mismatch).
shared.ip_reputation.statusclean, listed, partial (some lists could not be checked), unknown, stale (no check in 3 hours) or not_checked.
shared.ip_reputation.listsPer list: clean, listed, unknown (the list refused to answer or timed out; that says nothing about our IP) or skipped.

We check our IP once an hour on Spamhaus ZEN, Barracuda, SpamCop and UCEPROTECT Level 1, and we are alerted when it is listed or when bounce or complaint rates across all workspaces rise. The TypeScript SDK has mr.deliverability(), Python client.deliverability(), and MCP clients get_deliverability. For the suppression list: mr.suppressions.list() / mr.suppressions.remove(address, { confirm: true }), client.suppressions.list() / client.suppressions.remove(address, confirm=True), and the MCP tool list_suppressions (removing stays a person’s or your code’s decision).