Skip to content

Soft-bounce threshold alerts

runSoftBounceSweep in src/lib/soft-bounce-sweep.ts. Runs every 15 minutes (part of the */15 * * * * cron).

Why

A single temporary failure (event=failed, severity=temporary) is usually a transient SMTP hiccup. But ≥3 to the same recipient in 24h signals a real problem (full mailbox, forwarding loop, domain block). We want to surface that early.

Query shape

SELECT recipient,
COUNT(*) AS count,
(subquery for most-recent reason) AS last_reason
FROM mailgun_events
WHERE event = 'failed'
AND severity = 'temporary'
AND received_at >= ?
AND recipient IS NOT NULL
GROUP BY recipient
HAVING COUNT(*) >= 3

What it sends

For each offending recipient:

SMS to +19524518482 (Bill):

Soft-bounce threshold hit: {recipient} 3 temp failures in last 24h. Last reason: {reason}

Dedup

Uses alert_log (migration 0025):

INSERT INTO alert_log (alert_key, detail) VALUES
('soft-bounce:{recipient}', 'count=X, reason=Y')

Before sending, checks if a row with alert_key=soft-bounce:{recipient} exists in the last 24h. If yes → skip. One alert per recipient per 24h max.

Source

  • src/lib/soft-bounce-sweep.ts
  • migrations/0025_alert_log.sql
  • Tests: test/lib/soft-bounce-sweep.test.ts (6 cases)