Skip to content

Email — Webhook Ingestion

Postmark delivers per-event webhooks to the per-tenant endpoint installed at provisioning time. EmailWebhookIngestionService records each event and projects delivery status back onto the originating EmailJob.

Inbound callbacks carry Authorization: Bearer <ARDA_API_KEY> (validated by the component’s default authenticator) plus the per-configuration opaque webhookRoutingToken (DQ-017) that routes the event to the right configuration. Provisioning installs both on the Postmark webhook.

The persisted EmailJob row’s status column advances monotonically toward more concerning as events arrive:

  • DeliveryDELIVERED.
  • Bounce (hard) → BOUNCED + a SuppressionEntry for the recipient.
  • Spam complaintCOMPLAINED + a SuppressionEntry.
  • Open / click → recorded as EmailEvents without advancing job status (engagement signals, not delivery state).

An event that would imply a less-concerning state than the row currently holds is treated as out-of-order: the row is marked INCONSISTENT rather than dropped, and a replay (same idempotency key) against an INCONSISTENT job returns 412 so an operator triages before any re-send.

Webhook status reconciliation surfaces infrastructure failures to Postmark (for redelivery) rather than silently diverging job status.

SuppressionEntry rows are tenant-scoped. A live suppression causes the send-time suppression gate to reject with 403 before any Postmark call (see Provisioning & sending). Suppressions are created from hard bounces, spam complaints, and Postmark-console subscription-change events; the recipient key is canonicalized (trim + lowercase) the same way as the persisted recipient, so a suppressed address cannot diverge from the stored one.