[data-reveal]{opacity:1!important;transform:none!important}
FAQ
Mailing

FAQ

Answers to the common questions and problems with the Mailing module.

My code gets a send with the status suppressed

The recipient is on the suppression list: a hard bounce, a spam complaint, an unsubscription or a manual entry. error names the reason as an i18n key ($dms_mailing.errors.suppressed.hard_bounce, ….complaint, ….unsubscribe, ….manual). Do not retry: reactivate the address first if it is valid again.

A send was refused for a missing variable

Settings › Delivery guards › Missing variables is set to Refuse the send, and the call did not pass a required variable. Pass it in variables, or switch the guard to Send anyway to send with blanks. For a marketing template, unsubscribeUrl and preferencesUrl are required when its footer shows those links.

My provider's webhook calls are refused

Settings › Provider & webhook › Webhook status shows the last refused call and its reason:

ReasonFix
missing_secretSend the x-mailing-webhook-secret header. Most providers cannot add one: put a relay in front.
bad_secretCopy the current secret again (rotating invalidates the old one). A workspace that has never opened a mailing page or sent anything has no secret yet.
invalid_eventThe body is not in the generic event format.

The webhook URL points to localhost

The URL is built on the DMS clientBaseUrl. Set it to the public origin of the dashboard in the dms module's config.

The logo is missing from my e-mails

The logo is the DMS branding's light logo at an absolute public URL. It is left out on a localhost, private-IP or .local origin, and for SVG files, which Gmail and Outlook refuse. Use a PNG and a public clientBaseUrl.

A recipient got English instead of German

The template has no de version, so the module sent the workspace's fallback locale. The send log shows it (DE asked), and the editor offers to create the missing locale.

Do edits change what customers receive?

No. Edits stay in a draft until you publish; real sends always use the published version, and every send records the version it used.

My code changed a declared template, but the dashboard still shows the old one

DeclareTemplate returned kept: someone edited the template, left a draft, unpublished or archived it since the code last declared it, or it was made in the dashboard under that slug. A user's work always wins over a declaration. Publish the code's version from the editor, or bring the template back to exactly what the code last declared, and the next declaration refreshes it again. See Ship templates as code.

Does an unchanged declaration mean the template can send?

No. unchanged means the declaration matches the last one applied, not that the template is still live. Archiving or unpublishing it in the dashboard remains effective; removing its category does not make an otherwise unchanged declaration fail.

A declaration rejected, but its new content appears in the dashboard

A storage or attention-refresh failure can occur after publication commits. The latest version remains readable even if its separate history row was not written yet. Resolve the storage problem and retry the same declaration to repair the history row; do not delete the template to retry. See the consumer reference for concurrent-call and failure semantics.

The project does not start offline with Ethereal

@antelopejs/nodemailer with ethereal: true creates its test account over the internet when the project starts, and a failure stops the whole project before Mailing runs. Use a local SMTP server (Mailpit, MailHog) while offline: see Installation. Once a project runs, a provider that fails or cannot be reached only marks the sends failed and shows the provider banner on the Sends page; nothing crashes.

I upgraded to 0.6: what changed?

Mailing 0.6 implements @antelopejs/interface-dms-mailing >=0.8.0 <0.9.0. Upgrade the runtime alongside the interface before using DeclareTemplate, DeclareTemplateCategory or ImportSends; all three require an explicit, non-blank tenantId. SendTemplate and RecordEmailEvent keep their contracts. DMS 0.9 and renderer 0.6 remain required. See Ship templates as code for tenant lifecycle integration and retry semantics.

I upgraded to 0.5: what changed?

Mailing 0.5 requires @antelopejs/dms 0.9, @antelopejs/interface-dms 0.7 and the renderer @antelopejs/dms-frontend 0.6. Upgrade those dependencies together first along the DMS 0.9 upgrade guide; Troubleshooting covers dependency resolution and frontend integration errors. Templates, sends, settings and the webhook URL are unchanged, so no data migrates, and @antelopejs/interface-dms-mailing stays at 0.7: modules that call SendTemplate need no change.

I upgraded from 0.3: what changed?

The v2 redesign neither reads what 0.3.x wrote nor takes its calls:

  • Webhook URL. POST /api/mailing/events/<provider>, with the tenant in the body, is gone. Copy the URL Settings › Provider & webhook shows (/api/mailing/events/<provider>/<tenantId>) into the provider again.
  • Settings writes. POST /api/mailing/settings is gone: send the changed fields with PUT /api/mailing/settings. A write carrying a read-only field (webhookUrl, provider) is refused.
  • Categories. The settings take categories as an array only, no longer as its JSON text, and always with the marketing category (it can be renamed, not removed: campaign rules depend on it).
  • Interface. The module implements @antelopejs/interface-dms-mailing 0.7, whose SendStatus adds suppressed.
  • Data. A 0.3.x database is not read any more (settings rows without the tenant key, templates without version numbers, sends without a stage): start the module on an empty database.