Resend

Resend is the email provider for campaigns, sequence emails, manual replies and the send_email MCP tool.

Client

lib/plugins/resend-client.ts — ResendEmailClient

Topics

lib/services/resend-topic-service.ts

Webhooks

POST /api/webhooks/resend, dispatched by lib/events/resend-event-handler.ts

Configuration

RESEND_API_KEY=
RESEND_WEBHOOK_SECRET=   # Svix verification; when unset, verification is skipped

Client surface

Method Purpose

listDomains()

Verified domains, exposed at GET /api/email-domains/resend-domains

send(options)

One-off transactional send

sendCampaign(options)

Campaign send with template, topic, variables and unsubscribe headers

upsertContact(…​)

Create or update a Resend contact for topic membership

createVariationTemplate(…​)

Create a Resend template from an EmailVariation

updateVariationTemplate(…​)

Keep the Resend template in sync

getEmailContentById(emailId)

Fetch a sent message’s content

Sending model

Resend templates are the canonical content store. An EmailVariation holds the subject and HTML in the database and a resendTemplateId, kept in sync on create and update. templateType is always "custom".

sendCampaign receives:

Field Source

from

"<fromName> <fromEmail>" from the selected EmailSendingDomain

replyTo

buildReplyToForSendingDomain({ fromEmail, fromName })

to

The recipient address

subject, html

The effective variation

resendTemplateId

The variation’s Resend template, when present

topicId

Campaign.resendTopicId

templateVariableValues

Resolved from contact fields with per-variable fallbacks

unsubscribeUrl

/api/unsubscribe?token=<base64url targetId>

UNSUBSCRIBE_LINK is always included and applied last, so no user-defined variable can overwrite it. Resend substitutes {{{UNSUBSCRIBE_LINK}}} in both template and raw-HTML sends.

Topics

A topic segments an audience and gives Resend-managed unsubscribe alongside the application’s own. createResendTopicForCampaign(name, campaignId) provisions one; the id is stored as Campaign.resendTopicId. Duplicating a campaign creates a fresh topic rather than sharing the source’s, so the copies' audiences stay independent.

upsertContact registers the recipient before sending, which is what makes topic membership and platform-level unsubscribe work.

Unsubscribe headers (RFC 8058)

Every campaign email carries List-Unsubscribe and List-Unsubscribe-Post pointing at /api/unsubscribe, plus an in-body link using the same URL. One-click opt-out sets TargetEmails.newsletterOptOut, suppressing the address across every campaign in the workspace, and publishes Contact.Unsubscribe to the CRM queue.

Webhooks

Ten event types are handled: email.sent, email.delivered, email.delivery_delayed, email.opened, email.clicked, email.bounced, email.complained, email.suppressed, email.unsubscribed, email.received. The route verifies the Svix signature when the secret is set, persists the raw event idempotently on the svix-id header, and dispatches through ResendWebhookDispatcher.

See Resend email events for per-handler effects and inbound reply correlation.

Domain reputation

Two levels of sender configuration, with independent daily caps: Domain (a Resend-verified domain) and EmailSendingDomain (a concrete From address). Each Resend subdomain is its own verified domain, so it becomes its own Domain row rather than a child. See Email deliverability.

Per-account counters (sentCount, receivedCount, openedCount, clickedCount, bouncedCount, unsubscribedCount) are maintained by the webhook handlers and are the practical signal for spotting an account that is damaging the domain’s reputation.

Failure behaviour

  • No RESEND_API_KEY — sends fail; the emailSend workflow retries twice, then the target is left for a later tick.

  • No RESEND_WEBHOOK_SECRET — signature verification is skipped. Acceptable in development, not in production.

  • A Resend outage leaves targets in pending with their queue lock expiring, so they are re-leased automatically once the provider recovers.