← All @molecule/* packages · App templates
@molecule/api-emails-resendProvider bond · emails · API (Node) · v1.0.0 · Apache-2.0
Resend transactional email provider
npm install @molecule/api-emails-resendnpm · Source on GitHub · Implements @molecule/api-emails
@molecule/api-emails-resend is a provider bond on the API (Node) side: it implements the emails core interface (@molecule/api-emails) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setTransport } from '@molecule/api-emails'
import { provider } from '@molecule/api-emails-resend'
setTransport(provider)Works with: @molecule/api-bond, @molecule/api-emails, @molecule/api-secrets
Secrets: RESEND_API_KEY, RESEND_FROM (optional)
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Resend email provider for molecule.dev.
import { setTransport } from '@molecule/api-emails'
import { provider } from '@molecule/api-emails-resend'
setTransport(provider)
provider
npm install @molecule/api-emails-resend @molecule/api-bond @molecule/api-emails @molecule/api-secrets
EmailMessageEmail message options.
interface EmailMessage {
/**
* Sender address.
*/
from: string | EmailAddress
/**
* Recipient(s).
*/
to: string | EmailAddress | (string | EmailAddress)[]
/**
* CC recipient(s).
*/
cc?: string | EmailAddress | (string | EmailAddress)[]
/**
* BCC recipient(s).
*/
bcc?: string | EmailAddress | (string | EmailAddress)[]
/**
* Reply-to address.
*/
replyTo?: string | EmailAddress
/**
* Email subject.
*/
subject: string
/**
* Plain text body.
*/
text?: string
/**
* HTML body.
*/
html?: string
/**
* File attachments.
*/
attachments?: EmailAttachment[]
/**
* i18n key for the subject (for client-side translation).
*/
subjectKey?: string
/**
* i18n key for the plain text body (for client-side translation).
*/
textKey?: string
/**
* i18n key for the HTML body (for client-side translation).
*/
htmlKey?: string
}
EmailSendResultResult of sending an email.
interface EmailSendResult {
/**
* Whether the email was accepted for delivery.
*/
accepted: string[]
/**
* Addresses that were rejected.
*/
rejected: string[]
/**
* Message ID from the provider.
*/
messageId?: string
/**
* Raw response from the provider.
*/
response?: string
}
EmailTransportEmail transport interface.
All email providers must implement this interface.
interface EmailTransport {
/**
* Sends an email message.
* @returns The send result.
*/
sendMail(message: EmailMessage): Promise<EmailSendResult>
}
ResendAttachmentOne entry of a send request's attachments[], exactly as the Resend REST API
expects it: base64 content plus snake_case metadata.
interface ResendAttachment {
/** File content, base64-encoded. */
content: string
/** File name shown to the recipient; Resend derives the MIME type from it when `content_type` is unset. */
filename: string
/** MIME type of the file. */
content_type?: string
/** Content-ID for an inline image referenced as `cid:` in the HTML body. */
content_id?: string
}
ResendClientThe narrow HTTP client this bond uses to reach Resend.
interface ResendClient {
/**
* Sends one email via `POST /emails`.
* @returns The HTTP status and the message id.
*/
send(request: ResendSendRequest): Promise<ResendSendResponse>
}
ResendErrorBodyResend's JSON error body ({ statusCode, message, name }), with every field
optional because the wire is not trusted.
interface ResendErrorBody {
/** HTTP status Resend reports in the body (may be `null`). */
statusCode?: number | null
/** Human-readable error message. */
message?: string
/** Machine-readable error name, e.g. `validation_error`, `daily_quota_exceeded`. */
name?: string
}
ResendSendRequestRequest body for POST /emails. Field names are the REST API's own
(snake_case, e.g. reply_to) — NOT the camelCase the official SDK accepts.
interface ResendSendRequest {
/** Sender, as a bare address or `Name <address>`. */
from: string
/** Recipients (Resend accepts at most 50). */
to: string[]
/** Subject line. */
subject: string
/** CC recipients. */
cc?: string[]
/** BCC recipients. */
bcc?: string[]
/** Reply-To address. */
reply_to?: string
/** HTML body. */
html?: string
/** Plain-text body (Resend derives one from `html` when omitted). */
text?: string
/** File attachments. */
attachments?: ResendAttachment[]
}
ResendSendResponseWhat {@link ResendClient.send} resolves with: the HTTP status of the accepted
request and the message id Resend returned in the response body.
interface ResendSendResponse {
/** HTTP status code of the response (2xx). */
status: number
/** The `id` from the response body, when present. */
id?: string
}
ResendApiErrorA non-2xx response from the Resend API.
status is the HTTP status, code is Resend's machine-readable error name
(e.g. validation_error, rate_limit_exceeded, daily_quota_exceeded) when
the body carried one, and body is the parsed error body. Deliberately NOT
tagged with statusCode / errorKey: Resend's status describes OUR request
to Resend (a 401 is a bad key, a 403 an unverified domain), not the caller's
request to the app, so the API middleware must fall through to its generic
500 rather than echo it.
getClient()Returns the Resend HTTP client. Nothing is configured up front: the API key
and base URL are read from the environment on every send().
function getClient(): ResendClient
Returns: The Resend client.
sendMail(message)Sends an email through the Resend API.
function sendMail(message: EmailMessage): Promise<EmailSendResult>
message — The email message (to, from, subject, text/html, attachments).Returns: Send result with accepted addresses, message ID, and status code.
emailsResendSecretDefinitionsSecret definitions required by the Resend email bond.
const emailsResendSecretDefinitions: SecretDefinition[]
providerThe Resend email provider implementing the standard interface.
const provider: EmailTransport
RESEND_DEFAULT_BASE_URLResend's REST API base URL. Override with RESEND_BASE_URL.
const RESEND_DEFAULT_BASE_URL: 'https://api.resend.com'
Implements @molecule/api-emails interface.
Setup function to register this provider with the core interface:
import { setTransport } from '@molecule/api-emails'
import { provider } from '@molecule/api-emails-resend'
export function setupEmailsResend(): void {
setTransport(provider)
}
Peer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-emails ^1.0.1@molecule/api-secrets ^1.0.1RESEND_API_KEY (required) — Resend API key
re_...RESEND_FROM (optional) — Default sender
Acme <hello@example.com>@molecule/api-bond
@molecule/api-emails
@molecule/api-secrets
Zero dependencies — talks to Resend's REST API with the runtime's global
fetch (POST https://api.resend.com/emails), so every app that installs
this bond stays dependency-free; the official resend SDK can be swapped in
later without changing the bond's interface.
from MUST be on a domain you VERIFIED in Resend (Resend → Domains, DNS
records added and checked), or the API rejects the send with a 403
validation_error ("domain is not verified"). Before any domain is verified
the only usable sender is onboarding@resend.dev — testing only, and it can
deliver ONLY to the account owner's own address. Read the sender from config,
never hardcode a placeholder: set RESEND_FROM (e.g.
Acme <no-reply@your-verified-domain.com>) and it is used whenever a
message's from is empty; if both are missing, sendMail() throws a tagged
config-missing error naming RESEND_FROM.
Configuration is lazy and env-driven: RESEND_API_KEY (and the optional
RESEND_BASE_URL override for brokers / compatible endpoints) are read on
EACH send — never at import time — so a key resolved into process.env
after import (late secrets resolution via a secrets bond) is honored. If the
key is absent at send time, sendMail() throws a tagged config-missing
error (clean 503 / config.notConfigured) naming RESEND_API_KEY — never
an opaque Resend 401.
There is NO sandbox / test-mode flag in the Resend API (nothing like
SendGrid's sandboxMode). Every accepted request is a real send that counts
against the account's quota — including sends to the simulation recipients
delivered@resend.dev, bounced@resend.dev and complained@resend.dev,
which are the supported way to exercise the delivery / bounce / spam paths
without emailing a real inbox.
Attachments are sent base64-encoded inline; Resend caps the whole email
at 40 MB after encoding (larger sends are rejected). Buffer / string
content only — a stream attachment throws. A cid becomes Resend's
content_id for inline images (<img src="cid:...">).
API errors surface as ResendApiError carrying status (HTTP) and
code (Resend's error name — validation_error, rate_limit_exceeded,
daily_quota_exceeded, …). They are deliberately NOT tagged with
statusCode / errorKey, so the API middleware returns its generic 500
instead of echoing Resend's status to the caller. On success accepted
echoes every to recipient (Resend returns no per-recipient verdict) and
messageId is the id from the response body.
Integration checklist — drive the real UI (live preview, no mocks). The
sandbox CAPTURES outbound email instead of sending — read each message with
the read_activity tool (filter type 'email'); the verification/reset link
is in its payload. Never mock the send or modify production code to expose
it. Adapt each item to this app's actual screens/flows, and check every box
off one by one. A box you can't check is an integration bug to fix — not a
skip:
undefined placeholders).