Email marketing

Send one email to one or more lists, now or at a set time; run a sequence of emails for everyone who joins a list; and post announcements to your users. Every send respects consent and unsubscribes.

What it is

ToolWhat it doesAdmin screen
BroadcastsOne email to the people on chosen lists, now or scheduledEmail, Broadcasts (/admin/email/broadcasts)
Drip campaignsA sequence of emails, each a set number of hours after the one before, for everyone who joins a listEmail, Drip Campaigns (/admin/email/drip-campaigns)
AnnouncementsAn in-app notification to users, with an optional email copyEmail, Announcements (/admin/email/announcements)

All three send to Audience lists, including App users (live) lists for broadcasts and sequences (App audience). Each list's page also shows its broadcasts and its sequence. The email underneath, such as the provider, templates, the composer, the brand kit, the logs and the unsubscribe link, is the Email engine.

Turn it on or off

The feature id is email-marketing. It is on by default and needs audience. To turn it off, in src/custom/features.ts:

export const CUSTOM_FEATURES: FeatureChoice = {
    off: ['email-marketing'],
};

When it is off:

A lighter switch exists too: Settings, Email Settings has a Broadcasts and a Drip campaigns switch that pause those sends without removing anything (see Email engine). See Feature ids for every feature.

Set it up

  1. Set up an email provider and turn email on in Settings, Email Settings (Email engine). Until then every send is logged as skipped with email_disabled.

  2. Set the brand kit, so every email carries your logo, colors and footer with the unsubscribe link.

  3. Have at least one list with subscribed people (Audience).

  4. Open Email, Announcements once. It seeds the default templates, notification types, event mappings and the system lists. Seeding only adds what is missing.

Use it

Send a broadcast

  1. Open Email, Broadcasts (or a list's page, Broadcasts tab). Write the subject.

  2. Pick Send to lists. Optionally pick Exclude lists (anyone on them is skipped) and a premiumType filter (only contacts whose user account has that plan).

  3. Click Preview count for the number of subscribed people it will reach.

  4. Build the email from blocks and send yourself a test.

  5. Click Send broadcast, or turn on Schedule for later, pick a time and click Schedule broadcast. A scheduled broadcast can be cancelled until it starts.

How it sends:

Broadcast states: scheduled, queued, processing, paused, completed, failed, cancelled. Each email is also in Email Logs.

Run a drip sequence

  1. Open Email, Drip Campaigns and create a campaign: a name and the list it runs on. The list cannot change afterwards.

  2. Add steps. Each step is a template and a Delay (h): the hours after the previous step (for the first step, after joining). Pick an existing template, or create a new email for the step and refine it later in the Composer.

  3. Choose Enroll existing members on activate if people already on the list should get it too. Otherwise only people who join after activation do.

  4. Click Activate. A campaign needs at least one step.

How it runs:

A template made with New email (in the Composer or from a step) has no sender of its own, and a step sends with its template's sender. Give each such template a sender before activating: see Email engine.

Pause and Resume stop and restart sending without losing anyone's place, but people who join the list while it is paused are not enrolled. Archive ends the campaign for good.

To make each signup form's welcome email the first step of a sequence on its list, run the migrateWelcomeToSequences admin callable once. It creates the campaign per form and marks the form so the welcome is not also sent directly (Signup forms).

Post an announcement

  1. Open Email, Announcements, tab Send announcement. Write a title, a body and an optional link.

  2. Choose the audience: All users, By role (type the role, such as user), or By list. Optionally pick Exclude lists: anyone on them is skipped.

  3. Turn on Also send email for an email copy, then click Publish announcement.

An announcement is an in-app notification, so only people with an account get it: a contact on a list without an account is skipped. The email copy is marketing, so it respects unsubscribes. One announcement reads at most 2,000 people from each list, or 2,000 users for All users or a role. The other two tabs, Notification types and Event mappings, belong to Notifications and Automations.

Extend it

What it stores

FirestoreHolds
BroadcastEmails/{id}One broadcast: subject, HTML, sender, audience (include, exclude, filters), scheduledAt, status, and sent, skipped and failed counts
_broadcast_continue/{id}A request to carry on a paused broadcast (Cloud Functions only)
DripCampaigns/{id}A campaign: name, listId, status (draft, active, paused, archived), steps (template id and delay in hours), enrollExistingOnActivate, and enrolled, completed and exited counts
DripEnrollments/{campaignId}_{contactId}One person in one campaign: currentStep, nextSendAt, status (active, completed, exited) and why they left. For an app user the contact id is app_ followed by their App audience id
Announcements/{id}One announcement, its audience and its counts

Every collection is also in Data model. The notifications an announcement creates are in Notifications (Notifications); every email is in EmailLogs (Email engine). No Storage paths.

Cloud Functions (each deploys as arccms- followed by its name):

FunctionKindDoes
processBroadcastTrigger on BroadcastEmailsStarts a broadcast created as queued
continueBroadcastTrigger on _broadcast_continueCarries on a paused broadcast, and starts a scheduled one that is due
processScheduledBroadcastsEvery 5 minutesQueues due scheduled broadcasts
previewBroadcastAudienceAdmin callableThe eligible count
processDripQueueEvery 15 minutesSends up to 100 due steps per run
activateDripCampaign, archiveDripCampaignAdmin callablesActivate (and enroll existing members); archive (and end every enrollment)
migrateWelcomeToSequencesAdmin callableTurns each form's welcome into a sequence step
sendAnnouncementAdmin callableCreates the announcement and its notifications

Who can do what

DataAdminsEveryone else
BroadcastEmails, DripCampaignsRead and writeNothing
DripEnrollments, AnnouncementsRead; Cloud Functions writeNothing
_broadcast_continueNothing (Cloud Functions only)Nothing

The callables refuse anyone without the admin role (Roles and claims). A member only ever sees the notifications addressed to them.

Troubleshooting

SymptomCause and fix
Preview count is lower than the list's member countOnly subscribed people count. Pending and unsubscribed contacts, and people on an excluded list, are left out.
A broadcast completed with everyone skippedEmail is off or the Broadcasts switch is off: open Email Logs for the skip reason.
A scheduled broadcast shows failed, "overdue by more than 24h"It could not start within a day of its time. Create it again.
A broadcast fails with "Exceeded maximum processing chunks (200)"It paused and carried on 200 times without finishing, usually a very low rate limit or an exhausted quota. Raise the provider limits in Settings, Email Settings and send it again.
People on the list got nothing after activationOnly people who join after activation are enrolled, unless Enroll existing members on activate was on when the campaign was activated. Activate is offered only once, on a draft.
People who joined the list got nothingThey joined while the campaign was paused or a draft: only an active campaign enrolls people.
An announcement reached fewer people than the listContacts without an account are skipped.

A step whose template was deleted holds every enrollment at that step, and they retry every 15 minutes without end. Before deleting a template, archive the campaigns that use it or give their steps another template.