13 · Marketing
The Marketing module sends bulk email campaigns to contacts pulled from the CRM. Each campaign is a one-off HTML email sent to either all contacts or contacts filtered by status.
Where to find it
Sidebar → Marketing or /marketing. Requires the marketing
feature and an administrator role.
Concepts
- Campaign — an email message (subject + HTML/plain-text body) plus an audience. A campaign is sent once.
- Audience — chosen at campaign level, not saved separately. Either:
- All contacts — every CRM contact with an email address that has not opted out.
- By status — contacts whose CRM status is one of a set you pick.
There is no saved-segment, saved-list, or template concept — each campaign carries its own body and audience filter.
Layout
+----------+----------------------------------------------+
| Filters | Campaigns [+ New] |
| [All ] |---------------------------------------------|
| [Draft ] | April newsletter Sent 342 78% open |
| [Sending]| Q2 product launch Draft |
| [Sent ] | |
+----------+----------------------------------------------+
📷 Screenshot placeholder: screenshots/marketing-campaigns.png
The list page shows KPI cards (total, drafts, sending, sent with average open rate) and a status-filter pill group.
Creating a campaign
- + New campaign.
- Basics — campaign name, from name, from email, subject. Name, from email and subject are required.
- Body — write an HTML body and a plain text body directly
in the two text areas. Liquid-style placeholders such as
{{firstName}}are noted in the UI for per-recipient merge. - Recipients — pick All contacts or By status (then tick one or more contact statuses).
- Save draft. The campaign is created in
DRAFTstatus.
Campaigns are launched separately from the detail page — saving only creates a draft.
Campaign detail & launch
Open a campaign to see its status badge, audience label, KPI cards (recipients, delivered, failed, opens, open rate), the details block, and a rendered email preview.
While the campaign is in DRAFT you can:
- Edit — re-open the form. Only draft campaigns are editable; editing a launched campaign returns a 409.
- Launch campaign — builds the send list and queues delivery.
- Delete — removes the campaign.
Once SENDING, only a Refresh button is available to poll progress.
How a launch works
On launch the service:
- Queries CRM contacts in the org with an email address and
marketingOptOut = false, applying the status filter if set. - Creates one
MarketingSendrow per eligible contact, each with its own unsubscribe token, and moves the campaign toSENDING. - Queues the sends in batches of 100 to a background worker.
- The worker emails each contact, increments delivered/failed counts,
and flips the campaign to
SENTwhen no queued sends remain.
If no contacts are eligible the campaign goes straight to SENT with a
recipient count of 0.
Tracking
Each campaign records:
- Recipients (total send list size)
- Delivered / Failed counts
- Opens — via a 1×1 tracking pixel injected into the HTML body
- Open rate — opens ÷ delivered
Tracking is campaign-level counters only. There is no per-contact event log, no click tracking, and no bounce hard/soft split. There is no CSV export from the campaign report.
Unsubscribes
Every campaign email has an Unsubscribe link appended to both the
HTML and plain-text bodies (HTTP link only — there is no mailto:
variant). Clicking it sets marketingOptOut = true on the contact's
CRM record and shows a confirmation page.
⚠️ Caution. Opt-out is global — an opted-out contact is excluded from every future campaign. There is no in-product re-subscribe; the flag must be cleared on the CRM contact record.
Sending email
Campaign email is delivered through the shared email service. There is no in-product sender-domain verification screen — the from name and from email are free-text fields on the campaign. Deliverability (SPF/DKIM/DMARC) is handled at the mail-server / integration level.
Scheduling
There is no in-product scheduling or recurring/drip send. A draft is
sent immediately when you click Launch campaign. (The data model
carries a scheduledAt field, but the UI and launch path do not act on
it.)
Permissions
The whole Marketing UI is wrapped in the admin guard.
| Action | Role |
|---|---|
| View / create / launch / delete campaigns | ADMIN or SUPER_ADMIN |
The marketing service itself only checks for a valid access token; the admin restriction is enforced by the portal UI guard.
Common workflows
One-off announcement
- + New campaign → fill in basics and body.
- Recipients → All contacts (or By status).
- Save draft → open the campaign → Launch campaign.
Target a customer segment
- Create the campaign, set Recipients → By status, and tick the CRM statuses you want (statuses come from CRM settings).
See also
- CRM — campaign recipients come from CRM contacts
- Admin → Email settings