Newsletter
Full newsletter platform: subscriber management, campaign composer with A/B testing, digest builder, templates, automations, analytics, and open/click tracking
Newsletter Platform
The Newsletter module at /admin/newsletter is a full-featured email newsletter platform built into the ArgoBox admin area. It handles the entire lifecycle: subscriber acquisition, list management, campaign composition with A/B testing, template library, automation workflows, email delivery via Resend, and open/click analytics with tracking pixels.
The platform is built on the three-tier modular architecture. The core logic lives in a standalone npm package (@argobox/newsletter), with ArgoBox-specific database and delivery adapters in Tier 2, and the admin UI in Tier 3.
Admin Dashboard
The admin page (/admin/newsletter) has six tabs, each covering a different aspect of the platform.
Dashboard Tab
Overview of the newsletter's health and activity:
- Stats cards -- Total subscribers, active, confirmed, and unsubscribed counts
- Growth chart -- 30-day subscriber acquisition trend
- Top campaigns -- Recent campaigns ranked by engagement (opens, clicks)
- Recent events -- Live feed of tracking events (opens, clicks, unsubscribes)
All data is fetched from /api/newsletter/analytics.
Subscribers Tab
Full subscriber list management with search, pagination, import/export:
- Search by email or name
- Per-subscriber details -- Email, name, source, subscription date, confirmed status, engagement score
- CSV Import -- Upload a CSV file with at minimum an
emailcolumn header. Supports optionalname,source, andtagscolumns - CSV Export -- Download the full subscriber list as CSV via
/api/newsletter/export - Inline actions -- Unsubscribe or delete individual subscribers
Composer Tab
Campaign creation with rich HTML editing and A/B testing:
- Subject line with optional A/B variant (
ab_subject_b) - Rich HTML body editor with preview
- Preview text -- The snippet shown in email client inboxes
- Template selection -- Load a template to pre-fill the body
- Segment targeting -- Filter recipients by tags or signup source
- Scheduling -- Send immediately or schedule for a future date/time
- Sender overrides -- Custom from email and reply-to per campaign
Campaigns Tab
History of all campaigns (draft, scheduled, sent):
- Campaign subject, status, send date, recipient count
- Per-campaign analytics: open count, click count, unsubscribe count, bounce count
- Drill-down to detailed campaign analytics with top clicked links and event timeline
Templates Tab
Reusable email template library:
- Built-in templates -- Weekly digest, welcome, re-engagement, milestone, referral, survey
- Custom templates -- Create your own with name, description, category, subject template, and HTML body
- Professional HTML -- All built-in templates use responsive table-based HTML with the ArgoBox brand (gradient accents, glassmorphic styling)
- Usage tracking -- Each template tracks how many times it has been used
- Template categories -- digest, welcome, re-engagement, milestone, referral, survey
Automations Tab
Event-driven email workflows:
- Trigger types --
welcome(new subscriber), or custom triggers defined intrigger_config - Configurable delay -- Send immediately or after N minutes
- Template or custom body -- Link to a template or write inline HTML
- Enable/disable per automation without deleting
- Sent count tracking -- See how many emails each automation has triggered
Setup
1. Apply Database Migrations
Three D1 migrations must be applied in order:
| Migration | Purpose |
|---|---|
0021_newsletter.sql |
Base tables: newsletter_subscribers (id, email, source, status, tags) and newsletter_campaigns (id, subject, body, type, status) |
0022_newsletter_premium.sql |
Expands to full platform: double opt-in fields, engagement scoring, open/click counts, scheduling, segmentation, A/B testing, templates table, automations table, tracking events table |
0023_newsletter_templates_pro.sql |
Upgrades all built-in templates to professional branded HTML. Adds template categories: re-engagement, milestone, referral, survey. Enables welcome automation with professional HTML |
Migration files are in /migrations/.
2. Configure Environment / Settings
Newsletter settings are stored in Cloudflare KV (key: data:module:newsletter:config) and managed through the Settings page at /admin/settings/newsletter.
| Setting | Description |
|---|---|
fromEmail |
Display name and email for outbound newsletters (e.g., ArgoBox <newsletter@argobox.com>) |
replyTo |
Where subscriber replies are directed |
unsubscribeSecret |
HMAC-SHA256 secret for generating secure one-click unsubscribe tokens. Changing this invalidates all existing unsubscribe links |
The settings page includes a "Generate Random Secret" button for the unsubscribe secret.
3. Required Services
- Cloudflare D1 -- Subscriber, campaign, template, automation, and tracking data
- Cloudflare KV (
ARGOBOX_CACHEnamespace) -- Newsletter config storage + legacy subscriber fallback - Resend -- Email delivery (API key configured as
RESEND_API_KEYenvironment variable)
API Endpoints
All endpoints are under /api/newsletter/. Admin endpoints require Cloudflare Access authentication.
Public Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/newsletter/subscribe |
POST | Subscribe an email address. Accepts { email, source }. Returns success or duplicate status. Stores in D1 with KV fallback |
/api/newsletter/unsubscribe |
GET | One-click unsubscribe via HMAC token. Params: email, token. Verifies token, marks subscriber as unsubscribed |
/api/newsletter/unsubscribe |
POST | Form-based unsubscribe confirmation. Same token verification |
/api/newsletter/track/open |
GET | Tracking pixel endpoint. Params: cid (campaign ID), email. Records open event, returns 1x1 transparent GIF |
/api/newsletter/track/click |
GET | Click tracking redirect. Params: cid, email, url. Records click event, redirects to target URL |
Admin Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/newsletter/subscribers |
GET | List subscribers with pagination and search. Query params: page, limit, search, status |
/api/newsletter/subscribers |
POST | Create or update a subscriber. Body: { email, name?, source?, tags?, status? } |
/api/newsletter/subscribers |
DELETE | Remove a subscriber by email. Body: { email } |
/api/newsletter/campaigns |
GET | List campaigns with optional status filter. Query params: status |
/api/newsletter/campaigns |
POST | Create a new campaign. Body includes subject, body_html, segment filters, scheduling, A/B config |
/api/newsletter/send |
POST | Send a campaign to matching subscribers via Resend. Body: { campaignId } |
/api/newsletter/templates |
GET | List all email templates |
/api/newsletter/templates |
POST | Create or update a template. Body: { name, category, subject_template, body_html, ... } |
/api/newsletter/templates |
DELETE | Delete a template by ID. Body: { id } |
/api/newsletter/automations |
GET | List all automations |
/api/newsletter/automations |
POST | Create or update an automation. Body: { name, trigger_type, template_id, delay_minutes, ... } |
/api/newsletter/automations |
DELETE | Delete an automation by ID. Body: { id } |
/api/newsletter/analytics |
GET | Dashboard analytics: subscriber stats, growth chart, top campaigns, recent events. Query param: campaignId for per-campaign drill-down |
/api/newsletter/export |
GET | Download all subscribers as CSV |
/api/newsletter/import |
POST | Bulk import subscribers from CSV upload (multipart form) |
/api/newsletter/digest |
POST | Auto-generate a digest campaign from recent blog posts |
Architecture
Three-Tier Structure
Tier 1: @argobox/newsletter (npm package)
packages/newsletter/src/
core.ts -- Email builder, HMAC tokens, CSV import/export, validation
types.ts -- All TypeScript interfaces (Subscriber, Campaign, Template, etc.)
adapters.ts -- Standalone adapters (Resend sender, Node env, Bearer auth)
Tier 2: ArgoBox Adapters
src/lib/newsletter/
adapters/argobox.ts -- D1 database queries, KV operations, Resend delivery
settings.ts -- KV-based config loading with 60s cache
email-templates.ts -- ArgoBox-branded HTML email templates
Tier 3: ArgoBox Integration
src/pages/admin/newsletter.astro -- Admin dashboard (6-tab SPA)
src/pages/admin/settings/newsletter.astro -- Settings page
src/pages/api/newsletter/*.ts -- API route handlers
modules/newsletter/module.json -- Module manifest
Data Flow
- Subscriber signs up via public
/api/newsletter/subscribeendpoint - Stored in D1
newsletter_subscriberstable (with KV fallback for legacy) - Admin composes a campaign in the Composer tab, optionally using a template
- Campaign saved to D1
newsletter_campaignstable - Send triggered via
/api/newsletter/send-- queries matching subscribers, builds personalized emails with tracking pixels and click-tracked links, delivers via Resend - Opens tracked via 1x1 pixel in
/api/newsletter/track/open - Clicks tracked via redirect in
/api/newsletter/track/click - Analytics aggregated from
newsletter_tracking_eventstable, displayed in Dashboard tab
Database Tables
| Table | Purpose |
|---|---|
newsletter_subscribers |
Subscriber profiles with engagement tracking (20 fields) |
newsletter_campaigns |
Campaign definitions with A/B testing and segment config |
newsletter_templates |
Reusable email templates with categories and usage counts |
newsletter_automations |
Event-driven email workflows with trigger configuration |
newsletter_tracking_events |
Open/click/unsubscribe events with campaign and subscriber linkage |
Open Source Usage
The core logic is available as a standalone npm package for use outside of ArgoBox.
Installation
npm install @argobox/newsletter
Sub-path Imports
| Import | Contents |
|---|---|
@argobox/newsletter |
Everything (barrel export) |
@argobox/newsletter/core |
Pure functions: email builder, HMAC tokens, CSV, validation |
@argobox/newsletter/adapters |
Standalone adapters: Resend sender, Node env, Bearer auth |
@argobox/newsletter/types |
All TypeScript interfaces |
Quick Example
import {
buildNewsletterEmail,
generateUnsubscribeToken,
buildUnsubscribeUrl,
buildTrackingPixelUrl,
} from '@argobox/newsletter/core';
import { createResendEmailSender } from '@argobox/newsletter/adapters';
// Build a newsletter email
const token = await generateUnsubscribeToken('reader@example.com', process.env.UNSUB_SECRET!);
const unsubscribeUrl = buildUnsubscribeUrl('reader@example.com', token);
const { html, text } = buildNewsletterEmail({
subject: 'Weekly Homelab Digest',
unsubscribeUrl,
trackingPixelUrl: buildTrackingPixelUrl('campaign-1', 'reader@example.com'),
posts: [
{ title: 'ZFS vs Btrfs', url: 'https://example.com/zfs', description: 'A real-world comparison' },
],
});
// Send via Resend
const sender = createResendEmailSender(process.env.RESEND_API_KEY);
await sender.send({
from: 'newsletter@example.com',
to: 'reader@example.com',
subject: 'Weekly Homelab Digest',
html,
text,
headers: { 'List-Unsubscribe': `<${unsubscribeUrl}>` },
});
Custom Adapters
Implement the EmailSender, EnvAdapter, or AuthAdapter interfaces to use your own email provider or authentication:
import type { EmailSender } from '@argobox/newsletter/types';
export function createSESEmailSender(region: string): EmailSender {
return {
async send(opts) {
// Your AWS SES / Postmark / Mailgun implementation
return { ok: true };
},
};
}
Runtime Compatibility
Works in Cloudflare Workers, Node 18+, Deno, and Bun. Core functions use only Web standard APIs (crypto.subtle, fetch).
Key Files
| Path | Purpose |
|---|---|
packages/newsletter/ |
Standalone npm package (Tier 1 core + Tier 2 adapters) |
packages/newsletter/src/core.ts |
Email builder, HMAC tokens, CSV, validation |
packages/newsletter/src/types.ts |
All TypeScript interfaces |
packages/newsletter/src/adapters.ts |
Resend sender, Node env adapter, Bearer auth |
src/lib/newsletter/adapters/argobox.ts |
D1/KV database adapter for ArgoBox |
src/lib/newsletter/settings.ts |
KV config loader with 60s cache |
src/lib/newsletter/email-templates.ts |
Branded HTML email templates |
src/pages/admin/newsletter.astro |
Admin dashboard (6 tabs) |
src/pages/admin/settings/newsletter.astro |
Settings page |
src/pages/api/newsletter/*.ts |
13 API route files (16 endpoint methods) |
modules/newsletter/module.json |
Module manifest |
migrations/0021_newsletter.sql |
Base tables |
migrations/0022_newsletter_premium.sql |
Full platform expansion |
migrations/0023_newsletter_templates_pro.sql |
Professional template upgrade |
Related Docs
- Newsletter Signup -- Public-facing signup component and the original subscribe-only system
- Open-Source Packages -- All publishable packages including
@argobox/newsletter - Email -- The full email platform (separate from newsletter)