Repository navigation
Email 6/7 (Ops A): production product mail cutover to Resend #424
Description
Part of #418. Read the parent first. Step 6 of 7. No code.
Goal
Switch production product mail from SMTP to Resend, with a tested rollback and a clear definition of done.
Owner and gate
- Owner: maintainer. It needs the prod host env file, Resend and the workflow dispatch.
- Gate:
- Email 1/7 (PR 1): explicit provider config, the provider contract and job identity #419, Email 3/7 (PR 2): typed failures, delivery and a safe DLQ drain #421, Email 4/7 (PR 3): Resend webhook for bounces and complaints #422 and Email 5/7 (PR 4): admin Email setup page with a test email #423 are all on prod. D4 is ruled on Tracking: move email to Resend behind a pluggable provider #418.
- The privacy page update (step 0) is deployed.
- The maintainer's private pre-cutover checklist is done (kept off GitHub, per
SECURITY.md).
Steps
-
Privacy page (
apps/webapp/src/pages/privacy.tsx), through thetech-writerskill:- Name Resend as the email provider for sign-in and notification mail.
- Add "Where your data goes": Resend stores data in the US. The safeguard is the UK Extension to the EU-US Data Privacy Framework, plus SCCs with the UK Addendum. Confirm the wording with counsel.
- Say that notification and digest mail include short extracts of document and chat text. Say that the provider keeps sent mail and logs for 30 days.
- Bump
LEGAL_UPDATEDinapps/webapp/src/components/pages/legal/legalMetadata.ts:1, and deploy.
-
Record the current values in a comment here:
- the current
EMAIL_PROVIDERandEMAIL_FROMlines from/opt/projects/prod.docs.plus/.env; - the current email DLQ depth, from the Email setup page;
- the name of the current SMTP provider, for the Day 30 offboarding.
Rollback restores both lines. Restoring
EMAIL_PROVIDERalone would leave the old relay sending as the Resend domain, and that fails. - the current
-
In the Resend product team, create a prod send-only key for
mail.docs.plus. -
Edit
/opt/projects/prod.docs.plus/.env. Keep everySMTP_*line.EMAIL_PROVIDER=resend EMAIL_FROM="docs.plus <notify@mail.docs.plus>" # the D2 product domain RESEND_API_KEY=<prod send-only key> -
Redeploy (see Terms on Tracking: move email to Resend behind a pluggable provider #418).
-
In Resend, add the prod webhook for
https://<prod rest-api host>/api/email/webhooks/resend, with eventsemail.bounced,email.complained,email.suppressedandemail.failed.- Add
RESEND_WEBHOOK_SECRET=<its whsec_>to the prod host env file and redeploy again. - The webhook comes after the first deploy on purpose. Adding it earlier only collects 404 retries.
- Add
-
Smoke test.
- Open the Email setup page. Status is
ready, and the connection check showssend_only_key. - Send a test email and confirm it arrives.
- In Gmail, open a real digest or notification and confirm that the one-click Unsubscribe link appears.
- Open the Email setup page. Status is
Roll back at once if any of these happens in the first hour
incident-email-operatororincident-email-config-invalidfires;- the email DLQ depth rises above the step 1 value;
- the smoke test fails.
Rollback
- Restore both lines recorded in step 1.
- Keep
RESEND_API_KEY,RESEND_WEBHOOK_SECRETand the Resend webhook. Mail that Resend already accepted keeps producing bounces and complaints for days. - Redeploy.
- Send a test email from the Email setup page and confirm it arrives.
- Only then run the drain inside a
hocuspocus-workercontainer:bun scripts/drain-email-dlq.tsfirst (dry run), thenbun scripts/drain-email-dlq.ts --apply. - Post what happened here.
Exit
Close this issue when all of these hold for 7 days:
- No email alert fired.
-
worker-dlq-depthstayed silent. - The Resend dashboard met the D4 rule: no hard bounce and no complaint, or each one investigated and explained here.
- Each day, the maintainer requested one magic link and it arrived. Sign-in mail is unchanged until Email 7/7 (Ops B): Supabase Auth magic links through Resend SMTP #425 (Ops B), so this checks that nothing else broke.
Day 30 (maintainer)
Post on #418 one of these:
- "Removed
SMTP_*from the prod host env file. Revoked the old relay credential. Offboarded the old provider: account closed or stored mail deleted, and its record-of-processing row removed." - "Extended because ."
Exit evidence
Post as a comment here:
- The step 1 values.
- A link to the deployed privacy page change.
- A screenshot of the Email setup page after step 6, checked for unmasked addresses.
- The 7-day list above, ticked, with the 7 dates of the magic-link checks and the Resend bounce and complaint counts.
Activity
[-]Email 6/7 (Ops A): switch production product mail to Resend[/-][+]Email 6/7 (Ops A): production product mail cutover to Resend[/+]HMarzban commentedon Oct 9, 2026
Status, 2026-10-09: cutover live
GET /api/email/healthreports"provider":"resend".EMAIL_PROVIDER=resendandEMAIL_FROM="docs.plus <notify@mail.docs.plus>". TheSMTP_*lines are kept, and dated.env.bak-*backups hold the old values for rollback.Still open: step 0, the privacy page, is being built today. Step 5, the webhook, follows #422. The 7-day exit window started at 09:28:41Z today.
docs(webapp): name Resend as the email provider on /privacy
docs(repo): record the 9 Oct security and email batch in the changelo…
fix(webapp): show the composer skeleton while a heading chat waits fo…
docs(webapp): state the Resend sign-in sender once on /privacy
HMarzban commentedon Oct 12, 2026
Resend webhook live, 2026-10-12
https://prodback.docs.plus/api/email/webhooks/resend, with the eventsemail.bounced,email.complained,email.failedandemail.suppressed.RESEND_WEBHOOK_SECRETis set in the host env file. Deploy run 38239392350 (51a9be84f) finished green at 11:14:12Z.POSTto the webhook now answers401, not404. The route is mounted, and it refuses calls without a valid signature.From today, bounces and complaints land in
public.email_bounces. Before today that table could not record anything, so the #424 exit check for 2026-10-09 to 2026-10-12 must read the Resend log instead.Earliest close: 2026-10-16, if the Resend log and
email_bouncesshow no hard bounce and no complaint.HMarzban commentedon Oct 15, 2026
Related issues
record_email_bounce. Confirm the Confirm the bounce alert link migration is applied on production #354 link migration is on production, or bounce alerts open a "settings" pad.Cross-link review, 2026-10-15. Generated by Claude Code