Panoramica dei webhook
I webhook inviano gli eventi delle email al tuo server mentre accadono, così non devi fare polling su GET /emails/{id}. SpacePost firma ogni richiesta con lo schema Standard Webhooks (quello usato da Svix e Resend), quindi il codice di verifica esistente funziona senza modifiche.
-
Crea un endpoint che accetti
POSTcon un corpo JSON e risponda2xxrapidamente. -
Registralo
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);Finestra del terminale 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"} -
Verifica la firma di ogni richiesta. Vedi Verificare le firme.
-
Restituisci
2xx. Qualsiasi altra risposta (inclusi redirect e timeout) viene ritentata. Vedi Nuovi tentativi.
Formato della richiesta
Sezione intitolata “Formato della richiesta”Ogni consegna è un POST con Content-Type: application/json e questi header:
| Header | Valore |
|---|---|
webhook-id |
Id univoco di questa consegna. Lo stesso id a ogni nuovo tentativo, quindi usalo per la deduplicazione. |
webhook-timestamp |
Tempo Unix in secondi in cui è stato inviato questo tentativo. |
webhook-signature |
v1,<base64 HMAC-SHA256>. Un elenco separato da spazi se ci sono più firme. |
svix-id, svix-timestamp, svix-signature |
Duplicati identici dei tre header qui sopra, così le librerie Svix funzionano così come sono. |
Il corpo è { "type": "...", "created_at": "...", "data": { ... } }. Vedi Tipi di evento.
Gestire i webhook
Sezione intitolata “Gestire i webhook”POST /webhooks, GET /webhooks, GET /webhooks/{id}, PATCH /webhooks/{id} (modifica endpoint, events o status: enabled o disabled) e DELETE /webhooks/{id}. Il signing_secret viene restituito quando crei o recuperi un singolo webhook, non negli elenchi.
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);Requisiti per gli endpoint
Sezione intitolata “Requisiti per gli endpoint”https://in produzione (http://semplice è accettato solo in sviluppo).- L’host deve risolvere a un indirizzo pubblico. Localhost e le reti private vengono rifiutati. Per lo sviluppo locale usa un tunnel come ngrok o Cloudflare Tunnel.
- Rispondi entro 15 secondi.
- I redirect non vengono seguiti.