From 5efc20fde679dede14c7cb9c9de47bb83fe890ea Mon Sep 17 00:00:00 2001 From: Thibault Le Ouay Ducasse Date: Wed, 21 Oct 2026 15:47:14 +0200 Subject: [PATCH] email: improve deliverability --- .../routes/v1/pageSubscribers/post.test.ts | 2 +- .../src/routes/v1/pageSubscribers/post.ts | 2 +- apps/workflows/src/cron/monitor.ts | 3 +-- packages/db/src/schema/pages/constants.ts | 4 ++++ packages/emails/src/billing.test.tsx | 12 ++++------- packages/emails/src/billing.tsx | 7 +++---- packages/emails/src/client.tsx | 20 +++++++++---------- 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/apps/server/src/routes/v1/pageSubscribers/post.test.ts b/apps/server/src/routes/v1/pageSubscribers/post.test.ts index 5a5973b28..f45c8ed02 100644 --- a/apps/server/src/routes/v1/pageSubscribers/post.test.ts +++ b/apps/server/src/routes/v1/pageSubscribers/post.test.ts @@ -58,7 +58,7 @@ test("sends the same redesigned verification email as the status page form", asy const payload = send.mock.calls[0][0]; expect(payload.to).toEqual([email]); expect(payload.from).toBe( - "Status Page ", + "Status Page ", ); expect(payload.subject).toMatch(/^Confirm your subscription to /); // the route calls the template as a function, so this is its diff --git a/apps/server/src/routes/v1/pageSubscribers/post.ts b/apps/server/src/routes/v1/pageSubscribers/post.ts index 2b47d53cf..f8ea2b1f1 100644 --- a/apps/server/src/routes/v1/pageSubscribers/post.ts +++ b/apps/server/src/routes/v1/pageSubscribers/post.ts @@ -112,7 +112,7 @@ export function registerPostPageSubscriber(api: typeof pageSubscribersApi) { link, page: _page.title, }), - from: "Status Page ", + from: "Status Page ", to: [normalizedEmail], subject: `Confirm your subscription to ${_page.title}`, }); diff --git a/apps/workflows/src/cron/monitor.ts b/apps/workflows/src/cron/monitor.ts index 7142b9a09..d2c52786a 100644 --- a/apps/workflows/src/cron/monitor.ts +++ b/apps/workflows/src/cron/monitor.ts @@ -29,8 +29,7 @@ import { env } from "../env"; const redis = Redis.fromEnv(); -const PAUSE_FROM = - "Thibault from openstatus "; +const PAUSE_FROM = "Thibault from openstatus "; const PAUSE_REPLY_TO = "thibault@openstatus.dev"; // Check if GCP is properly configured (not empty and not placeholder values) diff --git a/packages/db/src/schema/pages/constants.ts b/packages/db/src/schema/pages/constants.ts index c1ebdb334..9d0139caf 100644 --- a/packages/db/src/schema/pages/constants.ts +++ b/packages/db/src/schema/pages/constants.ts @@ -15,6 +15,10 @@ export const subdomainSafeList = [ "shop", "skills", "cli", + "mail", + "notifications", + "ship", + "subscribers", ]; export const pageAccessTypes = [ diff --git a/packages/emails/src/billing.test.tsx b/packages/emails/src/billing.test.tsx index d193f8aa4..bf3ee7e95 100644 --- a/packages/emails/src/billing.test.tsx +++ b/packages/emails/src/billing.test.tsx @@ -584,7 +584,7 @@ describe("send plumbing", () => { const [payload, options] = send.mock.calls[0]; expect(payload.to).toEqual(["max@acme.dev"]); expect(payload.from).toBe( - "Thibault from openstatus ", + "Thibault from openstatus ", ); expect(payload.replyTo).toBe("thibault@openstatus.dev"); expect(payload.subject).toBe( @@ -607,7 +607,7 @@ describe("send plumbing", () => { "b@acme.dev", ]); expect(emails[0].from).toBe( - "openstatus ", + "openstatus ", ); expect(emails[0].replyTo).toBe("ping@openstatus.dev"); expect(emails[0].subject).toBe( @@ -685,9 +685,7 @@ describe("send plumbing", () => { }); expect(id).toBe("email_123"); const [payload, options] = send.mock.calls[0]; - expect(payload.from).toBe( - "openstatus ", - ); + expect(payload.from).toBe("openstatus "); expect(payload.replyTo).toBe("ping@openstatus.dev"); expect(payload.scheduledAt).toBe("2026-09-22T00:00:00.000Z"); expect(options).toEqual({ @@ -720,9 +718,7 @@ describe("send plumbing", () => { loss, }); const [payload, options] = send.mock.calls[0]; - expect(payload.from).toBe( - "openstatus ", - ); + expect(payload.from).toBe("openstatus "); expect(payload.replyTo).toBe("ping@openstatus.dev"); expect(payload.subject).toBe( "Your trial ends on 25 Sep — add a payment method to keep starter", diff --git a/packages/emails/src/billing.tsx b/packages/emails/src/billing.tsx index 84980e8c3..dd0c40fb5 100644 --- a/packages/emails/src/billing.tsx +++ b/packages/emails/src/billing.tsx @@ -34,11 +34,10 @@ import TrialEndingEmail, { import { sendBatchEmailHtml, sendEmail } from "./send"; const RAW_FROM = "Thibault from openstatus "; -// Signed hybrid: personal name on the system subdomain, replies to a real inbox. -const SIGNED_FROM = - "Thibault from openstatus "; +// Signed hybrid: personal name on the account subdomain, replies to a real inbox. +const SIGNED_FROM = "Thibault from openstatus "; const SIGNED_REPLY_TO = "thibault@openstatus.dev"; -const SYSTEM_FROM = "openstatus "; +const SYSTEM_FROM = "openstatus "; const SUPPORT_EMAIL = "ping@openstatus.dev"; const DAY_MS = 86_400_000; diff --git a/packages/emails/src/client.tsx b/packages/emails/src/client.tsx index c915a29b8..40d51776a 100644 --- a/packages/emails/src/client.tsx +++ b/packages/emails/src/client.tsx @@ -38,6 +38,8 @@ export function pageUpdateSubject(req: { } const SYSTEM_FROM = "openstatus "; +const ACCOUNT_FROM = "openstatus "; +const SUBSCRIBERS_DOMAIN = "subscribers.openstatus.dev"; const SUPPORT_EMAIL = "ping@openstatus.dev"; // Deterministic Resend rejections: retrying the identical request can never @@ -229,7 +231,7 @@ export class EmailClient { const unsubscribeUrl = `${statusPageBaseUrl}/unsubscribe/${subscriber.token}`; const manageUrl = `${statusPageBaseUrl}/manage/${subscriber.token}`; return { - from: `${req.pageTitle} `, + from: `${req.pageTitle} `, subject: pageUpdateSubject(req), to: subscriber.email, react: ( @@ -279,13 +281,11 @@ export class EmailClient { try { const html = await render(); const result = await this.client.emails.send({ - from: `${ - req.workspaceName || "openstatus" - } `, + from: ACCOUNT_FROM, replyTo: SUPPORT_EMAIL, - subject: `You've been invited to join ${ - req.workspaceName || "openstatus" - }`, + subject: req.workspaceName + ? `${req.invitedBy} invited you to join ${req.workspaceName} on openstatus` + : `${req.invitedBy} invited you to join openstatus`, to: req.to, html, }); @@ -341,7 +341,7 @@ export class EmailClient { try { const html = await render(); const result = await this.client.emails.send({ - from: "Status Page ", + from: `Status Page `, subject: `Confirm your subscription to ${req.page}`, to: req.to, html, @@ -370,7 +370,7 @@ export class EmailClient { try { const html = await render(); const result = await this.client.emails.send({ - from: "Status Page ", + from: `Status Page `, subject: `Authenticate to ${req.page}`, to: req.to, html, @@ -399,7 +399,7 @@ export class EmailClient { const html = await render(); const result = await this.client.emails.send({ - from: SYSTEM_FROM, + from: ACCOUNT_FROM, subject: "Sign in to openstatus", to: req.to, html, -- 2.54.0