Vue d'ensemble des webhooks
Les webhooks poussent les événements d’e-mail vers votre serveur au fur et à mesure, vous n’avez donc pas à interroger GET /emails/{id}. SpacePost signe chaque requête selon le schéma Standard Webhooks (celui de Svix et de Resend) : le code de vérification existant fonctionne donc sans changement.
-
Créez un endpoint qui accepte
POSTavec un corps JSON et répond2xxrapidement. -
Enregistrez-le
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);Fenêtre de terminal curl -X POST https://api.spacepost.dev/webhooks \-H "Authorization: Bearer $SPACEPOST_API_KEY" -H "Content-Type: application/json" \-d '{"endpoint": "https://acme.com/webhooks/spacepost","events": ["email.delivered", "email.bounced", "email.complained"]}'r = requests.post("https://api.spacepost.dev/webhooks",headers={"Authorization": f"Bearer {os.environ['SPACEPOST_API_KEY']}"},json={"endpoint": "https://acme.com/webhooks/spacepost", "events": ["email.delivered", "email.bounced"]},)print(r.json()["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"} -
Vérifiez la signature de chaque requête. Voir Vérifier les signatures.
-
Renvoyez
2xx. Toute autre réponse (redirections et délais dépassés compris) est retentée. Voir Nouvelles tentatives.
Format de la requête
Section intitulée « Format de la requête »Chaque distribution est un POST avec Content-Type: application/json et ces en-têtes :
| En-tête | Valeur |
|---|---|
webhook-id |
Id unique de cette distribution. Le même id à chaque nouvelle tentative : utilisez-le pour dédupliquer. |
webhook-timestamp |
Heure Unix en secondes à laquelle cette tentative a été envoyée. |
webhook-signature |
v1,<base64 HMAC-SHA256>. Une liste séparée par des espaces s’il y a plusieurs signatures. |
svix-id, svix-timestamp, svix-signature |
Doublons identiques des trois en-têtes ci-dessus, pour que les bibliothèques Svix fonctionnent telles quelles. |
Le corps est { "type": "...", "created_at": "...", "data": { ... } }. Voir Types d’événements.
Gérer les webhooks
Section intitulée « Gérer les webhooks »POST /webhooks, GET /webhooks, GET /webhooks/{id}, PATCH /webhooks/{id} (modifie endpoint, events ou status : enabled ou disabled) et DELETE /webhooks/{id}. Le signing_secret est renvoyé à la création d’un webhook ou lorsque vous en récupérez un seul, pas dans les listes.
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);Exigences pour les endpoints
Section intitulée « Exigences pour les endpoints »https://en production (http://simple n’est accepté qu’en développement).- L’hôte doit résoudre vers une adresse publique. Localhost et les réseaux privés sont rejetés. En développement local, utilisez un tunnel comme ngrok ou Cloudflare Tunnel.
- Répondez en moins de 15 secondes.
- Les redirections ne sont pas suivies.