Skip to content

Bounces, complaints and suppressions

Type Meaning Event Result
Hard (permanent) The address does not exist or the domain rejects mail permanently. email.bounced last_event becomes bounced; the address is added to your suppression list.
Soft (transient) A temporary problem such as a full mailbox or a server outage. SES keeps retrying. email.delivery_delayed Nothing is suppressed at first. After 3 soft bounces on different emails within 30 days, the address is added to your suppression list.

Hard bounces include bounce.type: "Permanent" and a bounce.subType in the webhook payload.

An address that keeps soft-bouncing is usually abandoned, and mailbox providers count repeated attempts against you. Soft bounces caused by the message rather than the address (message too large, content or attachment rejected) don’t count toward the limit; fix the message instead.

A complaint happens when a recipient marks your email as spam and their mailbox provider reports it back (a feedback loop). SpacePost raises email.complained, sets last_event to complained and adds the recipient to your suppression list. Complaints are the strongest negative signal there is: keep them below 0.1%.

The suppression list is a per-team do-not-send list. Before every send SpacePost checks all recipients (to, cc, bcc) against it:

  • Suppressed recipients are removed from the message and an email.suppressed event is raised.
  • If no recipient is left, nothing is sent and the email’s last_event is suppressed.
  • Quota is counted when the email is accepted, before the suppression check, so suppressed recipients still count toward your monthly quota.

Entries are added automatically for hard bounces, repeated soft bounces and complaints, or manually with the API. Each entry has a reason (bounce, complaint, unsubscribe or manual) and a human-readable detail, such as Hard bounce (NoEmail) or 3 soft bounces in 30 days (MailboxFull). The first reason recorded for an address is kept: adding an address manually never overwrites a bounce or complaint.

suppressions.ts
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost();
await spacepost.suppressions.create({ email: "do-not-mail@example.com" });
const { data } = await spacepost.suppressions.list({ limit: 100 });
for (const s of data?.data ?? []) console.log(s.email, s.reason);
await spacepost.suppressions.remove("do-not-mail@example.com"); // by email or id
Suppression
{
"object": "suppression",
"id": "0192e1c4-0000-7000-8000-0000000000d1",
"email": "nobody@example.com",
"reason": "bounce",
"detail": "Hard bounce (NoEmail)",
"created_at": "2026-10-23T14:02:12.000Z"
}

If a recipient’s domain does not accept mail at all (no MX or A records), SpacePost skips that recipient instead of attempting delivery, and raises email.suppressed. This protects your bounce rate. See Account verification and sending limits.

bounced@test.spacepost.dev, complained@test.spacepost.dev and (after three sends) softbounced@test.spacepost.dev create real suppression entries and count toward your rates. Remove them after testing. See Testing.