Email Campaigns
Bellamy Book includes an Email Campaigns feature for administrators to create and send bulk marketing or announcement emails to users. Campaigns can be scheduled for a future time or sent after you generate the recipient queue. The feature is license-gated and uses a background job (Quartz) to send emails in batches.
License
Email Campaigns is a license-gated feature. It is only available when the emailCampaigns feature flag is enabled (typically Tier 2 or Tier 3). Some tiers apply a rate limit (e.g. 500 emails per month); Tier 3 may allow unlimited usage.
How it works
- Create campaign — Admin creates a campaign with name, subject, body (plain or HTML), optional scheduled time, and option to include blog file download recipients.
- Generate queue — Admin triggers Generate queue for a campaign. The system builds the recipient list:
- All users with a verified email
- Optionally, emails from Blog File Download (users who downloaded blog files), if Include blog file download emails was checked when creating/editing the campaign
Duplicate emails are deduplicated (one email per address per campaign).
- Sending — The EmailCampaignJob (Quartz) runs on a cron schedule (default: every minute). It finds campaigns that are Scheduled or Draft with
ScheduledAtin the past, marks them as Sending, and sends pending emails in batches (e.g. 50 per run) via the application’s email sender. - Completion — When all queued emails for a campaign are processed (sent or failed), the campaign status becomes Completed (or Failed if the job marked it as failed).
Campaign statuses
| Status | Description |
|---|---|
| Draft | Created, no schedule or schedule in the past; not yet sending. |
| Scheduled | Has a future ScheduledAt; job will start sending when that time is reached. |
| Sending | Job is currently sending emails for this campaign. |
| Completed | All queued emails have been processed (sent or failed). |
| Cancelled | Admin cancelled the campaign (only from Draft/Scheduled/Sending). |
| Failed | Job marked the campaign as failed (e.g. critical error). |
Admin Panel
Email Campaigns are managed in the Admin Panel under Notifications → Email Campaigns (visible when emailCampaigns is enabled).
- Route:
/notifications/email-campaigns
Tabs
- Campaign list — Paginated list of campaigns (name, subject, status, scheduled time, total/sent/failed counts). Actions: Cancel (Draft/Scheduled/Sending), Delete, Generate queue (Draft/Scheduled, once per campaign).
- Create campaign — Form: name, subject, body, HTML checkbox, scheduled date/time, Include blog file download emails. Submit creates a campaign (Draft or Scheduled).
- History — Paginated list of sent emails per campaign. Filters: campaign ID, campaign name, recipient email.
Typical workflow
- Create a campaign (name, subject, body, optional schedule, optional “include blog file download emails”).
- Optionally set Scheduled at for future send time.
- Click Generate queue for that campaign (recipients are computed and stored; each campaign can have its queue generated only once).
- If scheduled, the job will start at that time; otherwise the job will pick it up on the next run (e.g. within a minute) if
ScheduledAtis in the past or null. - Monitor status and History for sent/failed counts.
API Endpoints
All endpoints are under api/EmailCampaign and require Admin role. License enforcement applies to write operations (create, generate-queue); the feature must be enabled and within limits.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/EmailCampaign | List campaigns (query: skip, take, status) |
| GET | /api/EmailCampaign/{campaignId} | Get campaign by ID |
| GET | /api/EmailCampaign/history | Get email history (query: campaignId, campaignName, recipientEmail, skip, take) |
| POST | /api/EmailCampaign | Create campaign (body: name, subject, body, isBodyHtml, scheduledAt?, includeBlogFileDownloadEmails) |
| PUT | /api/EmailCampaign/{campaignId} | Update campaign (same body as create; only Draft/Scheduled) |
| POST | /api/EmailCampaign/{campaignId}/cancel | Cancel campaign (Draft/Scheduled/Sending) |
| POST | /api/EmailCampaign/{campaignId}/generate-queue | Generate recipient queue (once per campaign; returns totalEmailsGenerated) |
| DELETE | /api/EmailCampaign/{campaignId} | Delete campaign and its queue |
Create/Update body
- name (required, max 200)
- subject (required, max 500)
- body (required)
- isBodyHtml (boolean, default true)
- scheduledAt (optional, ISO date-time; if set in the future, status is Scheduled)
- includeBlogFileDownloadEmails (boolean, default false) — include emails from blog file downloads in the queue
Background job
- Job:
EmailCampaignJob(Quartz, in-process). - Schedule: Configurable via Quartz:EmailCampaignJob (default: every minute, e.g.
0 * * ? * *). - Behavior: Loads campaigns that are ready to send (Scheduled/Draft with
ScheduledAt≤ now), marks them Sending, fetches pending email queue entries (batch size e.g. 50), sends each email viaIEmailSender, updates queue and campaign counts; marks campaign Completed or Failed when done.
Related
- Admin Panel — all admin features and license flags
- Features overview