Trinitas Volunteers

The Outbox

See and control every email the system sends.

What’s in the Outbox

/admin/email is the single place to see everything the email subsystem has done, is about to do, or failed to do. Two views:

  • Messages — one row per individual email (email_messages). Tabs: Scheduled, Sent, Failed.
  • Campaigns — grouped by email_campaigns: quarterly kickoff, weekly reminder, ad-hoc. Each campaign drills into its recipient list.

Columns on the messages view: kind, recipient, provider, scheduled_for, sent_at, status, provider_message_id (click to open the provider’s tracking page when available).

Campaign drill-in

Click a campaign → see:

  • Metadata — kind, scope (quarter:2026Q3 or service:2026-04-12), provider (cf_email / mailchimp / …), scheduled_for, sent_at, status.
  • Recipient list — every email_messages row attached to this campaign, with per-recipient status (sent / bounced / opened when provider reports).
  • Rendered preview — the actual HTML the next recipient will receive. For Mailchimp campaigns this mirrors what their dashboard shows.
  • ActionsCancel (only while scheduled), Resend failed, Push now (move a scheduled campaign’s send time to immediately).

Provider layers

The Outbox shows which provider each message and campaign went through. The app is designed so that any single provider can be swapped or removed:

  • Transactional sends (magic links, coverage broadcasts, swap responses) → Cloudflare Email Service by default.
  • Bulk campaigns (quarterly kickoff, weekly reminder) → per-kind provider selection: direct (fan-out via the transactional provider) or mailchimp (Marketing API with IF/ELSEIF merge tags).
  • Audience sync (Mailchimp list membership) → mailchimp or noop.

Defaults sit in the Worker’s wrangler.jsonc; overrides live in the admin settings (future phase). If Mailchimp ever needs to be disabled entirely, flip the campaign provider to direct and the audience provider to noop; no caller code changes.

What you can’t do from the Outbox

  • Edit a sent message. Once it’s out, it’s out.
  • Delete history. Everything is retained for audit. Log retention for Workers Logs is ~30 days in R2; email_messages stays forever.
  • Send on behalf of another admin. Every outbound write records the admin who triggered it.

When something goes wrong

If a send fails, the message row has status='failed' and the error column is populated. The Tail Worker posts errors to the deacons’ Slack within a minute or two. From the Outbox, Resend failed retries with fresh provider call; if it fails again, the error text usually points at the right fix (auth, rate limit, malformed recipient address).