Cette page est affichée en anglais. Une traduction française est en cours.
SkillsMay 11, 2026·4 min de lecture

Resend Webhooks — Email Delivery Events for AI Agents

Resend webhooks fire on delivery, open, click, bounce, complaint. Route to your agent for follow-ups, deliverability monitoring, cleanup.

Resend
Resend · Community
Prêt pour agents

Cet actif peut être lu et installé directement par les agents

TokRepo expose une commande CLI universelle, un contrat d'installation, le metadata JSON, un plan selon l'adaptateur et le contenu raw pour aider les agents à juger l'adaptation, le risque et les prochaines actions.

Stage only · 17/100Stage only
Surface agent
Tout agent MCP/CLI
Type
Skill
Installation
Stage only
Confiance
Confiance : New
Point d'entrée
Asset
Commande CLI universelle
npx tokrepo install e60ca183-5be1-4d16-ae96-2b7b3bedba46
Introduction

Resend fires webhooks for every email lifecycle event — delivered, opened, clicked, bounced, complained, delivery_delayed. Wire them to your AI agent and you can trigger automated follow-ups when an email opens, suppress recipients on hard bounce, or alert when complaint rates spike. Best for: cold outreach agents, transactional pipelines that need bounce hygiene, marketing automations with engagement-based branching. Works with: any HTTPS endpoint, Resend ≥ 2024. Setup time: 15 minutes.


FastAPI webhook endpoint

import os, hmac, hashlib, json
from fastapi import FastAPI, Request, HTTPException
from anthropic import Anthropic

app = FastAPI()
SIGNING_SECRET = os.environ["RESEND_WEBHOOK_SECRET"].encode()
claude = Anthropic()

def verify(payload: bytes, sig_header: str) -> bool:
    expected = hmac.new(SIGNING_SECRET, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig_header)

EVENT_HANDLERS = {
    "email.opened":    "schedule_followup",
    "email.clicked":   "log_engagement",
    "email.bounced":   "suppress_recipient",
    "email.complained":"alert_compliance",
}

@app.post("/resend/webhook")
async def resend_webhook(request: Request):
    payload = await request.body()
    sig = request.headers.get("svix-signature") or ""
    if not verify(payload, sig.split(",")[-1]):
        raise HTTPException(401, "bad signature")

    event = json.loads(payload)
    handler = EVENT_HANDLERS.get(event["type"])
    if not handler:
        return {"status": "ignored"}

    # Hand off to Claude for next-step decision
    claude.messages.create(
        model="claude-3-5-haiku-20241022",
        max_tokens=512,
        tools=[{"name": handler, "input_schema": {"type": "object", "properties": {
            "to": {"type": "string"}, "subject": {"type": "string"}, "campaign_id": {"type": "string"},
        }}}],
        messages=[{"role": "user", "content": f"Resend event: {event['type']}\n{event['data']}"}],
    )
    return {"status": "handled"}

Configure the webhook

In Resend dashboard → Webhooks → Add endpoint:

  • URL: https://your-app.com/resend/webhook
  • Events: select email.* (delivered, opened, clicked, bounced, complained, delivery_delayed)
  • Save and copy the signing secret → put in RESEND_WEBHOOK_SECRET env var

Event payload structure

{
  "type": "email.opened",
  "created_at": "2026-05-11T14:23:00Z",
  "data": {
    "email_id": "8d22a0b1-...-...-...",
    "to": ["user@example.com"],
    "from": "TokRepo <hi@tokrepo.com>",
    "subject": "Welcome to TokRepo",
    "tags": [{"name": "campaign", "value": "welcome-2026-05"}]
  }
}

Production hygiene

  • Hard bounces → mark recipient suppressed immediately. Sending again hurts your domain reputation.
  • Complaints → suppress AND lower send volume for the source campaign.
  • Delivery delays → usually transient. Alert only on >24h delay or repeating recipients.
  • Idempotency → store event['data']['email_id'] + event['type'] to dedupe retries.

FAQ

Q: Open tracking — is it accurate? A: Opens fire on tracking-pixel load. Image-blocking clients (Apple Mail Privacy Protection, many corporate clients) inflate or null open counts. Use opens as a directional signal, not a precise metric. Clicks are far more reliable.

Q: Why use webhooks vs polling the API? A: Webhooks are real-time; polling burns rate limit and adds latency. The Resend Emails API is fine for status-on-demand (emails.retrieve) but webhooks are the right primitive for event-driven agent flows.

Q: How do I test webhooks locally? A: Use ngrok http 8000 to tunnel; paste the ngrok URL into Resend webhook config. Resend dashboard has a 'Send test event' button to replay payloads without sending real email.


Quick Use

  1. Add endpoint at resend.com/webhooks, select email.* events, copy signing secret
  2. Build /resend/webhook handler that verifies svix-signature
  3. Route by event.type to suppress / followup / alert

Intro

Resend fires webhooks for every email lifecycle event — delivered, opened, clicked, bounced, complained, delivery_delayed. Wire them to your AI agent and you can trigger automated follow-ups when an email opens, suppress recipients on hard bounce, or alert when complaint rates spike. Best for: cold outreach agents, transactional pipelines that need bounce hygiene, marketing automations with engagement-based branching. Works with: any HTTPS endpoint, Resend ≥ 2024. Setup time: 15 minutes.


FastAPI webhook endpoint

import os, hmac, hashlib, json
from fastapi import FastAPI, Request, HTTPException
from anthropic import Anthropic

app = FastAPI()
SIGNING_SECRET = os.environ["RESEND_WEBHOOK_SECRET"].encode()
claude = Anthropic()

def verify(payload: bytes, sig_header: str) -> bool:
    expected = hmac.new(SIGNING_SECRET, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig_header)

EVENT_HANDLERS = {
    "email.opened":    "schedule_followup",
    "email.clicked":   "log_engagement",
    "email.bounced":   "suppress_recipient",
    "email.complained":"alert_compliance",
}

@app.post("/resend/webhook")
async def resend_webhook(request: Request):
    payload = await request.body()
    sig = request.headers.get("svix-signature") or ""
    if not verify(payload, sig.split(",")[-1]):
        raise HTTPException(401, "bad signature")

    event = json.loads(payload)
    handler = EVENT_HANDLERS.get(event["type"])
    if not handler:
        return {"status": "ignored"}

    # Hand off to Claude for next-step decision
    claude.messages.create(
        model="claude-3-5-haiku-20241022",
        max_tokens=512,
        tools=[{"name": handler, "input_schema": {"type": "object", "properties": {
            "to": {"type": "string"}, "subject": {"type": "string"}, "campaign_id": {"type": "string"},
        }}}],
        messages=[{"role": "user", "content": f"Resend event: {event['type']}\n{event['data']}"}],
    )
    return {"status": "handled"}

Configure the webhook

In Resend dashboard → Webhooks → Add endpoint:

  • URL: https://your-app.com/resend/webhook
  • Events: select email.* (delivered, opened, clicked, bounced, complained, delivery_delayed)
  • Save and copy the signing secret → put in RESEND_WEBHOOK_SECRET env var

Event payload structure

{
  "type": "email.opened",
  "created_at": "2026-05-11T14:23:00Z",
  "data": {
    "email_id": "8d22a0b1-...-...-...",
    "to": ["user@example.com"],
    "from": "TokRepo <hi@tokrepo.com>",
    "subject": "Welcome to TokRepo",
    "tags": [{"name": "campaign", "value": "welcome-2026-05"}]
  }
}

Production hygiene

  • Hard bounces → mark recipient suppressed immediately. Sending again hurts your domain reputation.
  • Complaints → suppress AND lower send volume for the source campaign.
  • Delivery delays → usually transient. Alert only on >24h delay or repeating recipients.
  • Idempotency → store event['data']['email_id'] + event['type'] to dedupe retries.

FAQ

Q: Open tracking — is it accurate? A: Opens fire on tracking-pixel load. Image-blocking clients (Apple Mail Privacy Protection, many corporate clients) inflate or null open counts. Use opens as a directional signal, not a precise metric. Clicks are far more reliable.

Q: Why use webhooks vs polling the API? A: Webhooks are real-time; polling burns rate limit and adds latency. The Resend Emails API is fine for status-on-demand (emails.retrieve) but webhooks are the right primitive for event-driven agent flows.

Q: How do I test webhooks locally? A: Use ngrok http 8000 to tunnel; paste the ngrok URL into Resend webhook config. Resend dashboard has a 'Send test event' button to replay payloads without sending real email.


Source & Thanks

Built by Resend. Webhook docs at resend.com/docs/dashboard/webhooks/introduction.

resend/resend-node — official SDK

🙏

Source et remerciements

Built by Resend. Webhook docs at resend.com/docs/dashboard/webhooks/introduction.

resend/resend-node — official SDK

Fil de discussion

Connectez-vous pour rejoindre la discussion.
Aucun commentaire pour l'instant. Soyez le premier à partager votre avis.

Actifs similaires