Email & SMS Campaigns (EDM)
OmniCRM's bulk-campaigns module (EDM — Electronic Direct Mail) lets operators build audience segments, design templates, and send bulk campaigns to customers over two channels: email (via SMTP) and SMS (via OmniMessage). A campaign targets one segment with one template on a chosen channel, with per-recipient personalisation, opt-out handling and tracking. This guide covers both channels.
See also: Mailjet <integrations_mailjet> for the transactional Mailjet integration, Notification API <integrations_notifications> for system notifications, Configuration and Customization <administration_configuration> for general config.
Scope note: This describes the bulk-campaign send path only (email and SMS). Transactional email (welcome, invoices, password resets, eSIM delivery, ticket updates) is delivered separately through the Mailjet REST API and is not affected by the settings below.
Overview
The module has these building blocks:
| Concept | Description |
|---|---|
| Segment | A targeted audience, defined by filter criteria (billing type, contact type, status, balance, …) or a static address list. |
| Template | Reusable content for a channel — an operator-uploaded HTML body (email) or a short SMS message, both with per-recipient personalisation. |
| Campaign | A send: one template to one segment on a chosen channel, with per-recipient tracking. |
| Channel | How a campaign is delivered — email (SMTP relay) or SMS (OmniMessage). |
Channels
A campaign's channel decides everything downstream — delivery transport, template format, and which opt-out applies:
| Channel | Delivered via | Personalisation | Opt-out | Details |
|---|---|---|---|---|
| SMTP relay (Mailjet, Mailgun, SES, …) | Jinja2 HTML templates | email category prefs + unsubscribe + suppression list | Email delivery | |
| SMS | OmniMessage | var1–var4 substitution | sms category prefs + OmniMessage STOP | SMS delivery (OmniMessage) |
Email templates and personalisation
Templates store raw HTML that you upload or paste into the template editor. Personalisation uses Jinja2 syntax, so templates can contain variables and simple logic:
{{ first_name }},{{ last_name }},{{ customer_name }},{{ email }}- conditionals and loops, e.g.
{% if first_name %}Hi {{ first_name }}{% else %}Hello{% endif %}
Any additional field provided by the segment for a recipient is also available to the template by name. Each recipient's email is rendered individually before sending.
Using bulk campaigns
This walkthrough covers building a segment, creating an email or SMS campaign, sending and tracking it, plus what recipients see. Everything lives under the Marketing section of the sidebar (Segments, Templates, Campaigns). (Operator-facing; written from the current implementation.)
Build a segment
A segment is the audience a campaign targets. From Marketing → Segments you can search, refresh and create segments. Each row shows the segment's criteria, current recipient count and when it was last refreshed; click a row to open it.
The Segments list. Recipient counts refresh on demand; clicking a row opens the segment.
Create Segment (or clicking a row to edit) opens the segment editor, where you define the audience from customer attributes — Customers without services, Zero balance customers, Billing Types, Contact Types and Customer Status. Save the segment first, then Generate Segment calculates the recipient list and Export CSV downloads it.
The segment editor: criteria on the left, a live recipient count and CSV export at the bottom.
Segments are channel-agnostic — the same segment can be used for an email or an SMS campaign. Email campaigns target contacts with an email address; SMS campaigns target contacts with a phone number.
Upload a template
Email campaigns send an HTML template. From Marketing → Templates, create or edit a template and use Upload .html to load an HTML file straight into the body (or paste it). Templates support Jinja2 personalisation, and the Available Variables panel inserts {{customer_name}}, {{contact_firstname}}, {{contact_lastname}} and {{contact_email}} at the cursor.

SMS campaigns don't use a template — you write the message inline when composing the campaign.
Create a campaign
From Marketing → Campaigns → Create Campaign, a short wizard guides you through the campaign. The Channel toggle (Email / SMS) at the top of the wizard drives which steps and fields appear.
-
Select Segment — choose the audience. The selected segment shows its recipient count.
Step 1: pick the segment whose contacts will receive the campaign. The channel toggle sits above the steps. -
Template / Message — for an email campaign, choose an HTML template (click a template card to select it; Preview shows the rendered HTML). For an SMS campaign this step is skipped — the wizard shows a notice and you write the message in the next step.
For SMS, the template step is replaced by a notice; you continue to write the message. -
Campaign Details — the channel-specific configuration:
- Category — the communication category the campaign belongs to (loaded from
edm.communication_categories); recipients opted out of this category for this channel are excluded. - Email: Campaign Name, Subject Line, From Email, From Name.
- SMS: Campaign Name, Sender ID and the SMS Message (personalise with
{{ first_name }},{{ last_name }},{{ customer_name }}).
Email campaign details: subject and sender fields, plus the category selector.
SMS campaign details: the form switches to Sender ID and an SMS Message body. - Category — the communication category the campaign belongs to (loaded from
-
Review & Save — confirm channel, category, segment and content, then save as a draft. View recipients lists the segment's contacts (name, email or phone, customer) before you commit.
Review for an email campaign.
Review for an SMS campaign, with the View recipients link and the save-as-draft note.
The campaign is always saved as a draft first — you send it from the campaign detail page (below).
Campaigns list
Marketing → Campaigns lists all campaigns with their Channel (Email / SMS), status, recipient count and — once sent — open and click rates. Filter by All / Draft / Sent, and click any row to open the campaign.
The Campaigns list. The Channel column distinguishes email from SMS at a glance; sent campaigns show their open and click rates inline.
Send and track a campaign
Click a campaign to open its detail page:
- A draft shows a Send Campaign button, enabled once the campaign has a segment and — for email, a template; for SMS, a message. If something is missing, the page lists what's required.
- Once sent, the detail page shows:
- Campaign Statistics — email: delivered, open rate, click rate, opened/clicked/bounced; SMS: delivered / failed, with a Sync button that pulls the latest delivery counts from OmniMessage.
- Recipients — a per-recipient list with status (
sent,delivered,opened,clicked,bounced,failed). The address column shows the email address (email) or phone number (SMS).
The campaign detail page for a draft SMS campaign, ready to send. Once sent, this page shows delivery/engagement stats and per-recipient status; for SMS, Sync refreshes counts from OmniMessage.
Once sent, the same page surfaces the Campaign Statistics panel and the per-recipient list:
A sent email campaign: delivered count, open/click rate and opened/clicked/bounced totals, alongside the per-recipient list and each recipient's status.
Because tracking is provider-agnostic, Delivered is derived from engagement — an open or click proves the message arrived. Open rate is therefore measured against delivered (engaged) recipients rather than total sent.
What recipients see (unsubscribe)
Every email carries an unsubscribe link that opens a preference centre: the contact's email categories with their current opt-in state, a Save preferences action, and an Unsubscribe from all email option. A mail client's one-click unsubscribe opts the contact out of just that campaign's category.
Image files above live in
docs/images/(filenames prefixededm-).
Email delivery
Campaign email is delivered over SMTP to a configured relay. This makes the send path provider-independent: the same OmniCRM configuration can point at Mailjet, Mailgun, Amazon SES, or a customer's own SMTP server.
For each recipient OmniCRM:
- Renders the template's HTML and text parts, and the subject, with the recipient's variables.
- Builds a
multipart/alternativeMIME message (plain-text fallback + HTML). - Sends it over a single, reused SMTP connection for the whole campaign.
- Records a per-recipient result (
sentwhen the relay accepts the message,failedon rejection).
Each message also carries a correlation header, X-MJ-CustomID, in the form campaign_{campaign_id}_recipient_{recipient_id}. Mailjet echoes this back in its event webhooks; other relays simply ignore the unknown header.
SMTP Relay Configuration
The relay is configured under the mailjet.smtp block in crm_config.yaml. Despite the section name, this is the generic SMTP relay used for all EDM campaigns and can point at any provider.
mailjet:
api_key: "your-api-key" # used by transactional email + as the default SMTP username
api_secret: "your-api-secret" # used by transactional email + as the default SMTP password
smtp:
host: in-v3.mailjet.com # SMTP relay hostname
port: 587 # submission port
use_tls: true # upgrade the connection with STARTTLS
use_auth: true # authenticate with username/password
username: '' # blank -> use mailjet api_key
password: '' # blank -> use mailjet api_secret
timeout: 30 # connection/command timeout in seconds
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
host | String | Yes | in-v3.mailjet.com | Hostname of the SMTP relay. |
port | Integer | No | 587 | Submission port. Use 587 for STARTTLS, 465 for implicit TLS; many providers also offer 2525/25. |
use_tls | Boolean | No | true | When true, the connection is upgraded with STARTTLS after connecting. Set false only for a local test server. |
use_auth | Boolean | No | true | When true, OmniCRM authenticates with username/password. Set false only for an unauthenticated test server. |
username | String | No | (empty) | SMTP username. When blank, OmniCRM falls back to the Mailjet api_key. For any non-Mailjet relay you must set this explicitly. |
password | String | No | (empty) | SMTP password. When blank, OmniCRM falls back to the Mailjet api_secret. For any non-Mailjet relay you must set this explicitly. |
timeout | Integer | No | 30 | Connection and command timeout, in seconds. |
Important:
username/passworddefault to the Mailjet API credentials purely for convenience. When pointing at a different provider, always set both explicitly — otherwise OmniCRM will try to authenticate to that provider with Mailjet credentials and fail.
Provider Setup Reference
The tables below give the SMTP connection settings and the native tracking mechanism for popular providers. Hostnames, ports, and credential conventions can change or vary by region/plan — confirm against your provider's current documentation. The Tracking notes are informational — what each relay offers in its own console if you use it; OmniCRM only sends SMTP and does not auto-detect or display provider-native stats.
Mailjet
| Setting | Value |
|---|---|
| Host | in-v3.mailjet.com |
| Ports | 587 (STARTTLS), 465 (TLS), 25 / 2525 |
| Username | API Key (Account → API Key Management) |
| Password | Secret Key |
Tracking: Open and click tracking are applied by Mailjet (account default, or per message via the X-Mailjet-TrackOpen / X-Mailjet-TrackClick headers). Engagement events are available in the Mailjet dashboard, via the Statistics API, and as Event API webhooks. OmniCRM already includes a Mailjet webhook handler; correlate events to recipients with the X-MJ-CustomID value OmniCRM sends.
Mailgun
| Setting | Value |
|---|---|
| Host | smtp.mailgun.org (US) · smtp.eu.mailgun.org (EU) |
| Ports | 587 / 465 / 25 / 2525 |
| Username | postmaster@your-sending-domain (or a custom SMTP login created on the domain) |
| Password | The SMTP credential password for that domain |
Tracking: Enable open/click tracking per domain, or per message via X-Mailgun-Track, X-Mailgun-Track-Opens, X-Mailgun-Track-Clicks. Attach a campaign tag with X-Mailgun-Tag and custom correlation data with X-Mailgun-Variables (JSON). Stats appear in the Mailgun dashboard and Events/Stats API; engagement can be pushed to OmniCRM via Mailgun webhooks.
Amazon SES
| Setting | Value |
|---|---|
| Host | email-smtp.<region>.amazonaws.com (e.g. email-smtp.us-east-1.amazonaws.com) |
| Ports | 587 / 2587 (STARTTLS), 465 / 2465 (TLS), 25 |
| Username | SES SMTP username (generated in the SES console) |
| Password | SES SMTP password (distinct from your AWS access keys) |
Tracking: Open and click tracking are enabled through a Configuration Set, attached per message with the X-SES-CONFIGURATION-SET header; add campaign dimensions with X-SES-MESSAGE-TAGS. Events (send, delivery, open, click, bounce, complaint) are delivered to an event destination (SNS, Kinesis Firehose, or EventBridge) and surfaced in CloudWatch — there is no built-in engagement dashboard, so plan an event destination if you want stats.
SendGrid (Twilio)
| Setting | Value |
|---|---|
| Host | smtp.sendgrid.net |
| Ports | 587 / 465 / 25 / 2525 |
| Username | the literal string apikey |
| Password | a SendGrid API key with Mail Send permission |
Tracking: Open/click tracking is set in Tracking Settings (account) or per message via the X-SMTPAPI header (JSON filters such as opentrack/clicktrack, plus category and unique_args for correlation). Stats are in the SendGrid dashboard and Stats API; engagement reaches OmniCRM via the Event Webhook.
Postmark
| Setting | Value |
|---|---|
| Host | smtp.postmarkapp.com |
| Ports | 587 / 2525 / 25 |
| Username | your Server API Token |
| Password | your Server API Token (same value as username) |
Tracking: Enable opens with X-PM-Track-Opens: true and links with X-PM-Track-Links: HtmlAndText. Tag campaigns with X-PM-Tag and add correlation with X-PM-Metadata-<key> headers. Use a Broadcast message stream for bulk/EDM sending. Stats are in the Postmark dashboard and Stats API; engagement reaches OmniCRM via webhooks.
Mailchimp Transactional (Mandrill)
| Setting | Value |
|---|---|
| Host | smtp.mandrillapp.com |
| Ports | 587 / 465 / 25 |
| Username | your Mailchimp/Mandrill account username (usually the account email) |
| Password | any valid Mandrill API key |
Tracking: Account default, or per message via X-MC-Track (e.g. opens,clicks_all). Tag with X-MC-Tags and add correlation with X-MC-Metadata (JSON). Stats are in the Mandrill dashboard and API; engagement reaches OmniCRM via webhooks.
Note: The standard Mailchimp Marketing product is not an SMTP relay. Only Mailchimp Transactional (Mandrill) offers SMTP sending.
Brevo (formerly Sendinblue)
| Setting | Value |
|---|---|
| Host | smtp-relay.brevo.com |
| Ports | 587 / 465 / 25 / 2525 |
| Username | your Brevo account login (email) |
| Password | your SMTP key (Brevo → SMTP & API → SMTP) |
Tracking: Account-level open/click tracking; stats in the Brevo dashboard; engagement reaches OmniCRM via webhooks.
Generic / self-hosted SMTP
| Setting | Value |
|---|---|
| Host | As provided (e.g. a Postfix relay, or a customer's Microsoft 365 / Google Workspace relay) |
| Ports | Typically 587 (STARTTLS) or 465 (TLS) |
| Username / Password | As provided by the operator |
Tracking: A plain SMTP server provides no native engagement tracking. Use OmniCRM-native tracking (below) for opens and clicks, and bounce (DSN) processing for delivery failures.
SMS delivery (OmniMessage)
Bulk campaigns can also be SMS, delivered through the separate OmniMessage service. A campaign's channel is email (default) or sms; for SMS, set channel: sms and an sms_message.
Recipients are the segment's contacts that have a phone number, filtered by the campaign's category on the sms channel.
Sending follows the OmniMessage bulk-campaign flow (OmniCRM performs all three steps):
POST /api/campaign_lists— upload recipients as a named list (msisdn+var1–var4).POST /api/campaigns— create the campaign (message,list_id,source_msisdn,source_smsc,drip_tps).POST /api/campaigns/control{action: "start"}— start the drip.
Personalisation: OmniMessage templates support only {{ var1 }}–{{ var4 }} (plus {{ msisdn }}). Write the familiar named placeholders and OmniCRM maps them automatically: first_name→var1, last_name→var2, customer_name→var3, email→var4.
Status: OmniMessage has no delivery webhook — poll GET /api/campaigns/:id for its stats. POST /crm/email-campaign/campaigns/<id>/sms-sync pulls the latest stats into the campaign (total_delivered / total_bounced / total_sent); run it on a schedule for live numbers.
Config (smsc block in crm_config.yaml):
smsc:
campaigns_url: 'https://smsc:8443/api' # bulk-campaigns API base
source_msisdn: 'YourBrand' # default sender
source_smsc: 'IMS_SMSC' # SMSC route
drip_tps: 50 # per-campaign send rate
verify_tls: false # OmniMessage uses a self-signed cert
timeout: 30
OmniMessage maintains its own STOP/opt-out suppression; OmniCRM additionally applies the contact's
smscategory preferences when building the recipient list.
Statistics and Tracking
There are two independent ways to measure a campaign. They are not mutually exclusive.
What each model gives you
| Metric | Recorded at send (every relay) | In your provider's own console ¹ | OmniCRM-native tracking (any relay) |
|---|---|---|---|
| Sent / queued | ✓ | ✓ | ✓ |
| Send failures (rejected at relay) | ✓ | ✓ | ✓ |
| Delivered | – | ✓ | ✓ (proven by an open/click; otherwise assumed = sent − bounced) |
| Opens | – | ✓ | ✓ (tracking pixel) |
| Clicks | – | ✓ | ✓ (link redirect) |
| Bounces | – | ✓ | ✓ (DSN processing) |
| Spam complaints | – | ✓ | provider feedback loop only |
| Unsubscribes | – | ✓ | ✓ (unsubscribe link + List-Unsubscribe) |
¹ OmniCRM does not detect which provider you use — it is configured with only an SMTP host and credentials, which look identical for every relay. The middle column is whatever your relay happens to offer, viewed in that provider's own console (not in OmniCRM) — unless you wire up its webhook (Model 1) or use OmniCRM-native tracking (Model 2).
Model 1 — Provider-native tracking
Most relays can apply their own open-pixel and link tracking and record the events. Because OmniCRM only holds an SMTP host + credentials, it cannot tell which provider that is, and it does not surface these numbers — you read them by logging into that provider's own console yourself. This needs no OmniCRM configuration; it is simply whatever your chosen relay offers.
To pull those events into OmniCRM's analytics instead, OmniCRM must ingest the provider's webhook and match each event back to a recipient using the per-message correlation field:
| Provider | Correlation field OmniCRM sets / uses |
|---|---|
| Mailjet | X-MJ-CustomID (handler already present) |
| Mailgun | X-Mailgun-Variables |
| Amazon SES | X-SES-MESSAGE-TAGS |
| SendGrid | unique_args (via X-SMTPAPI) |
| Postmark | X-PM-Metadata-* |
| Mandrill | X-MC-Metadata |
Each provider's event format differs, so ingesting a new provider's events requires a provider-specific webhook handler.
Model 2 — OmniCRM-native tracking (portable)
Because provider-native tracking only exists while you stay on that provider, OmniCRM measures engagement itself, identically on any relay. This is implemented and enabled by default:
- Opens — a 1×1 tracking pixel is injected into the HTML; the request records the open against the recipient.
- Clicks — links are rewritten to redirect through OmniCRM, which logs the click and forwards to the destination.
- Delivered — an open or click is proof of delivery, so OmniCRM marks the recipient delivered on the first pixel/redirect hit — no provider webhook required. Recipients that were
sentand never bounce are counted as assumed delivered. - Bounces — campaigns use a return-path that routes bounce notifications (DSNs) to OmniCRM's inbound mail server for processing.
- Unsubscribes — a
List-Unsubscribeheader and an in-body unsubscribe link feed the existing unsubscribe suppression list.
With this model all statistics live in OmniCRM and survive a change of email provider.
Current support status
- Active (default): SMTP campaign sending and OmniCRM-native tracking on any relay —
sent/failedat send, plus delivered, opened, clicked, bounced detected by OmniCRM itself (pixel, click-redirect, VERP bounce). No provider webhook required. See Tracking configuration. - Optional: if you happen to use Mailjet, its event webhook can also feed engagement in (correlated via
X-MJ-CustomID) — but OmniCRM does not depend on it.
Tracking configuration
OmniCRM-native tracking is configured under the edm block in crm_config.yaml:
edm:
enable_open_tracking: true # inject the open pixel into campaign HTML
enable_click_tracking: true # rewrite links through the click redirect
enable_unsubscribe: true # add List-Unsubscribe header + in-body link
tracking_base_url: '' # public URL recipients can reach; falls back to crm.base_url
tracking_secret: '' # secret that signs tracking tokens; falls back to mailjet.api_secret
bounce_domain: '' # domain whose MX points at the inbound mail server (enables VERP bounces)
bounce_prefix: 'bounce' # VERP local-part prefix -> bounce+<token>@<bounce_domain>
| Parameter | Type | Default | Description |
|---|---|---|---|
enable_open_tracking | Boolean | true | Inject a 1×1 open-tracking pixel into the HTML body. |
enable_click_tracking | Boolean | true | Rewrite absolute http(s) links to redirect through OmniCRM so clicks are logged. |
enable_unsubscribe | Boolean | true | Add a List-Unsubscribe header (+ RFC 8058 one-click) and an in-body unsubscribe link. |
tracking_base_url | String | (crm.base_url) | Public base URL for tracking links. Must be reachable by recipients' mail clients — not localhost. Falls back to crm.base_url when blank. |
tracking_secret | String | (mailjet.api_secret) | Secret used to HMAC-sign tracking tokens. Falls back to mailjet.api_secret when blank. |
bounce_domain | String | (empty) | Domain for VERP bounce addresses (bounce+<token>@<domain>). Its MX must point at OmniCRM's inbound mail server. Blank disables the VERP return-path. |
bounce_prefix | String | bounce | Local-part prefix for VERP bounce addresses. |
Tokens are HMAC-signed (campaign_id, recipient_id) pairs, so tracking needs no extra database column and links cannot be forged or enumerated without the secret.
Note on tokens: the token is the campaign/recipient IDs signed, not encrypted — the IDs are base64-encoded, so a recipient who decodes a tracking URL can read the (sequential) campaign and recipient numbers. Tokens are therefore tamper-proof but not opaque. If the IDs must not be exposed, switch to random per-recipient tokens stored on
EmailCampaignRecipient(adds a column) or an encrypted token payload.
The public tracking endpoints (no authentication) are served by the API:
| Endpoint | Purpose |
|---|---|
GET /crm/email-campaign/edm/open/<token> | Records an open; returns a 1×1 transparent GIF. |
GET /crm/email-campaign/edm/click/<token>?u=<url> | Records a click, then 302-redirects to <url> (http/https only). |
GET or POST /crm/email-campaign/edm/unsubscribe/<token> | Adds the recipient to the suppression list and records a marketing/email opt-out in the contact's communication preferences (honoured on future sends). |
Important:
tracking_base_urlmust be a URL recipients can reach. Bounce capture additionally requiresbounce_domainwith an MX record pointing at OmniCRM's inbound mail server, which recognises VERP recipients and records the bounce instead of creating a ticket.
Communication preferences
Per-contact opt-outs are stored in a communication_preferences JSON field on the contact (no extra table), shaped as {category: {channel: opted_in}} — categories marketing, newsletter, product_updates, surveys; channels email, sms. It uses an opt-out model: a missing category/channel means the contact is opted in, so new categories or channels need no schema change.
A campaign unsubscribe records a marketing/email opt-out, and campaign recipient selection excludes any contact opted out of marketing/email (in addition to the global suppression list). EDM campaigns are treated as the marketing category. Note: the opt-out filter uses JSON_EXTRACT and is not indexed, which is fine at typical contact volumes.
Communication categories
Categories are operator-defined in crm_config.yaml under edm.communication_categories (no table), as a list of {key, name, channel, default_opt_in, active}:
edm:
communication_categories:
# Email marketing message types (opt-out: default_opt_in true = subscribed until they opt out)
- {key: marketing, name: Marketing, channel: email, default_opt_in: true}
- {key: newsletter, name: Newsletter, channel: email, default_opt_in: true}
- {key: promos, name: Promotions & Offers, channel: email, default_opt_in: true}
- {key: product_updates, name: Product Updates, channel: email, default_opt_in: true}
- {key: surveys, name: Surveys & Feedback, channel: email, default_opt_in: false}
- {key: events, name: Events & Webinars, channel: email, default_opt_in: false}
# SMS marketing message types (opt-in: default_opt_in false = excluded until they opt in)
- {key: marketing, name: Marketing SMS, channel: sms, default_opt_in: false}
- {key: promos, name: Promotions SMS, channel: sms, default_opt_in: false}
- {key: service_alerts, name: Service Alerts, channel: sms, default_opt_in: true}
Each category belongs to one channel (email or sms) and carries its own default opt-in, so an operator can run, say, an opt-out Promotions (email) category alongside an opt-in-required SMS Alerts (sms) category.
Effective opt-in for a contact resolves as: an explicit contact preference wins; otherwise the category's default_opt_in applies.
| Endpoint | Purpose |
|---|---|
GET /crm/email-campaign/communication-categories | List the category catalog (read-only; defined in crm_config.yaml). |
GET /crm/email-campaign/contacts/<id>/communication-preferences | Resolved opt-in per active category for a contact. |
PUT /crm/email-campaign/contacts/<id>/communication-preferences | Set one preference (body {category, channel, opted_in}). |
Each campaign specifies a category (defaults to marketing). Recipient selection excludes contacts opted out of that category/channel, applying the category's default_opt_in for contacts with no explicit preference.
The unsubscribe link is a preference centre: it shows the contact's email categories with their current opt-in state (the campaign's own category highlighted), lets them save changes, and offers Unsubscribe from all email (a global suppression). A one-click unsubscribe (RFC 8058 List-Unsubscribe-Post) opts the contact out of just that campaign's category.
Deliverability
Regardless of provider, configure authentication for your sending domain so campaigns reach the inbox:
| Mechanism | Purpose |
|---|---|
| SPF | Authorises the provider's servers to send for your domain. |
| DKIM | Cryptographically signs messages; each provider supplies the DKIM record(s) to publish. |
| DMARC | Aligns SPF/DKIM and sets a policy for failures; improves placement and reporting. |
| Return-Path | Set so bounce notifications (DSNs) are routed where OmniCRM (or the provider) can process them. |
Testing
For development and testing, point the relay at a local SMTP debug server that captures messages instead of delivering them:
mailjet:
smtp:
host: 127.0.0.1
port: 1025
use_tls: false
use_auth: false
A debug server is provided for this purpose; it prints each message's recipients, subject, and correlation ID (and, in verbose mode, the full rendered body), so you can confirm Jinja2 rendering and message structure without sending live email.
Troubleshooting
Campaign stuck or recipients marked failed immediately
- Verify
host/portand thatusername/passwordare set for the chosen provider (they default to Mailjet credentials when blank). - Confirm the sender address is a verified/authorised sender on the provider.
- Check the provider's domain authentication (SPF/DKIM) is complete.
Email sends but personalisation shows blank or literal {{ ... }}
- Confirm the variable name exists for the segment (e.g.
first_name). - Check the template uses valid Jinja2 syntax; a template error fails that recipient and is recorded in its error message.
Provider dashboard shows opens/clicks but OmniCRM analytics do not
- This is expected unless OmniCRM ingests that provider's webhook (Model 1) or OmniCRM-native tracking is enabled (Model 2). Mailjet event ingestion is available today; other providers require a provider-specific handler.
No open/click data anywhere
- Open/click tracking must be enabled on the provider (account setting or per-message headers). On a plain/self-hosted relay there is no native tracking — use OmniCRM-native tracking.