Skip to content

Resend

[endpoints.transport]
type = "resend"
[endpoints.transport.settings]
api_key = "${env.RESEND_API_KEY}"
SettingRequiredDescription
api_keyyesResend API key. Found at resend.com/api-keys.
base_urlnoOverride the API endpoint. Test-only escape hatch.
AspectBehavior
API endpointPOST https://api.resend.com/emails
AuthenticationAuthorization: Bearer <api_key>
Body formatJSON (Resend’s structured fields: from, to, subject, text, reply_to)
Per-request timeout5s (transport-level)
Connection reuseShared *http.Client per process
transport_message_idParsed from response id field
Resend responseError classRetry?
200 OK(success)no
429 Too Many RequestsErrRateLimitedyes, after 5s
5xx server errorErrTransientyes, after 1s
Network timeout / connection refusedErrTransientyes, after 1s
4xx other than 429 (400, 401, 403, 422)ErrTerminalno

Resend’s error body shape is {"statusCode": N, "message": "...", "name": "ErrorType"}. Posthorn surfaces the message field in the terminal-failure log line and in 502 response detail (operator-visible) when the operator’s logging level surfaces it.

Same shape as any HTTP-API transport:

  • SPF authorizing Resend (include:_spf.resend.com per Resend’s wizard)
  • DKIM record from Resend’s domain-verification flow
  • Optional DMARC alignment
SymptomLikely causeFix
HTTP 422 The from address does not match a verified domainSender domain not verified in Resendresend.com/domains → verify the domain
HTTP 403 early in adoptionResend’s anti-abuse holds new accounts; sandbox-style restrictionsContact Resend support to release production sending
Messages route to recipient’s spamDKIM not deployed yetDNS propagation can take an hour; Resend’s verifier confirms when it’s live