@alimzen/relayo-resend
v0.1.0
Published
Resend driver for Relayo: sending through the Resend API and normalized events from its signed webhooks.
Maintainers
Readme
@alimzen/relayo-resend
Resend driver for Relayo: sends through the Resend API and turns its signed webhooks into Relayo events. No SDK, no runtime dependencies.
npm install @alimzen/relayo @alimzen/relayo-resendSending
import { createRelayo } from '@alimzen/relayo';
import { resend } from '@alimzen/relayo-resend';
const relayo = createRelayo({
email: resend({ apiKey: process.env.RESEND_API_KEY!, webhook: { secret: process.env.RESEND_WEBHOOK_SECRET! } }),
});
await relayo.email.send({ ...message, idempotencyKey: `invoice-${invoice.id}` });| Option | |
| --- | --- |
| apiKey | Required |
| webhook.secret | Signing secret of the webhook (whsec_...) |
| webhook.toleranceSeconds | Reject signatures older than this (default 300; 0 turns the check off) |
| webhook.verifySignature | Set to false to accept unsigned webhooks |
| timeoutMs, baseUrl, fetch, name | Request timeout (default 30 000), endpoint and fetch overrides, provider name (default "resend") |
idempotencyKeyis sent asIdempotency-Key: Resend won't send the same message twice within 24 hours.- Relayo's
messageIdand yourmetadatatravel as Resend tags, which come back on every webhook. Tags only allowA-Z a-z 0-9 _ -and 256 characters, sometadatais encoded (base64url JSON) and must stay small; larger metadata is refused with aValidationErrorbefore calling Resend. - Message
tagsbecome Resend tags ({ name: tag, value: 'true' }). providerOptionsare merged into the request body (e.g.{ scheduled_at: 'in 1 hour', topic_id }).
| Resend answer | Relayo error | Retryable |
| --- | --- | --- |
| 401, 403 (bad key, unverified domain, testing mode) | AuthenticationError | no |
| 429 rate_limit_exceeded, daily_quota_exceeded, monthly_quota_exceeded | RateLimitError (retryAfter from the headers, or until midnight UTC for the daily quota) | yes |
| 409 concurrent_idempotent_requests, resource_locked; 5xx; network errors | ProviderUnavailableError | yes |
| Other 4xx (400, 409 invalid_idempotent_request, 422) | ValidationError | no |
Receiving events
Add a webhook in the Resend dashboard pointing at your endpoint, copy its signing secret into
webhook.secret, and pass each request with its raw body to relayo.handleWebhook('email', req).
Resend signs with Svix (HMAC-SHA256 of svix-id.svix-timestamp.body).
| Resend event | Relayo event |
| --- | --- |
| email.sent | accepted |
| email.delivered | delivered |
| email.delivery_delayed | deferred |
| email.bounced | bounced; suppression-list bounces become rejected (suppressed) |
| email.suppressed | rejected (suppressed) |
| email.complained | complained |
| email.opened / email.clicked | opened / clicked (with url) |
| email.failed | failed |
Other events (email.scheduled, domain.*, contact.*...) are ignored. Event ids derive from svix-id,
which Resend keeps on retries.
