Skip to content
BuildBase

Email deliverability and sending health

The bounce and complaint limits that pause a campaign, the never-email lists, bounce intake for every sender, and why sign-in mail keeps flowing.

Deliverability is whether your email reaches the inbox at all. Gmail, Yahoo and the other mailbox providers judge you mostly on two numbers. One is how much of your mail bounces (comes back undelivered). The other is how much is marked as spam, which is called a complaint.

By the time Gmail or Yahoo acts on those numbers, every message from your domain suffers. So BuildBase watches both as mail goes out, and stops marketing mail first. The mail people are waiting on to sign in is kept out of every one of these stops.

# Is marketing mail blocked, and how much transactional mail went out today?
curl https://api.console.buildbase.app/api/emails/health \
  -H "Authorization: $BUILDBASE_TOKEN"
{
  "marketingBlocked": false,
  "blockedAt": null,
  "reason": null,
  "transactionalToday": 182,
  "authCodesToday": 40,
  "authCodeDayMultiplier": 10
}

Before you start

BuildBase needs to hear about your bounces. Mailgun and Amazon SES tell it with a signed webhook (a message from the provider that BuildBase can check really came from it). For SMTP and Gmail senders, BuildBase reads them from the sender's own mailbox instead (see Bounce intake). Set this up under Messaging > Sender accounts.

Bounce and complaint limits

There are two levels. Each needs both a rate and a minimum count, so the first bounce of a 40-person send is not "2.5% bounced, stop everything".

LevelBounce rateComplaint rateWhat happens
Pauseover 2%, at least 5over 0.08%, at least 2This campaign pauses, and its console page says why
Blockover 5%, at least 10over 0.3%, at least 3All of the organization's marketing mail stops

The pause level is checked for each campaign, before every batch. The block level covers all of the organization's marketing mail over the last seven days, and is checked each time a bounce or complaint comes in. Complaints are checked first, because they weigh more.

A paused campaign resumes when a person resumes it, after looking at its list. An owner or admin lifts a block with Lift block on Messaging > Campaigns, or with POST /api/emails/health/unblock. Lifting it resumes nothing: every campaign the block paused stays paused until someone resumes it.

Sign-in and transactional mail are never blocked here. A sign-in code is not the mail that drew the complaints, and refusing it would lock people out.

Bounce intake

How bounces get back to BuildBase depends on the sender:

SenderHow bounces come back
MailgunA webhook signed with your Mailgun signing key, at /api/webhooks/email/:orgId/mailgun
Amazon SESYour SNS topic (Amazon's notification service), at /api/webhooks/email/:orgId/ses
SMTP and GmailThe bounce mailbox, read every ten minutes

An SMTP server or Gmail doesn't call a webhook. It mails a bounce notice back to the address the message came from. To catch those, turn on bounce tracking in the sender's edit dialog:

  • For an SMTP sender, give the IMAP login of the mailbox that receives its bounces (port 993 by default). IMAP is how mail apps read a mailbox.
  • For a Gmail sender, let it read its own inbox.

BuildBase is careful in that mailbox. It reads only recent mail from a mail system (mailer-daemon, postmaster). It believes only a standard delivery status report that names a message it sent for your organization. It marks a matched notice read and touches nothing else. A notice without the message's tracking code is ignored, so nobody can forge a bounce to get an address suppressed.

A hard bounce (an address that does not exist) or a complaint is marked on the message's history row. The contact is flagged so no campaign mails it again, and any workflow listening for that event starts.

Your never-email list

Every organization has its own suppression list: addresses BuildBase will not send to for you. It catches two things a contact flag cannot: an address that is not a contact (a user, or a one-off send), and a spam trap. A spam trap is an address a mailbox provider or blocklist runs only to catch senders with bad lists. Once listed, it stays off every list for good, even if it is imported again.

The email API refuses a suppressed address up front with RECIPIENT_SUPPRESSED, instead of skipping it silently.

The shared never-email list

A dead mailbox or a spam trap hurts whichever sender mails it next. So when one organization's provider reports one, the shared list lets every other organization on the same BuildBase server skip it too. The rules:

  • Only hard bounces and spam traps go on it. Unsubscribes and complaints never do.
  • Only what a provider reported counts (a signed webhook or a bounce mailbox). An address someone marked by hand never does.
  • A bounce or a spam trap counts only once two different paying billing accounts have reported it, so one customer cannot list an address, even with two organizations.
  • Each address is stored as a keyed hash (a one-way scramble of it) under the shared list's own key, which never matches a row of your own list.
  • A skip gives one neutral reason, Address not deliverable, the same for a bounce and a trap.

An undeliverable entry lasts 180 days and a trap 365. A skip it causes counts as a bounce in your own sending health. So testing addresses against the list costs you what mailing them would.

Note

The shared list is built, and off by default. BuildBase turns it on for a server once its data processing terms cover it. Until then, only your own list and your contacts' flags apply.

DMARC

DMARC is a DNS record that tells mailbox providers what to do with mail that claims to come from your domain but fails their checks. Gmail and Yahoo require one from bulk senders, even p=none, a policy that blocks nothing.

Verifying a sending domain looks for it at _dmarc.<domain>. When a subdomain has none, it walks up to the main domain. The result shows as one more DNS row: the policy found, or the record to publish, v=DMARC1; p=none;. Connect the domain to Gmail Postmaster to see Gmail's own SPF, DKIM and DMARC results for it. (SPF and DKIM are the two other DNS checks that prove mail really came from you.)

Sign-in mail is never held up

People are sitting on the sign-in screen waiting for these, so they are handled differently from everything else.

  • Untracked. Sign-in links and codes, verification, resets, changes of address, security notices and invitations go out with no open-tracking image and no rewritten links. A live sign-in token never passes through a redirect or a log.
  • Never behind a campaign. Sending is paced per organization and per sender, so one large campaign cannot take over the mail queue everyone shares. A message over its pace is delayed a few seconds, never dropped. Sign-in mail skips the pacing.
  • Its own day. Codes a person is waiting on get their own daily allowance, ten times your plan's transactional limit. A per-recipient hourly cap stops anyone using your sign-up form to flood one inbox.

When BuildBase suspends sending

Platform staff can suspend an organization's sending, for example while abuse is investigated. A suspension has two levels:

LevelStopsKeeps flowing
bulkCampaigns, workflow and custom mail, the email API, every web push, sender and SMS testsSign-in codes, magic links, resets, verification and invitations, and sign-in texts
allEverything, sign-in mail includedNothing

A console action answers 403 with SENDING_SUSPENDED, and so does the email API. A campaign message caught by a suspension is held, never skipped, and goes out once sending is restored. A queued push or a workflow step is skipped with the reason and never retried.

Next Steps