Get Hedhog updates in your inbox
New releases, fresh recipes, and breaking changes — no spam.
Campaign Submodule
campaigns
Core campaign CRUD plus lifecycle actions: schedule, start, pause, resume, cancel, and duplicate.
Source path: libraries/campaign/src/services/campaign.service.ts, campaign-dispatch.service.ts, campaign-dashboard.service.ts, campaign-event.service.ts
Module file: campaign.module.ts
Introduction
The Campaigns submodule owns the `campaign` record itself: its content (subject, HTML/plain-text body, optional template reference), its target recipient list, its delivery profile, and its lifecycle state machine. A campaign is created in `draft` status and moves through `scheduled` → `sending` → `completed` (or `paused`, `canceled`, `failed`) as CampaignDispatchService drives it forward. Only campaigns in `draft`, `canceled`, or `failed` status can be deleted, which protects historical delivery data for anything that has actually gone out.
Starting a campaign (`POST /campaign/:id/start`, aliased at `/send`) is the most involved operation in the module. CampaignDispatchService first validates that the campaign has a mail delivery profile (an Integration Profile), a recipient list, and non-empty content, and — when the `campaign.unsubscribe.required` setting is enabled — that the content includes the `{{unsubscribeUrl}}` placeholder. It then pulls the recipient list, deduplicates by normalized email, and cross-references the global suppression list before ever touching the database in a write transaction. Inside a single transaction it upserts one `campaign_message` row per eligible recipient (skipping suppressed/unsubscribed/bounced ones as `skipped`, and reusing rows that already exist to avoid duplicating in-flight sends on resume), then updates the campaign counters and flips status to `sending`. Only after the transaction commits does it enqueue one queue job per pending message via `CampaignEmailWorkerService` — or, if `campaign.queue.enabled` is turned off, send every message inline and synchronously within the same request, reporting per-recipient progress through a Notification record.
Pausing and resuming only touch the campaign status; the underlying messages and their queue jobs are left alone, so a resumed campaign continues from wherever the in-flight queue left off rather than re-enqueuing already-sent messages. Canceling sets `status: canceled`, which the worker reads at send time to skip remaining messages without deleting them. Duplicate creates a new draft from an existing campaign's content and configuration, explicitly resetting every delivery counter, timestamp, and the schedule so the copy starts clean.
The global dashboard (`GET /campaign/dashboard`) and per-campaign dashboard (`GET /campaign/:id/dashboard`) are read-only aggregation endpoints backed by CampaignDashboardService: status counts across all campaigns, messages sent today, pending/queued backlog, 24h failure counts, suppression/unsubscribe totals, and a day-bucketed send history for the last 7/30 days, plus rate-limit context (daily cap and remaining capacity) sourced from CampaignRateLimitService. `GET /campaign/:id/status` is a lighter combination of the raw campaign record with aggregated message-status counts, useful for polling without pulling the full dashboard payload. Every state transition also writes an audit row via CampaignEventService, exposed at `GET /campaign/:id/events`.
HTTP Endpoints
18 endpoints
Paginated list of campaigns. Filter by status (draft, scheduled, sending, paused, completed, canceled, failed) and channel.
- Query
- page, pageSize, search, status, channel
Create a new campaign in draft status.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | yes | — |
| channel | "email" | no | Default: "email" |
| subject | string | no | Email subject line |
| content_html | string | no | HTML email body |
| content_text | string | no | Plain-text fallback body |
| template_id | number | no | Use a saved template for content |
| recipient_list_id | number | no | Default recipient list |
| scheduled_at | string (ISO 8601) | no | Schedule send time |
| integration_profile_id | number | no | Mail delivery profile (e.g. AWS SES) |
| metadata | object | no | Arbitrary metadata |
Bulk delete campaigns. Only campaigns in draft, canceled, or failed status can be deleted.
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| ids | number[] | yes | IDs to delete |
Get a campaign by ID, including its linked integration profile details.
- Params
- id (int)
Update campaign fields. Only provided fields are changed.
- Params
- id (int)
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | no | — |
| channel | "email" | no | — |
| subject | string | no | — |
| content_html | string | no | — |
| content_text | string | no | — |
| template_id | number | no | — |
| recipient_list_id | number | no | — |
| scheduled_at | string (ISO 8601) | no | — |
| integration_profile_id | number | no | — |
| metadata | object | no | — |
Delete a single campaign. Must be in draft, canceled, or failed status.
- Params
- id (int)
Global campaign dashboard with aggregate delivery statistics across all campaigns, including a 7/30-day send history and rate-limit context.
Per-campaign dashboard with delivery counters, send progress percentage, recent lifecycle events, recent delivery errors, and rate-limit context.
- Params
- id (int)
Get campaign record combined with aggregated message statistics, grouped by message status.
- Params
- id (int)
Audit log of lifecycle events for a campaign (created, scheduled, queued, sending_started, sent, failed, completed, canceled, paused, resumed, open, click, unsubscribed, test_sent, etc.).
- Params
- id (int)
- Query
- page, pageSize, event_type
Schedule a campaign for future delivery. Sets status to "scheduled".
- Params
- id (int)
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| scheduled_at | string (ISO 8601) | yes | Target dispatch date/time |
Immediately dispatch a campaign. Validates content/profile/list, deduplicates and filters suppressed recipients, creates pending campaign_message rows in a transaction, then enqueues one delivery job per message (or sends inline if the queue is disabled).
- Params
- id (int)
Alias for /start. Immediately dispatch the campaign.
- Params
- id (int)
Enqueue a single test email for this campaign to a specified address. Does not create a campaign_message row or affect campaign counters.
- Params
- id (int)
Body
| Field | Type | Required | Notes |
|---|---|---|---|
| string | yes | Test recipient email address | |
| name | string | no | Test recipient display name |
Cancel a campaign. Sets status to "canceled"; the email worker skips any messages still in flight for this campaign.
- Params
- id (int)
Pause a sending campaign. Sets status to "paused" without altering in-flight messages or queue jobs.
- Params
- id (int)
Resume a paused campaign. Sets status back to "sending"; calling /start afterward continues from where the queue left off.
- Params
- id (int)
Duplicate a campaign into a new draft with "(copy)" appended to the name. Delivery counters, timestamps, and schedule are reset.
- Params
- id (int)