Bounces, complaints and suppressions
Bounces
Section titled “Bounces”| 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.
Complaints
Section titled “Complaints”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
Section titled “The suppression list”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.suppressedevent is raised. - If no recipient is left, nothing is sent and the email’s
last_eventissuppressed. - 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.
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# listcurl https://api.spacepost.dev/suppressions?limit=100 -H "Authorization: Bearer $SPACEPOST_API_KEY"
# addcurl -X POST https://api.spacepost.dev/suppressions \ -H "Authorization: Bearer $SPACEPOST_API_KEY" -H "Content-Type: application/json" \ -d '{ "email": "do-not-mail@example.com" }'
# remove, by email or idcurl -X DELETE https://api.spacepost.dev/suppressions/do-not-mail@example.com -H "Authorization: Bearer $SPACEPOST_API_KEY"{ "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"}Unreachable recipient domains
Section titled “Unreachable recipient domains”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.
Using test addresses
Section titled “Using test addresses”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.