Skip to content

Resend Integration

Receive and route Resend webhooks for email delivery, engagement, and bounce events.

Setup

1. Create a Source in Hookbase

Resend uses Svix under the hood for webhook delivery, which signs payloads using HMAC-SHA256 with a base64-encoded signature in the webhook-signature header.

bash
curl -X POST https://api.hookbase.app/api/sources \
  -H "Authorization: Bearer whr_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Resend Production",
    "slug": "resend",
    "verificationConfig": {
      "type": "svix",
      "secret": "whsec_..."
    }
  }'

Save your webhook URL:

https://api.hookbase.app/ingest/{orgSlug}/resend

2. Configure Resend Webhook

  1. Go to Resend Dashboard → Webhooks
  2. Click Add webhook
  3. Enter your Hookbase ingest URL
  4. Select the events you want to receive
  5. Click Create
  6. Copy the Signing secret (starts with whsec_) and update your Hookbase source

3. Create Routes

bash
# Create destination for your email event handler
curl -X POST https://api.hookbase.app/api/destinations \
  -H "Authorization: Bearer whr_your_api_key" \
  -d '{"name": "Email Events Handler", "url": "https://api.myapp.com/webhooks/resend"}'

# Create route
curl -X POST https://api.hookbase.app/api/routes \
  -H "Authorization: Bearer whr_your_api_key" \
  -d '{"name": "Resend to Email Handler", "sourceId": "src_...", "destinationIds": ["dst_..."]}'

Signature Verification

Resend uses Svix for webhook delivery. The signature is sent across three headers:

HeaderDescription
webhook-idUnique message ID
webhook-timestampUnix timestamp (seconds)
webhook-signaturev1,<base64-encoded-signature>

Hookbase verifies Svix signatures automatically:

json
{
  "verificationConfig": {
    "type": "svix",
    "secret": "whsec_..."
  }
}

Timestamp Tolerance

Svix signatures include a timestamp. Hookbase rejects webhooks older than 5 minutes by default to prevent replay attacks.

Common Events

email.sent

Triggered when an email is accepted by Resend for delivery.

json
{
  "type": "email.sent",
  "created_at": "2026-03-07T12:00:00.000Z",
  "data": {
    "email_id": "em_1234567890",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your order has been confirmed",
    "created_at": "2026-03-07T12:00:00.000Z"
  }
}

email.delivered

Triggered when an email is successfully delivered to the recipient's mail server.

json
{
  "type": "email.delivered",
  "created_at": "2026-03-07T12:00:02.000Z",
  "data": {
    "email_id": "em_1234567890",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your order has been confirmed",
    "created_at": "2026-03-07T12:00:00.000Z"
  }
}

email.opened

Triggered when a recipient opens the email (requires open tracking enabled).

json
{
  "type": "email.opened",
  "created_at": "2026-03-07T12:05:00.000Z",
  "data": {
    "email_id": "em_1234567890",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your order has been confirmed",
    "created_at": "2026-03-07T12:00:00.000Z"
  }
}

email.clicked

Triggered when a recipient clicks a link in the email (requires click tracking enabled).

json
{
  "type": "email.clicked",
  "created_at": "2026-03-07T12:06:00.000Z",
  "data": {
    "email_id": "em_1234567890",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your order has been confirmed",
    "click": {
      "link": "https://myapp.com/orders/12345",
      "timestamp": "2026-03-07T12:06:00.000Z"
    },
    "created_at": "2026-03-07T12:00:00.000Z"
  }
}

email.bounced

Triggered when an email bounces (permanent delivery failure).

json
{
  "type": "email.bounced",
  "created_at": "2026-03-07T12:00:03.000Z",
  "data": {
    "email_id": "em_1234567890",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your order has been confirmed",
    "bounce": {
      "message": "550 5.1.1 The email account that you tried to reach does not exist."
    },
    "created_at": "2026-03-07T12:00:00.000Z"
  }
}

email.complained

Triggered when a recipient marks the email as spam.

json
{
  "type": "email.complained",
  "created_at": "2026-03-07T14:00:00.000Z",
  "data": {
    "email_id": "em_1234567890",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Your order has been confirmed",
    "created_at": "2026-03-07T12:00:00.000Z"
  }
}

Transform Examples

Slack Alert for Bounces and Complaints

javascript
function transform(payload) {
  const data = payload.data;
  const isBounce = payload.type === 'email.bounced';
  const title = isBounce ? 'Email Bounced' : 'Spam Complaint';
  const color = isBounce ? 'warning' : 'danger';

  return {
    attachments: [{
      color: color,
      title: title,
      fields: [
        { title: "To", value: data.to.join(', '), short: true },
        { title: "Subject", value: data.subject, short: true },
        { title: "From", value: data.from, short: true },
        { title: "Email ID", value: data.email_id, short: true }
      ],
      footer: isBounce && data.bounce ? data.bounce.message : "Marked as spam by recipient",
      ts: Math.floor(new Date(payload.created_at).getTime() / 1000)
    }]
  };
}

Email Analytics Format

javascript
function transform(payload) {
  const data = payload.data;

  return {
    email_id: data.email_id,
    event_type: payload.type.replace('email.', ''),
    from: data.from,
    to: data.to,
    subject: data.subject,
    link_clicked: data.click ? data.click.link : null,
    bounce_message: data.bounce ? data.bounce.message : null,
    event_at: payload.created_at,
    sent_at: data.created_at
  };
}

Filter Examples

Delivery Failures Only

json
{
  "name": "Delivery Failures",
  "logic": "or",
  "conditions": [
    {
      "field": "type",
      "operator": "equals",
      "value": "email.bounced"
    },
    {
      "field": "type",
      "operator": "equals",
      "value": "email.complained"
    }
  ]
}

Engagement Events

json
{
  "name": "Engagement Events",
  "logic": "or",
  "conditions": [
    {
      "field": "type",
      "operator": "equals",
      "value": "email.opened"
    },
    {
      "field": "type",
      "operator": "equals",
      "value": "email.clicked"
    }
  ]
}

Specific Sender

json
{
  "name": "Transactional Emails",
  "logic": "and",
  "conditions": [
    {
      "field": "data.from",
      "operator": "equals",
      "value": "[email protected]"
    }
  ]
}

Headers

Resend (Svix) webhooks include these headers:

HeaderDescription
webhook-idUnique message identifier
webhook-timestampUnix timestamp (seconds)
webhook-signaturev1,<base64-signature>
Content-Typeapplication/json
User-AgentSvix-Webhooks

Troubleshooting

Signature Verification Failed

  1. Verify you're using the webhook signing secret (starts with whsec_)
  2. Ensure verification type is set to svix, not hmac
  3. Check that the secret matches what's shown in the Resend dashboard

Missing Events

  1. Check which events are selected in Resend's webhook settings
  2. Verify your sending domain is properly configured (SPF, DKIM)
  3. Check Resend's webhook logs for delivery attempts

No Open/Click Events

  1. Open and click tracking must be enabled in your Resend domain settings
  2. HTML emails are required for tracking — plain text emails cannot be tracked
  3. Some email clients block tracking pixels

Released under the MIT License.