Get Hedhog updates in your inbox
New releases, fresh recipes, and breaking changes — no spam.
Campaign Submodule
suppression
Global, campaign-independent suppression list that CampaignDispatchService consults to skip emails it must not send to.
Source path: libraries/campaign/src/services/campaign-suppression.service.ts
Module file: campaign.module.ts
Introduction
The Suppression submodule maintains `campaign_suppression`, a single global list of email addresses that must never receive campaign mail, regardless of which recipient list they appear on. Every campaign send performs a bulk suppression check (`CampaignSuppressionService.bulkCheck`) against the full audience before creating message rows, so a suppressed address is skipped at enqueue time, not silently dropped later — the corresponding `campaign_message` row is created with `status: skipped` so the campaign's totals stay accurate.
Each suppression entry carries a `reason` — `manual`, `bounce`, `complaint`, `unsubscribe`, or `invalid` — plus a free-text `note`, an optional `source` string, and optional `campaign_id`/`message_id` back-references to the send that triggered it. Adding an entry has a side effect beyond the suppression table itself: `CampaignSuppressionService.create` also updates any matching `campaign_recipient` rows to `status: bounced` (when `reason` is "bounce") or `status: suppressed` (for every other reason), keeping the recipient-level status consistent with the suppression-level record. The unsubscribe flow (in the Unsubscribes submodule) and any future bounce/complaint webhook handler both funnel through this same `create` method.
Removing a suppression entry — whether by bulk delete or by single-ID delete — is the literal inverse: it deletes the suppression row(s) and resets the corresponding `campaign_recipient` rows back to `status: active`, effectively re-admitting that address into future sends. There is no separate "reactivate" endpoint; deleting the suppression record is how an operator manually un-suppresses an address.
`GET /campaign/suppression/stats` returns a reason-by-reason breakdown (total, plus counts for unsubscribe/bounce/complaint/manual/invalid), which the dashboard surfaces to give operators visibility into why their suppression list is growing — a spike in `bounce` typically signals a deliverability problem distinct from a spike in `unsubscribe`.
HTTP Endpoints
5 endpoints
Suppression list statistics broken down by reason (bounce, complaint, unsubscribe, manual, invalid) plus a total count.
Paginated list of suppressed email addresses.
- Query
- page, pageSize, search, reason, source
Add an email to the suppression list. Also marks any matching recipient records as suppressed (or bounced, if reason is "bounce").
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| string | yes | Email address to suppress | |
| reason | "manual" | "bounce" | "complaint" | "unsubscribe" | "invalid" | no | Default: "manual" |
| source | string | no | Default: "manual" |
| note | string | no | Optional free-text note |
| campaign_id | number | no | Associated campaign ID |
| message_id | number | no | Associated message ID |
Bulk delete suppression records. Also re-activates matching recipient records (status set back to "active").
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| ids | number[] | yes | IDs to delete |
Delete a single suppression record. Also re-activates the corresponding recipient.
- Params
- id (int)