
What it does
- Designs e-mail templates in a block editor, with typed variables, conditions, lists and live test data.
- Keeps edits in draft and publishes them as versions, in several locales.
- Sends a template by its slug from your code, an automation or the dashboard, through any
@antelopejs/interface-emailprovider. - Logs every send with the provider's timeline, and turns delivery, open and click events into an overview.
- Protects your sender reputation with a suppression list fed by hard bounces, spam complaints and unsubscriptions.
Install
Run the AntelopeJS CLI from the root of a project that already loads @antelopejs/dms. Add an e-mail provider if the project has none:
ajs project modules add @antelopejs/dms-mailing
ajs project modules add @antelopejs/nodemailer
Quick start
In development, send to a throwaway Ethereal inbox:
// antelope.config.ts
nodemailer: {
source: { type: "package", package: "@antelopejs/nodemailer", version: ">=0.0.6 <1.0.0" },
config: { ethereal: true },
},
Restart the backend, then the frontend, and open Modules › Mailing (/modules/mailing/overview). On a workspace with no templates, create the Order confirmation template from the starter gallery and publish it. Its slug is order-confirmed. The New template dialog derives the slug from the name instead, so it gives order-confirmation. Check the slug before you send. Any module can then send it through @antelopejs/interface-dms-mailing:
import { SendTemplate } from "@antelopejs/interface-dms-mailing";
await SendTemplate("order-confirmed", {
tenantId,
to: "[email protected]",
variables: {
customer: { firstName: "Sofie" },
order: {
number: "#10482",
total: { value: 4800, currency: "EUR" },
lines: [{ name: "Linen shirt", total: { value: 4800, currency: "EUR" } }],
url: "https://example.com/orders/10482",
},
},
});
Compatibility
| Package | Supported | Tested with |
|---|---|---|
@antelopejs/interface-api | >=0.3.0 <1.0.0 | 0.3.0 |
@antelopejs/interface-api-util | >=0.3.0 <1.0.0 | 0.3.0 |
@antelopejs/interface-core | >=0.4.0 <1.0.0 | 0.4.0 |
@antelopejs/interface-data-api | >=0.6.1 <1.0.0 | 0.6.1 |
@antelopejs/interface-database | >=0.3.0 <1.0.0 | 0.3.0 |
@antelopejs/interface-database-decorators | >=0.2.0 <1.0.0 | 0.2.0 |
@antelopejs/interface-dms | >=0.12.0 <1.0.0 | 0.12.0 |
@antelopejs/interface-dms-automation | >=0.6.0 <1.0.0 | 0.6.0 |
@antelopejs/interface-dms-mailing | >=0.10.0 <0.11.0 | 0.10.0 |
@antelopejs/interface-email | >=0.0.6 <1.0.0 | 0.0.6 |
The module also needs:
- A DMS backend in a release that implements the
@antelopejs/interface-dmsrange in the table above. - The
@antelopejs/dms-frontendrenderer in the range ofengines["@antelopejs/dms-frontend"]infrontend-vue/package.json. Theajs dmsCLI refuses to build the module with a renderer outside that range. - A database module that enforces unique indexes, such as
@antelopejs/mongodbor@antelopejs/postgres. - An e-mail provider, which is an
@antelopejs/interface-emailimplementation such as@antelopejs/nodemailer.
Slugs
Sends find a template by its slug, so a slug belongs to one template per workspace. The slug index of the mailing_templates table is unique. Creating or duplicating a template on a slug another template holds, even in a race, answers 409 with $dms_mailing.errors.duplicate_slug and creates nothing. Deleting a template frees its slug. The unique index needs a database module that enforces unique indexes, such as @antelopejs/mongodb or @antelopejs/postgres. RethinkDB refuses the schema.
Documentation
- Introduction
- Installation
- Configuration
- Usage
- Extending: sending from code, templates as code, importing past sends, provider events and webhooks, automations
- FAQ
The guide's sources live in packages/dms-mailing/docs. The module ships the dms-mailing Claude Code skill in packages/dms-mailing/skills, declared under antelopeJs.skills.
Contributing
This package lives in the AntelopeJS/dms-mailing pnpm workspace, next to @antelopejs/interface-dms-mailing. Read CONTRIBUTING.md and the repository README for the development commands.