Every adapter follows the same rules, so a package that calls send can retry without knowing the provider.
Errors
- Throw means retry. Any error thrown from
sendtells the caller to try again later (a timeout, throttling, missing credentials, an unverified sender). PermanentMailErrormeans fail. It is thrown for a send that can never succeed, such as an invalid address or a message the provider rejected. The caller marks it failed without retrying. Check for it withisPermanentMailError(error), which also works when two copies of this package are installed.- At-least-once. A caller can repeat a send after a crash that came after the provider accepted it, so a recipient can rarely get a duplicate. A mailer doesn’t have to be idempotent.
- The mailer never changes the message it’s given.
Each adapter’s own error mapping is on its page: Wraps, your own SES client, email-sdk.
replyTo and headers
replyTo is its own field. Adapters map it to the provider’s reply-to option and never copy it into headers.
headers is for extras such as In-Reply-To, References or List-Unsubscribe. wrapsMailer, sesMailer and emailSdkMailer reject the headers the provider owns (From, To, Cc, Bcc, Reply-To, Subject, Date, Message-ID, MIME-Version, Content-Type, Content-Transfer-Encoding, Return-Path, Sender) with a PermanentMailError.
memoryMailer rejects the same list, in any case, with a PermanentMailError, and records nothing for that send. Before 0.2.0 it only checked malformed headers; since 0.2.0 a test that passes one of these to memoryMailer fails. Use replyTo for the reply-to address.
Header names and values follow RFC 5322. A name is printable ASCII (33–126) without :. A value is printable ASCII, spaces and tabs, on one line: no CR or LF (so no folding), no other control character and no non-ASCII text (RFC 2047-encode it first). Every mailer here, memoryMailer included, throws a PermanentMailError for anything else before it calls the provider. The contract suite checks the name rules and the line-break and control-character rules for values. A tab is valid RFC 5322 and passes this check, but SES rejects it in a value (@wraps.dev/email before sending, SES v2 by its [ -~]* value pattern), so wrapsMailer and sesMailer fail such a send with a PermanentMailError too.
wrapsMailer and sesMailer send a display name in from that has non-ASCII characters as RFC 2047 encoded-words (Société <help@acme.com> goes out as =?UTF-8?B?U29jacOpdMOp?= <help@acme.com>), as the SES v1 SendEmail reference asks for Source, and quote an ASCII name that needs it (Acme, Inc.). An ASCII name that already holds an encoded-word is sent as given. The address itself is sent as given.
Result
send resolves to { id, messageId? }.
idis the provider’s id for the send when the provider reports one. When it doesn’t,emailSdkMailermakes up"<adapter>:<uuid>", andsesMailerandwrapsMailermake up"ses:<uuid>", rather than throwing, which would send the message again.messageIdis the RFC 5322Message-IDactually sent, with angle brackets, only when the adapter knows it:wrapsMailerandsesMailerin us-east-1. See Wraps.