Email Bounce and Rejection Reasons
When an email cannot be delivered, the receiving mail server returns an SMTP error. Different providers surface these in different ways. Truncus passes on the bounce type that AWS SES reports and classifies each email, so your system can act on it.
Where a failure shows up
| Where | What you get |
|---|---|
POST /api/v1/emails/send_sync | The terminal state in the same request: bounced or complained (or delivered). blocked when the send was refused before any attempt. |
POST /api/v1/emails/send | Only what happened to the request: blocked or failed before or at hand-off. A bounce arrives later as an event. |
Webhook / event bounce | data.bounce_type (Permanent, Transient or Undetermined), data.bounce_subtype, data.recipients |
GET /api/v1/emails/{id} | intelligence.failure_reason, failure_category, is_retryable and recommended_action |
Failure reasons on the email record
failure_reason | When | recommended_action |
|---|---|---|
mailbox_not_found | Permanent bounce | remove_from_list |
mailbox_full | Transient or undetermined bounce (mailbox full, server busy) | retry_later |
spam_complaint | Recipient marked the message as spam | suppress_permanently |
suppressed | Address is on your suppression list | review_suppression_list |
blocked | Refused before sending | none |
send_failed | The email could not be sent | check_configuration |
cancelled | Cancelled before sending | none |
Permanent bounces and complaints add the address to your suppression list automatically. A later send to that address returns blocked with code ALL_RECIPIENTS_SUPPRESSED.
Common SMTP failures
| Failure | Typical SMTP code | Recommended action |
|---|---|---|
| Mailbox full | 552 | Retry later |
| Address does not exist | 550 | Remove address |
| Domain does not resolve | NXDOMAIN | Remove address |
| Rejected by spam filter | 554 | Check domain reputation |
| Domain policy rejection | 550 | Review sender config |
| Transient delivery issue | 451 | Retry later |
Which of these counts as a permanent or a transient bounce is decided by SES and reported in bounce_type and bounce_subtype. Act on those fields, not on the SMTP text.
Mailbox full
The recipient mailbox has exceeded its storage quota.
Typical SMTP response:
552 5.2.2 Mailbox full
This is a soft bounce — a temporary condition. SES reports it as a Transient bounce, and the email's failure_reason is mailbox_full. Retry after a delay. If it persists after several retries, treat the address as unreachable.
Address does not exist
The email address does not exist on the recipient server.
Typical SMTP response:
550 5.1.1 The email account that you tried to reach does not exist
This is a hard bounce: a Permanent bounce, failure_reason mailbox_not_found. Truncus adds the address to your suppression list. Remove it from your system as well. Continuing to send to non-existent addresses damages your domain reputation.
Domain does not resolve
The recipient domain has no valid MX record or is not resolvable in DNS.
Common causes: typo in the domain part of the email address, domain expired, domain deleted.
Treat as a hard bounce. Remove the address.
Rejected by spam filter
The message was rejected by the recipient server's spam filter.
Typical SMTP response:
554 5.7.1 Message rejected due to policy
Investigate your sending domain's reputation. Check your DKIM/SPF/DMARC configuration. Review message content for spam triggers.
Suppressed address
The address is on your suppression list — meaning it previously generated a hard bounce or spam complaint.
Truncus blocks sending to suppressed addresses to protect your domain reputation. Both send endpoints return:
{ "status": "blocked", "code": "ALL_RECIPIENTS_SUPPRESSED", "message_id": "msg_a2f7e8d1" }
Do not retry. Contact your recipient to verify their address if needed.
Transient delivery issue
The receiving server returned a transient error.
Typical SMTP response:
451 4.7.1 Try again later
This is a soft bounce. Retry after a delay.
Handling failures programmatically
With /emails/send, the send response only tells you whether the request was accepted. Bounces and complaints reach you as webhook events:
app.post('/truncus/webhook', async (req, res) => {
const event = req.body
if (event.event_type === 'bounce') {
if (event.data.bounce_type === 'Permanent') {
// hard bounce — Truncus has suppressed the address; remove it on your side too
await removeEmailAddress(event.email.to)
} else {
// soft bounce — schedule your own retry with a limit
await scheduleRetry(event.email.to)
}
}
if (event.event_type === 'complaint') {
await markAddressSuppressed(event.email.to)
}
res.status(200).send('ok')
})
A blocked send response means the address is suppressed or the domain cannot send this message: do not retry it. To get the bounce in the same request instead, call /emails/send_sync, which returns bounced or complained as the terminal state.
Bounce types
Hard bounce: permanent delivery failure. The address is invalid or permanently unreachable. Remove from your list immediately. Continuing to send to hard-bounced addresses degrades your sender reputation.
Soft bounce: temporary failure. The address is valid but temporarily unreachable. Retry after a delay, but set a retry limit.
bounce_type | Type | failure_reason |
|---|---|---|
Permanent | Hard | mailbox_not_found |
Transient | Soft | mailbox_full |
Undetermined | Soft | mailbox_full |