Logotipo Hedhog

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

FieldTypeRequiredNotes
namestringyes
channel"email"noDefault: "email"
subjectstringnoEmail subject line
content_htmlstringnoHTML email body
content_textstringnoPlain-text fallback body
template_idnumbernoUse a saved template for content
recipient_list_idnumbernoDefault recipient list
scheduled_atstring (ISO 8601)noSchedule send time
integration_profile_idnumbernoMail delivery profile (e.g. AWS SES)
metadataobjectnoArbitrary metadata

Bulk delete campaigns. Only campaigns in draft, canceled, or failed status can be deleted.

Body

FieldTypeRequiredNotes
idsnumber[]yesIDs 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

FieldTypeRequiredNotes
namestringno
channel"email"no
subjectstringno
content_htmlstringno
content_textstringno
template_idnumberno
recipient_list_idnumberno
scheduled_atstring (ISO 8601)no
integration_profile_idnumberno
metadataobjectno

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

FieldTypeRequiredNotes
scheduled_atstring (ISO 8601)yesTarget 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

FieldTypeRequiredNotes
emailstringyesTest recipient email address
namestringnoTest 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)