Resend
Fromenance is a communication provenance platform, and the Resend adapter turns Resend’s email.sent and email.delivered events into registrations without changing your application code. Resend exposes the rendered body through its API, so registrations made this way carry a full content fingerprint.
By the end you will have a Resend source with a webhook URL and secret, the Resend webhook pointed at it, the code placed in your template, and events showing on the source’s health panel.
1. Create the source
Section titled “1. Create the source”Admin app: Sources, Add source, kind Resend. Or from the API with an admin key:
curl -X POST https://api.fromenance.com/v1/sending-sources \ -H "Authorization: Bearer fr_live_..." \ -H "Content-Type: application/json" \ -d '{"name":"Resend transactional","kind":"resend","config":{"api_key":"re_..."},"expected_daily_volume":20000,"silence_alert_minutes":120}'The response includes webhook_url (https://api.fromenance.com/v1/hooks/esp/src_...) and secret, shown once. config.api_key is optional: a Resend API key with read access lets Fromenance fetch the rendered body of each sent email (GET https://api.resend.com/emails/{id}) so the registration gets a fingerprint. It is encrypted at rest with your tenant key.
2. Configure the webhook at Resend
Section titled “2. Configure the webhook at Resend”- Resend dashboard, Webhooks, Add webhook.
- Endpoint URL: the
webhook_urlfrom step 1. - Events: email.sent and email.delivered. Other event types are ignored.
- Save, then copy the Signing secret Resend shows (
whsec_...) and paste it into the source in the admin app (Sources, the source, Signing secret). The secret Fromenance generated at creation is replaced by Resend’s, because Resend signs with its own key.
What Resend sends and how it is verified
Section titled “What Resend sends and how it is verified”Resend signs webhooks through Svix. Each request carries svix-id, svix-timestamp, and svix-signature; Fromenance verifies the HMAC-SHA256 over id.timestamp.body with the signing secret and rejects requests outside the timestamp tolerance. A failed check returns 401 and increments signature_failures on the source.
From each event Fromenance reads, per recipient in data.to:
| Registration field | Resend field |
|---|---|
message_id |
data.email_id |
| recipient (hashed immediately, never stored) | each address in data.to |
sent_at |
data.created_at |
from_address |
data.from |
| subject fingerprint | data.subject |
| verify code | tag fromenance_code, or the X-Fromenance-Code header when Resend echoes headers |
template_id, campaign_id |
tags template_id, campaign_id |
| content fingerprint | fetched through the Messages API when config.api_key is set |
3. Place the code
Section titled “3. Place the code”The adapter does not require a reservation: a send event without a code registers the message and issues a code. But a code that is not in the footer cannot help the customer, so for Resend you have two options.
Option A, reserve before send (recommended). Call POST /v1/communications/reserve with the recipient, place the code in the template, and send with the tag so the event completes the reservation:
await resend.emails.send({ from: "alerts@northfieldbank.example", to: [customer.email], subject: "A new device signed in to your account", html: render(template, { fromenance_code: reserved.verify_code }), tags: [ { name: "fromenance_code", value: reserved.verify_code }, { name: "template_id", value: "new-device-alert-v2" }, ],});Option B, register with the SDK and skip the webhook. If you already render the email in your code, @fromenance/sdk register() gives you the code before send with no webhook at all. Use the webhook when sends happen in systems you do not control.
4. Confirm
Section titled “4. Confirm”- Sources, the source, Send test event (
POST /v1/sending-sources/{id}/test-event) runs a synthetic sent event through the adapter and creates a sandbox registration. - Send one real message. Within seconds
health.last_event_atupdates andregistered_todayincrements.
Source health
Section titled “Source health”| Field | Meaning |
|---|---|
last_event_at, events_last_hour |
Webhook traffic |
signature_failures |
Requests rejected for a bad Svix signature. A non zero and rising count means the secret in Fromenance does not match Resend |
reserved, completed, reserve_to_complete_ratio |
Reservations versus sends that completed them |
coverage_gaps |
Reservations that expired without a send event |
silent |
No event within silence_alert_minutes (default 1440) while the source is active. Raises an alert in the admin app |
Rotate the secret with POST /v1/sending-sources/{id}/rotate-secret if it leaks; Resend’s own signing secret is replaced from the Resend dashboard.