Ir al contenido

Resumen de webhooks

Los webhooks envían los eventos de correo a tu servidor en cuanto ocurren, así que no tienes que consultar GET /emails/{id} de forma periódica. SpacePost firma cada petición con el esquema Standard Webhooks (el que usan Svix y Resend), por lo que el código de verificación existente funciona sin cambios.

  1. Crea un endpoint que acepte POST con un cuerpo JSON y responda 2xx rápidamente.

  2. Regístralo

    create-webhook.ts
    import { SpacePost } from "@spacepost/node";
    const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);
    const { data: webhook, error } = await spacepost.webhooks.create({
    endpoint: "https://acme.com/webhooks/spacepost",
    events: ["email.delivered", "email.bounced", "email.complained"],
    });
    if (error) throw new Error(`${error.name}: ${error.message}`);
    // Store this secret: you need it to verify signatures. It starts with "whsec_".
    console.log(webhook.id, webhook.signing_secret);
    Response
    {
    "object": "webhook",
    "id": "0192e1c4-0000-7000-8000-0000000000aa",
    "endpoint": "https://acme.com/webhooks/spacepost",
    "events": ["email.delivered", "email.bounced", "email.complained"],
    "status": "enabled",
    "created_at": "2026-10-23T14:00:00.000Z",
    "signing_secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"
    }
  3. Verifica la firma en cada petición. Consulta Verificar firmas.

  4. Devuelve 2xx. Cualquier otra cosa (incluidas las redirecciones y los tiempos de espera agotados) se reintenta. Consulta Reintentos.

Cada entrega es un POST con Content-Type: application/json y estos encabezados:

Encabezado Valor
webhook-id Id único de esta entrega. El mismo id en cada reintento, así que úsalo para deduplicar.
webhook-timestamp Tiempo Unix, en segundos, en que se envió este intento.
webhook-signature v1,<base64 HMAC-SHA256>. Una lista separada por espacios si hay varias firmas.
svix-id, svix-timestamp, svix-signature Duplicados idénticos de los tres encabezados anteriores, para que las bibliotecas de Svix funcionen tal cual.

El cuerpo es { "type": "...", "created_at": "...", "data": { ... } }. Consulta Tipos de evento.

POST /webhooks, GET /webhooks, GET /webhooks/{id}, PATCH /webhooks/{id} (cambia endpoint, events o status: enabled o disabled) y DELETE /webhooks/{id}. El signing_secret se devuelve al crear un webhook u obtener uno solo, no en los listados.

manage-webhooks.ts
import { SpacePost } from "@spacepost/node";
const spacepost = new SpacePost(process.env.SPACEPOST_API_KEY);
const id = "0192e1c4-0000-7000-8000-0000000000aa";
// Subscribe to more events and temporarily disable delivery.
await spacepost.webhooks.update({ id, events: ["email.delivered", "email.bounced", "email.opened"], status: "disabled" });
const { data } = await spacepost.webhooks.list({ limit: 20 });
console.log(data?.data.map((w) => `${w.id} ${w.status} ${w.endpoint}`));
await spacepost.webhooks.remove(id);
  • https:// en producción (http:// a secas solo se acepta en desarrollo).
  • El host debe resolver a una dirección pública. Localhost y las redes privadas se rechazan. Para el desarrollo local usa un túnel como ngrok o Cloudflare Tunnel.
  • Responde en menos de 15 segundos.
  • No se siguen las redirecciones.