Install and enable
Activating MailWizard — the licence step, the three tables, the fifteen permission keys, the hourly cron job, the mail transport it depends on, and a smoke test that proves a campaign really sends.
Activation is two minutes of clicking. The part that actually decides whether campaigns send is the mail configuration underneath it, and that is not something MailWizard checks or reports on. Work through this page in order and finish with the smoke test — a MailWizard install that is broken looks exactly like one that is working until an operator notices a campaign has been Active for six hours with zero recipients sent.
Before you activate
- Bicrypto core installed and running — see Installing Bicrypto
- Outbound mail works: a password-reset email from your install arrives in a real inbox
- The
cronPM2 app is running — campaigns are sent by a scheduled job and nothing else - Redis reachable, because the cron scheduler runs on BullMQ
- Your CodeCanyon username and the MailWizard licence code to hand
The mail check is the one people skip. MailWizard does not own a transport, a queue or a sending domain — it calls the platform's mailer. If that is misconfigured, every recipient on every campaign fails, and the only place the reason appears is the cron log.
Activation
-
Open Admin → System → Extensions — the screen is at
/admin/system/extension. -
Find MailWizard and activate it — enter your CodeCanyon username and licence code. The platform verifies the purchase before enabling anything. The extension's internal name is
mailwizard; that is the name you will see in the database and in cron logs. -
Confirm the menu appears — Extensions → Business Tools → Email Marketing in the admin navigation, landing on
/admin/mailwizard/campaign. -
Confirm the addon's own header — the MailWizard screens carry their own three-item nav: Campaigns, Templates, Blocks.
Turning the extension off at /admin/system/extension stops the campaign cron
within about a minute — the scheduler re-reads extension status on a 60-second
loop and deregisters the job without a restart. Campaign, template and block rows
are left alone, so re-enabling picks up exactly where you were. A campaign left
ACTIVE when you disable the addon simply stops progressing.
What activation creates
Three tables, created automatically by the model sync:
| Table | Holds |
|---|---|
mailwizard_template |
Templates: the exported HTML (content) and the editor's design JSON (design) |
mailwizard_block |
Reusable blocks: a name, an optional category, and one or more editor rows as JSON |
mailwizard_campaign |
Campaigns: name, subject, status, speed, template link, and the whole recipient list as a JSON string in targets |
All three are paranoid — deletes are soft — and all three keep their bodies in
TEXT('long') columns, because a single template with an inlined image passes
64 KB without difficulty.
The recipient list living inside the campaign row is the single most consequential design decision in this addon. It is why there is no per-recipient reporting, why the analytics screen cannot chart delivery, and why a campaign has a hard recipient ceiling. See Building and sending a campaign.
If you build from source, run pnpm types:generate once the tables exist.
Permissions
Fifteen keys are seeded — five actions across three objects. Assign them to roles
at /admin/crm/role; Super Admin holds them all automatically.
| Key | Grants |
|---|---|
access.mailwizard.campaign |
Opening the Campaigns screen and the addon's menu entry |
view.mailwizard.campaign |
Reading campaigns, including the recipient list |
create.mailwizard.campaign |
Creating a campaign |
edit.mailwizard.campaign |
Editing, changing status, updating targets, retrying failures |
delete.mailwizard.campaign |
Deleting campaigns, singly or in bulk |
access.mailwizard.template |
Opening the Templates screen |
view.mailwizard.template |
Reading templates, and the template dropdown on the campaign form |
create.mailwizard.template |
Creating and importing templates |
edit.mailwizard.template |
Saving from the visual editor |
delete.mailwizard.template |
Deleting templates |
access.mailwizard.block |
Opening the Blocks screen |
view.mailwizard.block |
Reading blocks, and the editor's Blocks panel |
create.mailwizard.block |
Creating a block, including Save as block in the template editor |
edit.mailwizard.block |
Editing a saved block |
delete.mailwizard.block |
Deleting blocks |
The Add Targets dialog reads the platform's own user list through
/api/admin/crm/user, which is gated by view.user — a core CRM permission, not
a MailWizard one. A role holding every key in the table above and not view.user
opens the picker to an empty list with no error, so the operator concludes there
are no users to email.
access.* keys gate the admin pages; the view / create / edit / delete
keys gate the API. Both matter: granting only the page key produces a screen
that loads and then reports permission errors on every request.
The one cron job
Check it at /admin/system/cron, category mailwizard.
| Job | Every | Does |
|---|---|---|
processMailwizardCampaigns |
60 minutes | Picks up every ACTIVE campaign and sends up to speed emails from each |
That is the entire sending mechanism. There is no queue worker, no second job and no other trigger — the Start button on a campaign only sets its status.
Scheduled jobs run in the cron PM2 app. If that process is stopped, crashed or
never started, every campaign sits at ACTIVE and 0% forever, and nothing in the
MailWizard screens says so. pm2 list is the check. See
Processes and ports.
The Campaigns screen has one indicator for this: the Stalled Sends figure
counts campaigns that are ACTIVE but have not been written to in over an hour.
On a healthy install that number is zero.
Mail transport
MailWizard sends through whichever transport APP_EMAILER selects. It does not
have settings of its own — there is no MailWizard settings screen.
nodemailer-service, nodemailer-smtp, nodemailer-sendgrid or local.The From address and sender name are also global, and there is no per-campaign override:
| Transport | From address comes from | Sender name comes from |
|---|---|---|
nodemailer-service |
APP_NODEMAILER_SERVICE_SENDER |
APP_EMAIL_SENDER_NAME, else NEXT_PUBLIC_SITE_NAME, else Support |
nodemailer-smtp |
NEXT_PUBLIC_APP_EMAIL if set, else APP_NODEMAILER_SMTP_SENDER |
same |
nodemailer-sendgrid |
APP_SENDGRID_SENDER |
same |
local |
NEXT_PUBLIC_APP_EMAIL, else no-reply@localhost |
same |
Two consequences worth planning for. First, every campaign appears to come from
the same address as your transactional email — a bounce storm on a marketing
send damages the reputation of the address your password resets go out on.
Second, DKIM signing configured through APP_NODEMAILER_DKIM_* only applies to
the local sendmail transport. On SMTP, service or SendGrid, signing is your
provider's job and must be set up in DNS.
The platform's global outbound kill switch is checked by the notification queue
and by the templated sendEmail path. The MailWizard send loop calls the mailer
directly and never consults it. Setting MAIL_DISABLED=true on a staging copy of
production silences the rest of the platform and leaves any ACTIVE campaign
sending real email to real customers.
Before restoring a production database onto a test box, set every campaign to
PENDING or CANCELLED — or disable the mailwizard extension outright.
Smoke test
Prove the whole chain end to end before you point a campaign at real customers. Use your own mailbox as the only recipient.
-
Create a template — Templates → Create, name it
Smoke test, drop a text block onto the canvas with a recognisable sentence in it, and save. Reopen it from the list; the canvas should show what you built, not a blank page. -
Create a campaign — Campaigns → Create. Name and subject anything, pick the
Smoke testtemplate, set Emails per hour to1. -
Add exactly one recipient — click Add Targets, search for your own account by first name (the search matches first name only), tick it, and Add selected. Save the campaign.
-
Start it — on the campaign page press Start. The status becomes
ACTIVE. -
Run the job rather than waiting an hour — open
/admin/system/cron, findprocessMailwizardCampaignsunder themailwizardcategory and trigger it. -
Check the outcome — the recipient card should badge Sent, progress should read 100%, and the campaign status should be
COMPLETED. The email should be in your inbox.
If the recipient badges Failed, the reason is written onto the recipient card itself — that text comes straight from the mail transport and is almost always an authentication or connection error rather than anything to do with MailWizard. Troubleshooting works through the common ones.
Upgrading an existing install
Run pnpm updator, then restart the backend so the campaign cron and the admin
routes pick up the new build. Two things to check afterwards.
The old create flow discarded the design and the HTML it was given and stored a
literal {} as the body, while reporting success. Every campaign using such a
template delivered an empty email. Editing always saved correctly, so opening the
template and saving once repairs it.
The Templates list flags these for you: a template whose design cannot be parsed carries an HTML only badge, and one with no body carries No content.
Saving a campaign whose recipient list exceeds 10,000 characters is refused. Campaigns that already hold a longer list keep sending — only saving is blocked, which means you cannot edit them at all. Plan to split them before you need to change one.
New permissions are seeded on start. If you added roles between versions, revisit
/admin/crm/role after upgrading — the block permissions in particular did not
exist before v6.2.0, and a role without them sees an empty Blocks panel in the
editor.