跳转到内容

Webhook 概览

Webhook 会在邮件事件发生时将其推送到你的服务器,因此你无需轮询 GET /emails/{id}。SpacePost 使用 Standard Webhooks 方案(Svix 和 Resend 所用的方案)为每个请求签名,因此现有的验证代码无需修改即可使用。

  1. 创建一个端点,接受带 JSON 请求体的 POST 请求,并快速返回 2xx。

  2. 注册该端点

    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. 验证每个请求的签名。参见验证签名。

  4. 返回 2xx。 其他任何响应(包括重定向和超时)都会被重试。参见重试。

每次投递都是一个 POST 请求,带有 Content-Type: application/json 以及以下标头:

标头 值
webhook-id 本次投递的唯一 id。每次重试都使用相同的 id,因此可用它来去重。
webhook-timestamp 本次尝试发送时的 Unix 时间(秒)。
webhook-signature v1,<base64 HMAC-SHA256>。如果有多个签名,则为以空格分隔的列表。
svix-id, svix-timestamp, svix-signature 与上面三个标头完全相同的副本,因此 Svix 库可直接使用。

请求体为 { "type": "...", "created_at": "...", "data": { ... } }。参见事件类型。

POST /webhooks、GET /webhooks、GET /webhooks/{id}、PATCH /webhooks/{id}(可修改 endpoint、events 或 status:enabled 或 disabled)以及 DELETE /webhooks/{id}。signing_secret 会在创建或获取单个 Webhook 时返回,列表中不包含。

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://(纯 http:// 仅在开发环境中被接受)。
  • 主机必须解析到公网地址。localhost 和私有网络会被拒绝。本地开发时请使用 ngrok 或 Cloudflare Tunnel 等隧道。
  • 请在 15 秒内响应。
  • 不会跟随重定向。