Email publisher dashboard (admin)
The /admin/email surface is where the founder and other admins edit drip copy, compose broadcasts, and manage the suppression list. This article documents the dashboard layout, the editor flow, and the safety guardrails on test sends.
Where to find it
Visit /admin/email when signed in as an admin (your Clerk user ID must be listed in the RADIUSOS_ADMIN_USER_IDS env var). Non-admins are silently redirected to the home page so the route's existence doesn't leak.
The dashboard sits on top of the email infrastructure shipped in Phase 1 of the email drip workstream (lib/email/* + the EmailSequence / EmailStep / EmailSubscription / EmailSuppression / EmailSendLog / EmailBroadcast Prisma models). The auto-enroll cron is Phase 2, the publisher is this Phase 3.
Tabs at a glance
| Tab | What it does |
|---|---|
| Overview | Active subscriber count, suppression count, 24h send count, 7-day delivery rate, 7-day open rate, last 20 sends. |
| Sequences | All EmailSequence rows. System sequences (Onboarding, Evergreen) carry a system tag and can't be deleted, only edited. |
| Broadcasts | One-off composer surface. Each broadcast has a segment (all / free / pro / business / team) and an optional schedule. |
| Subscribers | EmailSubscription table. Pause / resume per row. Filter by sequence or status. |
| Suppressions | Opt-out list. Manual remove from this surface to put a falsely-suppressed user back on the list. |
| Send log | Every EmailSendLog row. Filter by status (queued / sent / delivered / bounced / complained / failed) or email substring. |
Editing a step
Click any sequence to see its ordered steps. Click any step to open the editor. The editor is a two-pane layout:
- Left: subject, preheader (inbox preview text), Markdown body, optional CTA label + URL, day delay from prior step, and the published toggle.
- Right: a live HTML preview rendered through the same lib/email/render.ts pipeline as production sends. The preview updates 200ms after each keystroke with mock merge fields (Becca / Whitford Roofing).
Click Save to persist. Use the merge field chips at the bottom to copy {{firstName}} / {{firstWorkspaceName}} / etc. into your body.
Toggle the mobile preview in the top-right of the preview pane to spot-check at 360px. Most subscribers read on their phone first.
Sending a test
Every editor surface has a Send test to me button. Clicking it fires the current draft (subject / preheader / body / CTA) to YOUR Clerk email address with a [TEST] subject prefix. The recipient is resolved server-side from your Clerk session and cannot be changed from the form.
This is intentional and load-bearing for safety. The editor cannot be used to email arbitrary subscribers. If you need to message a specific user, use a broadcast with a tightly-scoped segment, or send from your own Gmail.
If the EMAIL_DRY_RUN env var is set, the test path returns success without actually hitting Resend. The admin tool tells you it ran in dry-run mode so you know to flip the var off when ready.
Composing a broadcast
Broadcasts use the same editor as steps, plus two extra controls:
- Segment: all / free / pro / business / team. Computed at send time. (Segmentation beyond these tier filters is future work.)
- Schedule for: optional datetime-local input. Save the broadcast first, then click Schedule to flip it to scheduled status. Unschedule returns it to draft.
Sent and sending broadcasts are read-only. Drafts and scheduled broadcasts can be edited and deleted.
Suppressions and opt-outs
Subscribers who unsubscribe are automatically added to the EmailSuppression table. Hard bounces and spam complaints are added too once the Resend webhook handler ships (Phase 6).
If a real user emails you saying their unsubscribe was a mistake, find their address on the Suppressions tab and click Remove. They'll start receiving emails again on the next cron tick.
You can also pre-emptively suppress an address (typo cleanup, internal test addresses) via the Add manually button. Reason defaults to 'manual'.