Esta página se muestra en inglés. Una traducción al español está en curso.
SkillsMay 11, 2026·4 min de lectura

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.

Listo para agents

Staging seguro para este activo

Este activo primero queda en staging. El prompt copiado pide inspeccionar los archivos staged antes de activar scripts, config MCP o config global.

Stage only · 29/100Política: staging
Superficie agent
Cualquier agent MCP/CLI
Tipo
Skill
Instalación
Stage only
Confianza
Confianza: Community
Entrada
Asset
Comando de staging seguro
npx -y tokrepo@latest install e60ca183-5be1-4d16-ae96-2b7b3bedba46 --target codex

Primero deja archivos en staging; la activación requiere revisar el README y el plan staged.

Introducción

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

🙏

Fuente y agradecimientos

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

resend/resend-node — official SDK

Discusión

Inicia sesión para unirte a la discusión.
Aún no hay comentarios. Sé el primero en compartir tus ideas.

Activos relacionados