Kraiter
Guides

Delivery & Webhooks

Understand the email send lifecycle, SES webhook processing, bounce handling, and delivery monitoring.

When Kraiter sends an email, it goes through a lifecycle of states from queued to delivered (or bounced). Kraiter integrates with AWS SES webhooks to track every stage of delivery and automatically handle bounces, complaints, and other delivery events.

Send lifecycle

Every email sent through Kraiter follows this lifecycle:

queued → sent → delivered
                  ↘ bounced
                  ↘ complained

States

StateDescription
queuedThe email has been accepted by Kraiter and is waiting to be sent to SES
sentThe email has been submitted to SES for delivery
deliveredSES confirmed the recipient's mail server accepted the email
bouncedThe email bounced — the recipient's mail server rejected it
complainedThe recipient reported the email as spam

A send starts in the queued state when you call the send API or when a sequence step fires. It moves to sent once Kraiter submits it to SES, and then transitions to a final state based on SES feedback.

Checking send status

SDK
const send = await kraiter.sends.get('snd_abc123def456');

console.log(send.status);       // 'queued', 'sent', 'delivered', 'bounced', 'complained'
console.log(send.sentAt);       // When submitted to SES
console.log(send.deliveredAt);  // When delivered (if delivered)
cURL
curl https://api.kraiter.com/sends/snd_abc123def456 \
  -H "Authorization: Bearer YOUR_API_KEY"

SES webhook processing

Kraiter receives delivery notifications from AWS SES via SNS webhooks. When SES processes an email, it sends notifications for delivery, bounce, complaint, open, and click events. Kraiter processes these automatically.

What happens on each event

Delivery notification:

  • Send status updated to delivered
  • email.delivered system event created for the contact
  • Delivery timestamp recorded

Bounce notification:

  • Send status updated to bounced
  • email.bounced system event created
  • Bounce type and reason recorded
  • Contact may be suppressed (see bounce types below)

Complaint notification:

  • Send status updated to complained
  • email.complained system event created
  • Contact is automatically suppressed with reason complaint

Open notification:

  • email.opened system event created
  • Contact's derived properties updated (lastEmailOpenedAt, emailsOpenedCount)

Click notification:

  • email.clicked system event created
  • The clicked URL and timestamp are recorded
  • Contact's derived properties updated (lastEmailClickedAt, emailsClickedCount)

Bounce types

SES classifies bounces into two categories:

Hard bounces

A hard bounce means the email address is permanently undeliverable. Common reasons:

  • The email address does not exist
  • The domain does not exist
  • The mailbox has been disabled

Kraiter's response: The contact is automatically suppressed with reason bounce. No further emails will be sent to this address.

Soft bounces

A soft bounce means the delivery failed temporarily. Common reasons:

  • The recipient's mailbox is full
  • The receiving mail server is temporarily unavailable
  • The message is too large

Kraiter's response: Soft bounces are recorded but do not trigger suppression. SES automatically retries soft-bounced messages for up to 12 hours. If the retries fail, the bounce is upgraded to a hard bounce.

Bounce sub-types

Each bounce includes a sub-type with more detail:

Sub-typeCategoryDescription
UndeterminedHardCould not determine the specific reason
DoesNotExistHardEmail address does not exist
MessageTooLargeSoftMessage exceeds size limit
MailboxFullSoftRecipient's mailbox is full
ContentRejectedSoftContent was rejected by the mail server
GeneralSoftGeneral temporary failure

Complaint handling

When a recipient marks your email as spam in their email client (Gmail, Yahoo, Outlook, etc.), the ISP sends a complaint notification via the feedback loop. Kraiter processes these complaints automatically:

  1. The complaint notification arrives via SES webhook
  2. The contact is immediately suppressed with reason complaint
  3. All active sequence enrolments for the contact are cancelled
  4. An email.complained system event is created

Complaints are a serious signal. Even a small number of complaints can affect your sender reputation. Monitor your complaint rate and keep it below 0.1% of delivered emails.

Delivery notifications

You can list recent sends and filter by status to monitor delivery:

SDK
// Get recent bounces
const bounces = await kraiter.sends.list({
  status: 'bounced',
  limit: 50,
});

// Get recent complaints
const complaints = await kraiter.sends.list({
  status: 'complained',
  limit: 50,
});
cURL
curl "https://api.kraiter.com/sends?status=bounced&limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

Retry behaviour

Kraiter and SES handle retries at different levels:

SES-level retries

For soft bounces, SES automatically retries delivery for up to 12 hours. You do not need to manage this — it happens transparently.

Kraiter-level retries

If Kraiter cannot submit an email to SES (e.g. due to a temporary SES API error), it retries the submission with exponential backoff. The email remains in queued state during retries.

If all retries are exhausted, the send is marked as failed. This is rare and typically indicates a configuration issue or SES service disruption.

What is not retried

  • Hard bounces are never retried — the address is permanently undeliverable
  • Complaint-suppressed contacts are never retried
  • Emails blocked due to unsubscribe are not retried (the contact opted out)

Monitoring delivery health

Keep an eye on these key metrics to maintain good delivery health:

Bounce rate

Your bounce rate should stay below 5%. A higher rate signals list quality problems:

SDK
const metrics = await kraiter.metrics.tenant({
  period: 'daily',
  from: '2025-06-01',
  to: '2025-06-30',
});

for (const day of metrics.items) {
  const bounceRate = day.sent > 0 ? (day.bounced / day.sent * 100).toFixed(2) : '0.00';
  if (parseFloat(bounceRate) > 5) {
    console.warn(`High bounce rate on ${day.date}: ${bounceRate}%`);
  }
}

Complaint rate

Keep complaints below 0.1% of delivered emails. Above this threshold, ISPs may throttle or block your sends.

Domain health

Check your domain health status regularly:

SDK
const domains = await kraiter.domains.list();

for (const domain of domains.items) {
  if (domain.health !== 'healthy') {
    console.warn(`Domain ${domain.domain} health: ${domain.health}`);
  }
}

See the Domains guide for more on domain health monitoring.

Best practices

  • Clean your contact list regularly. Remove contacts who consistently bounce. Stale lists are the primary cause of high bounce rates.
  • Use double opt-in. Verify email addresses before adding contacts to your active list. This dramatically reduces bounces.
  • Monitor complaints daily. A spike in complaints needs immediate investigation. Check your recent sends for content or targeting issues.
  • Warm up new domains. When you start sending from a new domain, gradually increase volume over several weeks. Sudden high-volume sending from a new domain triggers spam filters.
  • Do not send to purchased lists. Purchased email lists have high bounce and complaint rates. Build your contact list organically.
  • Check send status for critical emails. For important transactional emails (password resets, account confirmations), check the send status and implement fallback mechanisms if delivery fails.