All Docs
FeaturesNurtureHubUpdated March 20, 2026

Resend Webhook Integration & SVIX Signature Verification

Resend Webhook Integration & SVIX Signature Verification

Available from: v1.0.25

NurtureHub receives real-time email delivery events from Resend via a webhook endpoint at /api/webhooks/resend. This powers live delivery confirmation, bounce suppression, consent management, and engagement-based intent scoring — without polling or manual reconciliation.


How It Works

When NurtureHub sends an email via Resend, it embeds a set of tags into the message at send time. When Resend fires a delivery event (delivered, bounced, opened, clicked, etc.), those tags travel back with the event payload, allowing the webhook handler to correlate it precisely with the right tenant, contact, journey, and scheduled email record.

The webhook handler then dispatches the event into the Inngest processing pipeline, where the appropriate function handles the update idempotently.


Supported Events

Resend EventInngest Event FiredEffect
email.deliveredemail/delivery.confirmedscheduledEmail marked sent; deliveredAt recorded; audit log written
email.bouncedemail/bounce.receivedContact email suppressed; journey paused; agent notified
email.complainedemail/bounce.received (bounceType=spam_complaint)Contact email suppressed; consent set to denied; agent notified
email.openedemail/engagement.received (eventType=opened)Engagement event written; intent score recalculation triggered
email.link.clickedemail/engagement.received (eventType=clicked)Engagement event written with clicked URL; booking-link detection adds 30pt intent weight
email.delivery_delayed(none)Warning logged; Resend retries automatically

Booking-Link Detection

When a recipient clicks a link, the handler checks whether the URL matches the pattern /book|valuat|appoint|calendly/i. If it does, a 30-point intent weight bonus is applied during scoring — surfacing the contact as a hot lead faster than a standard click.


NurtureHub Tag Schema

The following tags are embedded in every outbound Resend email and read back on every inbound webhook event:

Tag KeyDescription
nh_tenant_idThe NurtureHub agency tenant ID
nh_contact_idThe CRM contact being nurtured
nh_scheduled_email_idThe specific scheduled send record
nh_journey_idThe nurture journey this email belongs to
nh_journey_email_idThe individual journey email step

Events arriving without these tags are logged and skipped — they do not cause errors or trigger retries.


Signature Verification

Resend delivers webhooks via SVIX. The handler verifies every inbound request using HMAC-SHA256 before processing:

  • Algorithm: HMAC-SHA256 over ${svix-id}.${svix-timestamp}.${raw-body}
  • Runtime: Web Crypto SubtleCrypto API — compatible with Next.js Node.js and Edge runtimes
  • Replay protection: Requests with a timestamp older than 5 minutes are rejected
  • Multi-signature support: All v1,<sig> entries in the svix-signature header are checked; verification passes if any entry matches
  • Dev mode: If RESEND_WEBHOOK_SECRET is not set, signature verification is skipped with a warning — useful for local development

Required Headers

svix-id: <unique message ID>
svix-timestamp: <unix timestamp in seconds>
svix-signature: v1,<base64-encoded-signature> [v1,<additional-sig> ...]

Requests missing any of these headers when a secret is configured are rejected with 401 Unauthorized.


Configuration

1. Obtain your webhook secret

Go to https://resend.com/webhooks and create a new webhook endpoint pointing at:

https://<your-domain>/api/webhooks/resend

Copy the signing secret (it will be prefixed with whsec_).

2. Set the environment variable

RESEND_WEBHOOK_SECRET=whsec_<your-secret>

3. Select events to subscribe to

Subscribe to all six event types in the Resend dashboard:

  • email.delivered
  • email.bounced
  • email.complained
  • email.opened
  • email.link.clicked
  • email.delivery_delayed

Idempotency

The resendDeliveryConfirmed Inngest function is fully idempotent. If a email/delivery.confirmed event is received more than once for the same record (e.g. due to a Resend retry), the function detects that the record is already in sent or cancelled state and exits cleanly without writing duplicate data.


Error Handling

The webhook endpoint always returns HTTP 200 regardless of internal processing errors. This is intentional — returning 4xx or 5xx would cause Resend to retry the event, potentially creating duplicate processing. All processing errors are logged for observability without triggering retry storms.


Parallel Tracking Paths

Engagement events (opens and clicks) can reach NurtureHub via two parallel paths:

  1. Pixel / redirect tracking — the existing path using tracking pixels and redirect links embedded in emails.
  2. Resend webhook — the new path added in v1.0.25.

Both paths feed into the same email-engagement-processor Inngest function. The webhook path uses a synthetic tokenId format (resend_open_<msgId>, resend_click_<msgId>) to distinguish its events from the pixel/redirect path.