From f5d656650f6aba335bcb99563d5830e87ef3657f Mon Sep 17 00:00:00 2001 From: Nariman Jelveh Date: Mon, 20 Jul 2026 11:55:46 -0700 Subject: [PATCH] feat(email): accept custom message headers in sendRaw (#3411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(email): accept custom message headers in sendRaw Lets callers set transport-level headers such as List-Unsubscribe / List-Unsubscribe-Post. Also documents the puter-email driver's automatic unsubscribe / report-abuse footer and the new `suppressed` result field in the puter.js email module. * feat(email): envelope override in sendRaw + suppressed in EmailSendResult type Adds an optional `envelope` field to SendMailOptions so callers can set transport recipients independently of the visible To/Cc headers (used by the email-send driver for per-recipient deliveries), and adds the `suppressed: string[]` field to the public EmailSendResult typing to match the driver's documented result. Co-Authored-By: Claude Fable 5 * feat(email): document per-recipient best-effort delivery via `failed` The email-send driver now attempts every recipient's private delivery even when one fails, returning failed addresses in the result's `failed` array instead of failing the whole call — so callers retry only the failed subset and never re-mail delivered recipients. Adds the field to the public EmailSendResult typing and module docs. --- src/backend/clients/email/EmailClient.ts | 7 +++++++ src/puter-js/src/modules/Email.js | 10 ++++++++++ src/puter-js/types/modules/email.d.ts | 10 +++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/backend/clients/email/EmailClient.ts b/src/backend/clients/email/EmailClient.ts index 60faf2116..7e274c470 100644 --- a/src/backend/clients/email/EmailClient.ts +++ b/src/backend/clients/email/EmailClient.ts @@ -45,11 +45,18 @@ export interface SendMailOptions { to: string; cc?: string; bcc?: string; + /** Optional transport recipients when they differ from visible headers. */ + envelope?: { + from?: string; + to: string; + }; subject: string; html?: string; text?: string; replyTo?: string; attachments?: EmailAttachment[]; + /** Extra message headers (e.g. List-Unsubscribe), passed to the transport. */ + headers?: Record; } export type EmailValidator = (email: string) => Promise | boolean; diff --git a/src/puter-js/src/modules/Email.js b/src/puter-js/src/modules/Email.js index 833abcf86..98cb2836d 100644 --- a/src/puter-js/src/modules/Email.js +++ b/src/puter-js/src/modules/Email.js @@ -22,6 +22,16 @@ import * as utils from '../lib/utils.js'; * }); * * Positional form: `await puter.email.send(to, subject, body)`. + * + * Every mail automatically gets an unsubscribe / report-abuse footer. + * Recipients who unsubscribe are dropped from future sends — they come + * back in the result's `suppressed` array — and a send whose `to` list + * is entirely unsubscribed is rejected. + * + * Each recipient gets a private delivery. A recipient whose delivery + * fails comes back in the result's `failed` array (everyone else got + * their copy — retry with just those addresses); the call only rejects + * when no recipient could be delivered. */ class Email { /** diff --git a/src/puter-js/types/modules/email.d.ts b/src/puter-js/types/modules/email.d.ts index 50435a763..3b2b820a1 100644 --- a/src/puter-js/types/modules/email.d.ts +++ b/src/puter-js/types/modules/email.d.ts @@ -36,10 +36,18 @@ export interface EmailSendOptions { } export interface EmailSendResult { - /** Transport message id, when the mail server reports one. */ + /** First transport message id reported for this send, when available. */ messageId: string | null; /** Total charge for this send, in microcents. */ cost: number; + /** Recipients omitted because they opted out of this sender's mail. */ + suppressed: string[]; + /** + * Recipients whose delivery attempt failed. Everyone else got their + * copy — retry with just these addresses. A send where every delivery + * fails rejects instead. + */ + failed: string[]; } /**