Ir al contenido

Buenas prácticas con webhooks

Verifica la firma, encola el evento (fila en base de datos, cola, tarea en segundo plano) y devuelve 2xx de inmediato. SpacePost espera como máximo 15 segundos, y un manejador lento se convierte en reintentos y duplicados.

La entrega es de al menos una vez. Guarda el webhook-id de cada entrega procesada (basta una restricción de unicidad) y omite las repeticiones.

const inserted = await db.insert(processedWebhooks).values({ id: webhookId }).onConflictDoNothing().returning();
if (inserted.length === 0) return new Response("duplicate", { status: 200 });

Usa el cuerpo sin procesar, rechaza las marcas de tiempo con más de 5 minutos de antigüedad y compara las firmas en tiempo constante (el SDK y las bibliotecas lo hacen). Consulta Verificar firmas.

Los eventos pueden llegar desordenados, sobre todo tras reintentos. Compara el created_at del payload con lo que tienes guardado y aplica solo los cambios que hagan avanzar el estado. El propio last_event de SpacePost sigue la misma regla.

Evento Acción habitual
email.bounced Marca la dirección como no válida en tu base de datos; deja de enviarle.
email.complained Da de baja de inmediato al usuario del correo no esencial.
email.delivery_delayed Normalmente nada; avisa si es un correo urgente (restablecimiento de contraseña).
email.failed Registra data.failed.reason, avisa y, si procede, reintenta con una corrección.
email.suppressed Un destinatario estaba en tu lista de supresión; muéstralo en tu interfaz de administración.

Suscríbete solo a los eventos que gestionas. Menos eventos significan menos tráfico y menos fallos.

  • Sírvelo por HTTPS con un certificado válido.
  • No registres secretos ni payloads completos que contengan datos personales.
  • Usa una ruta dedicada sin ningún otro comportamiento y devuelve un cuerpo genérico.
  • Guarda el signing_secret en un gestor de secretos, no en el control de versiones.

Vigila el registro de entregas del panel o configura alertas ante fallos repetidos: si tu endpoint está caído más de unas 27 horas, se agotan todos los reintentos y tendrás que reenviar manualmente.