Skip to main content
Bounces occur when an email cannot be delivered to the recipient. Understanding different bounce types and handling them correctly is essential for maintaining good deliverability.

Bounce Types

Hard Bounces

Hard bounces are permanent delivery failures. The recipient’s mail server has definitively rejected the email, and retrying will not succeed. Common causes:
  • Email address doesn’t exist
  • Domain doesn’t exist
  • Recipient has blocked the sender
  • Mailbox has been deactivated
When a hard bounce occurs, you receive a message.bounce webhook event. Hard bounces use bounce classes in the 10–30 and 100 ranges:
[{
  "msys": {
    "message_event": {
      "type": "bounce",
      "timestamp": "1705312205",
      "transmission_id": "12345678",
      "message_id": "abcd-1234-efgh",
      "rcpt_to": "invalid@nonexistent.com",
      "bounce_class": "10",
      "error_code": "550",
      "reason": "User Unknown",
      "raw_reason": "550 5.1.1 The email account that you tried to reach does not exist",
      "friendly_from": "you@example.com",
      "subject": "Your Order Confirmation",
      "sending_ip": "192.168.1.1"
    }
  }
}]
Hard-bounced addresses are automatically suppressed and should be removed from your mailing lists. Continuing to send to these addresses damages your sender reputation.

Soft Bounces

Soft bounces are temporary delivery failures. The email might be deliverable if retried later. Common causes:
  • Mailbox is full
  • Server is temporarily unavailable
  • Message is too large
  • Rate limiting by the recipient server
Soft bounces use bounce classes in the 20–70 ranges:
[{
  "msys": {
    "message_event": {
      "type": "bounce",
      "timestamp": "1705312205",
      "transmission_id": "12345678",
      "message_id": "abcd-5678-efgh",
      "rcpt_to": "full@example.com",
      "bounce_class": "22",
      "error_code": "452",
      "reason": "Mailbox Full",
      "raw_reason": "452 4.2.2 Mailbox full",
      "friendly_from": "you@example.com",
      "subject": "Your Weekly Update",
      "sending_ip": "192.168.1.1"
    }
  }
}]
Lettr automatically retries soft bounces with exponential backoff. If delivery continues to fail, the address may eventually be suppressed.

Automatic Handling

Lettr automatically handles bounces for you:
1

Tracks Bounces

All bounces are logged and tracked in your dashboard with full details.
2

Suppresses Hard Bounces

Hard-bounced addresses are automatically added to your suppression list.
3

Retries Soft Bounces

Soft bounces are automatically retried with exponential backoff.
4

Sends Webhooks

Bounce events are sent to your configured webhook endpoints in real-time.

Bounce Classes

Lettr uses numeric bounce classes to categorize bounces. These correspond to the bounce_class field in webhook events:
Bounce ClassCategoryTypeDescriptionRecommended Action
1UndeterminedVariesBounce could not be classifiedReview manually
10Invalid RecipientHardEmail address does not existRemove from list
20Soft BounceSoftGeneral temporary failureAutomatic retry
21DNS FailureSoftDomain DNS lookup failed temporarilyAutomatic retry
22Mailbox FullSoftRecipient’s mailbox is over quotaWait and retry
25Admin FailureSoftMailbox admin-related issueReview and retry
30Generic Bounce (no RCPT)HardRejected without valid recipientRemove from list
40Generic BounceSoftTemporary generic failureAutomatic retry
50Mail Block (General)SoftBlocked by receiving serverInvestigate
51Mail Block (Spam Related)SoftBlocked as spam by receiving serverReview content
52Mail Block (Content)SoftBlocked due to content filteringReview content
60Auto-ReplyN/AAutomatic reply (vacation, etc.)No action needed
70Transient FailureSoftTemporary infrastructure issueAutomatic retry
100Relay DeniedHardRelay not permittedRemove from list
Hard bounce classes (10, 30, 100) result in automatic suppression. Soft bounce classes may lead to suppression if delivery continues to fail after retries.

Handling Bounces

Webhook Integration

The most reliable way to handle bounces is through webhooks. Set up a webhook endpoint to receive message.bounce events:
app.post('/webhooks/lettr', express.json(), async (req, res) => {
  res.sendStatus(200);

  for (const event of req.body) {
    const eventType = Object.keys(event.msys)[0];
    const data = event.msys[eventType];

    if (data.type === 'bounce') {
      const bounceClass = parseInt(data.bounce_class);

      // Log the bounce for analysis
      await logBounce({
        email: data.rcpt_to,
        bounceClass: bounceClass,
        errorCode: data.error_code,
        reason: data.raw_reason,
        timestamp: new Date(parseInt(data.timestamp) * 1000)
      });

      // Hard bounce classes: 10 (invalid recipient), 30 (no RCPT), 100 (relay denied)
      if ([10, 30, 100].includes(bounceClass)) {
        // Remove from mailing list immediately
        await db.subscribers.update({
          where: { email: data.rcpt_to },
          data: {
            status: 'bounced',
            bounceClass: bounceClass,
            lastBounceAt: new Date()
          }
        });

        // Notify relevant team members for spam-related blocks
        if ([50, 51, 52].includes(bounceClass)) {
          await notifyTeam({
            type: 'reputation_concern',
            email: data.rcpt_to,
            reason: data.raw_reason
          });
        }
      }
    }
  }
});

Querying Email Events

You can retrieve events for specific emails using the API:
curl "https://app.lettr.com/api/emails/12345678" \
  -H "Authorization: Bearer lttr_YOUR_API_KEY"
The response includes all events for that email, including bounces:
{
  "message": "Email retrieved successfully.",
  "data": {
    "results": [
      {
        "event_id": "evt-abc-123",
        "type": "injection",
        "timestamp": "2024-01-15T10:30:00.000Z",
        "request_id": "12345678",
        "rcpt_to": "recipient@example.com",
        "subject": "Your Order Confirmation",
        "friendly_from": "you@example.com"
      },
      {
        "event_id": "evt-def-456",
        "type": "bounce",
        "timestamp": "2024-01-15T10:30:05.000Z",
        "request_id": "12345678",
        "rcpt_to": "recipient@example.com",
        "reason": "User Unknown",
        "raw_reason": "550 5.1.1 The email account that you tried to reach does not exist",
        "error_code": "550"
      }
    ],
    "total_count": 2
  }
}

Bounce Rate Guidelines

Monitor your bounce rates to maintain good deliverability:
Bounce RateStatusAction
< 2%HealthyMaintain current practices
2-5%WarningReview list hygiene
> 5%CriticalClean list immediately
ISPs closely monitor bounce rates. A bounce rate consistently above 5% can result in your emails being blocked or filtered to spam.

Preventing Bounces

Require subscribers to confirm their email address before adding them to your list. This ensures addresses are valid and owned by the person who signed up.
Use email validation at the point of collection. Check for common typos, invalid formats, and disposable email addresses.
Remove subscribers who haven’t engaged in 6-12 months. Inactive addresses are more likely to bounce over time.
Set up webhook handlers to process bounce events in real-time. Don’t wait for batch processing.
Track open and click rates. Low engagement often precedes bounces as users abandon email addresses.

Delayed Delivery

Sometimes emails are temporarily delayed rather than bounced. These are tracked as message.delay events:
[{
  "msys": {
    "message_event": {
      "type": "delay",
      "timestamp": "1705312205",
      "transmission_id": "12345678",
      "message_id": "abcd-1234-efgh",
      "rcpt_to": "recipient@example.com",
      "reason": "421 Server temporarily unavailable",
      "friendly_from": "you@example.com",
      "subject": "Your Order Confirmation",
      "sending_ip": "192.168.1.1",
      "num_retries": "1"
    }
  }
}]
Delayed emails are automatically retried. You don’t need to take action unless delays persist, which may indicate a delivery issue.

Out-of-Band Bounces

Some bounces arrive asynchronously after the initial delivery attempt appears successful. These are called out-of-band (OOB) bounces and are tracked as message.out_of_band events:
[{
  "msys": {
    "message_event": {
      "type": "out_of_band",
      "timestamp": "1705314000",
      "transmission_id": "12345678",
      "message_id": "abcd-1234-efgh",
      "rcpt_to": "recipient@example.com",
      "bounce_class": "22",
      "reason": "Mailbox Full",
      "raw_reason": "552 5.2.2 Mailbox full",
      "error_code": "552"
    }
  }
}]
Handle OOB bounces the same way as regular bounces — update your records and suppress hard bounces.