> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> 接收来自 Dodo Payments 的实时事件通知。设置端点、验证签名、处理重试，并在开发期间测试 webhooks。

![Webhook Cover Image](https://mintcdn.com/dodopayments/mOQO5ej_lx0yH9p-/images/cover-images/Webhooks.webp?fit=max\&auto=format\&n=mOQO5ej_lx0yH9p-\&q=85\&s=477435b1004773582aa51236b5cc58e7)

当您的 Dodo Payments 账户中发生事件时，Webhooks 会发送实时通知。您可以使用它们自动化工作流、更新数据库、发送通知，并让各个系统保持同步。

> [!NOTE]
>
> Dodo Payments webhooks 遵循 [Standard Webhooks](https://standardwebhooks.com/) 规范进行签名验证并定义 payload 结构。

## 主要功能

Webhooks 提供实时交付、内置安全机制、自动重试和事件筛选功能。所有官方 SDK 都包含签名验证辅助方法，dashboard 提供测试、监控和重放工具。

## 入门

**1. Go to Developer → Webhooks**

在 Dodo Payments Dashboard 中，导航至 **Developer → Webhooks**。

**2. Click Add Endpoint**

点击 **Add endpoint** 以创建新的 webhook 接收器。

**3. Enter Your Endpoint URL**

提供 Dodo Payments 将发送 webhook 事件的 HTTPS URL，或选择集成连接器（Slack、Discord、Zapier、Resend 等），无需编写代码即可将事件路由到第三方服务。

**4. Select Events**

选择要接收的事件。事件按资源（payment、subscription、dispute 等）组织。您可以选择单个事件或整个资源，以接收所有相关事件。

**5. Save**

点击 **Create endpoint**。您的 webhook signing secret 会显示在端点的 **Overview** 选项卡中。

> [!WARNING]
>
> 请妥善保护 webhook secret。切勿将其暴露在客户端代码或版本控制中。

要轮换 webhook secret，请打开端点，在 **Overview** 选项卡中点击 secret 旁边的 **Rotate secret**。轮换后，旧 secret 在 24 小时内仍然有效。

## 集成连接器

使用集成连接器将 webhook 事件直接路由到第三方服务，无需构建和维护自定义 webhook 处理程序。

### 连接器的工作方式

连接器会将 Dodo Payments 事件转换为目标服务所需的格式。您需要提供的详细信息取决于目标服务：

| 连接器类型 | 您需要提供的内容 | 目标位置 |
| - | - | - |
| **Incoming webhook URL** | 您在供应商控制面板中创建的 webhook URL。无需 API key。 | [Slack](/integrations/slack.md)、[Discord](/integrations/discord.md)、[Microsoft Teams](/integrations/microsoft-teams.md)、[Zapier](/integrations/zapier.md)、[Windmill](/integrations/windmill.md) |
| **Vendor API** | API key。目标 URL 来自连接器模板。 | [Resend](/integrations/resend.md)、[SendGrid](/integrations/sendgrid.md)、[Segment](/integrations/segment.md)、[Customer.io](/integrations/customer-io.md)、[Close CRM](/integrations/close-crm.md)、[Loops](/integrations/loops.md) |

dashboard 会显示您的企业可用的所有连接器。请参阅 [External Integrations](/integrations/introduction.md)，了解每个目标服务可以如何处理这些事件。

### 设置连接器

创建或编辑端点时，选择一个连接器，侧边面板会显示该目标服务的设置说明。保存前测试转换，以确认事件已正确转换。

> [!TIP]
>
> 使用连接器，无需编写代码即可连接到受支持的目标服务。如果需要自定义逻辑，请改用标准端点，并使用 [transformation](#transformations)。

## 配置订阅事件

配置每个 webhook 端点接收哪些事件。

**1. Navigate to Webhook Endpoints**

转到 **Developer → Webhooks**，然后点击您的端点。

**2. Open Event Configuration**

点击 **Edit**，打开端点配置侧边面板。

**3. Select Events**

事件类型选择器会以可搜索的树状结构显示所有可用的 webhook 事件，并按资源分组（例如 `payment`、`subscription`、`dispute`）。勾选您要接收的事件旁边的复选框。您可以选择单个事件、整个资源，或混合选择。

**4. Save Configuration**

点击 **Save** 以应用更改。

> [!WARNING]
>
> 如果取消选择所有事件，您的 webhook 端点将接收每种事件类型。请仅选择应用程序需要的事件。

## 事件目录

转到 **Developer → Webhooks**，打开 **Event catalog** 选项卡，查看 Dodo Payments 可以发送的所有事件类型。选择一个事件可查看其 schema 和示例 payload。

> **[Webhook Events Guide](/developer-resources/webhooks/intents/webhook-events-guide.md)**
>
> 浏览按资源分组的事件参考文档。

## Webhook 交付

### 超时

Webhook 的连接和读取操作均有 **30 秒超时**。请通过立即返回 `200` 状态码来异步处理 webhook，然后在后台处理事件。

### 自动重试

失败的投递会采用指数退避进行重试，最多总计 8 次：

| 尝试次数 | 延迟 |
| - | - |
| 1 | 立即 |
| 2 | 5 秒 |
| 3 | 5 分钟 |
| 4 | 30 分钟 |
| 5 | 2 小时 |
| 6 | 5 小时 |
| 7 | 10 小时 |
| 8 | 10 小时（最终） |

使用控制面板手动重新发送失败消息，或批量恢复特定时间范围内的消息。

### 幂等性

每个 webhook 都包含唯一的 `webhook-id` header。请存储此 ID，以检测并跳过重复事件，因为重试可能会多次传递同一事件。

```javascript theme={null}
// Use a persistent store such as your database in production. An in-memory Set is lost on restart.
const processedWebhooks = new Set();

app.post('/webhook', async (req, res) => {
  const webhookId = req.headers['webhook-id'];

  if (processedWebhooks.has(webhookId)) {
    return res.status(200).json({ received: true });
  }

  // processWebhook is your handler. Record the ID only after it succeeds,
  // so a failed attempt is processed again on retry.
  await processWebhook(req.body);
  processedWebhooks.add(webhookId);
  res.status(200).json({ received: true });
});
```

> [!WARNING]
>
> 始终实现幂等性检查。由于存在重试，您可能会多次收到同一事件。

### 事件排序

由于重试或网络状况，事件可能会乱序到达。每个 webhook 都包含一个 `timestamp` 字段；如果您的应用需要对事件排序，请使用此字段。您始终会在投递时收到最新的 payload 状态。

## 保护 Webhook

始终验证 webhook payload，并使用 HTTPS。

### 验证签名

每个 webhook 都包含一个 `webhook-signature` header：这是使用您的 secret key 对 payload 和 timestamp 进行签名后生成的 HMAC SHA256 签名。

#### SDK 验证（推荐）

所有官方 SDK 都包含内置辅助方法。初始化客户端时设置 `DODO_PAYMENTS_WEBHOOK_KEY`，然后调用 `unwrap()` 来验证并解析 payload。有两种方法可用：

* `unwrap` — 使用 webhook secret key 验证签名，然后解析 payload。
* `unsafe_unwrap` — 在不验证签名的情况下解析 payload。仅用于测试。

方法名称遵循各语言的惯例：TypeScript 中为 `unwrap` / `unsafeUnwrap`，Python 中为 `unwrap` / `unsafe_unwrap`，Go 中为 `Unwrap` / `UnsafeUnwrap`。

> [!TIP]
>
> 初始化 Dodo Payments 客户端时，通过 `DODO_PAYMENTS_WEBHOOK_KEY` 提供 webhook secret。

```typescript TypeScript/Node.js theme={null}
import DodoPayments from 'dodopayments';
import express from 'express';

const app = express();
app.use(express.raw({ type: 'application/json' }));

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
});

app.post('/webhook', async (req, res) => {
  try {
    const event = client.webhooks.unwrap(req.body.toString(), {
      headers: {
        'webhook-id': req.headers['webhook-id'] as string,
        'webhook-signature': req.headers['webhook-signature'] as string,
        'webhook-timestamp': req.headers['webhook-timestamp'] as string,
      },
    });
    res.json({ received: true });
  } catch (error) {
    res.status(401).json({ error: 'Invalid signature' });
  }
});
```

```python Python theme={null}
from fastapi import FastAPI, Request, HTTPException
from dodopayments import DodoPayments
import os

app = FastAPI()
client = DodoPayments(
    bearer_token=os.getenv("DODO_PAYMENTS_API_KEY"),
    webhook_key=os.getenv("DODO_PAYMENTS_WEBHOOK_KEY"),
)

@app.post("/webhook")
async def handle_webhook(request: Request):
    try:
        event = client.webhooks.unwrap(
            await request.body(),
            headers={
                "webhook-id": request.headers.get("webhook-id", ""),
                "webhook-signature": request.headers.get("webhook-signature", ""),
                "webhook-timestamp": request.headers.get("webhook-timestamp", ""),
            },
        )
        return {"received": True}
    except Exception:
        raise HTTPException(status_code=401, detail="Invalid signature")
```

```go Go theme={null}
import (
	"io"
	"net/http"
	"os"
	"github.com/dodopayments/dodopayments-go"
	"github.com/dodopayments/dodopayments-go/option"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	client := dodopayments.NewClient(
		option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
		option.WithWebhookKey(os.Getenv("DODO_PAYMENTS_WEBHOOK_KEY")),
	)
	rawBody, _ := io.ReadAll(r.Body)
	event, err := client.Webhooks.Unwrap(rawBody, r.Header)
	if err != nil {
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
		return
	}
	_ = event // Handle the verified event here.
	w.WriteHeader(http.StatusOK)
}
```

#### 手动验证（替代方案）

如果您未使用 SDK，请自行验证签名：

1. 将 `webhook-id`、`webhook-timestamp` 和原始 request body 用句点连接，构建待签名内容：`{id}.{timestamp}.{body}`。请完全按照接收到的原始 body 使用，在进行任何 JSON 解析之前完成此操作。
2. 获取您的 webhook secret。如果它以 `whsec_` 开头，请移除该前缀，然后对剩余部分进行 base64 解码，以获取 signing key。
3. 使用 signing key 计算待签名内容的 HMAC-SHA256，并对结果进行 base64 编码。
4. `webhook-signature` header 包含一个或多个以空格分隔的签名，每个签名格式为 `v1,<base64-signature>`。如果任意 `v1` 签名与您的签名匹配，则请求有效。请使用 constant-time 函数进行比较。
5. 如果 `webhook-timestamp` 与当前时间相差过大，请拒绝请求，以防止 replay attack。Standard Webhooks libraries 允许 5 分钟的时间差。

有关参考实现，请参阅 [Standard Webhooks libraries](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries)。有关事件 payload 格式，请参阅 [Webhook Payload](/developer-resources/webhooks/intents/payment.md)。

### 源 IP 地址

签名验证是受支持的 authentication 方法。它可以证明请求使用您的 webhook secret 进行了签名，而网络层检查无法做到这一点。

Webhook 投递来自一个会随时间变化的 IP 地址池。请勿依赖 IP allowlist 进行 authentication。应始终验证 `webhook-signature` header，具体请参阅[验证签名](#verifying-signatures)。

如果您的 firewall 要求使用 allowlist：

* **不要永久硬编码地址。** 地址范围会随时间变化，过时的规则会在无提示的情况下阻止投递。
* **请求当前地址范围**：在锁定 firewall 之前，向 [support@dodopayments.com](mailto:support@dodopayments.com) 请求当前范围。
* **留意变更通知。** 投递地址发生变化时，我们会通过电子邮件通知受影响的商户——请在指定日期前应用更新。
* **无论添加何种网络规则，都要保持签名验证启用。**

> [!TIP]
>
> 在 serverless 和托管 hosting 平台上，入站 IP 过滤通常不可用或不切实际。在这些环境中，签名验证是正确的控制措施。

> [!NOTE]
>
> 被阻止的投递会被视为失败，并按照[自动重试](#automatic-retries)中所述的计划进行重试。如果 firewall 规则导致投递失败，可以在修复规则后重新发送这些消息——请参阅[重新发送和恢复消息](#replaying-and-recovering-messages)。

### 响应 Webhook

您的 webhook handler 必须返回 `2xx status code` 以确认已接收。任何其他响应都会被视为失败，webhook 将被重试。

### 最佳实践

* **仅使用 HTTPS。** HTTP endpoints 容易遭到拦截。
* **立即响应。** 立即返回 `200` 状态码，然后异步处理事件。
* **实现幂等性。** 使用 `webhook-id` header 检测并跳过重复事件。
* **保护您的 secret。** 将 `DODO_PAYMENTS_WEBHOOK_KEY` 存储在 environment variables 或 secrets manager 中，绝不要存储在 version control 中。

## Webhook Payload 结构

### Request 格式

```http theme={null}
POST /your-webhook-url
Content-Type: application/json
```

### Headers

**webhook-id** `string` *required*

此 webhook 事件的唯一标识符。用于幂等性检查。

**webhook-signature** `string` *required*

用于验证 webhook authenticity 的 HMAC SHA256 签名。

**webhook-timestamp** `string` *required*

发送 webhook 时的 Unix timestamp（以秒为单位）。

### Request Body

**business\_id** `string` *required*

您的 Dodo Payments business identifier。

**type** `string` *required*

触发此 webhook 的事件类型（例如 `payment.succeeded`、`subscription.active`）。

**timestamp** `string` *required*

事件发生时间的 ISO 8601 格式 timestamp。

**data** `object` *required*

包含事件详细信息的特定于事件的 payload。

**Data object properties**

**payload\_type** `string`

资源类型。可以是以下值之一：`Payment`、`Subscription`、`Refund`、`Dispute`、`LicenseKey`、`CreditLedgerEntry`、`CreditBalanceLow`、`AbandonedCheckout`、`DunningAttempt`、`EntitlementGrant` 或 `Payout`。

其他字段会因事件类型而异。有关完整 schemas，请参阅特定于事件的文档。

### Payload 示例

```json theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "payment.succeeded",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "payload_type": "Payment",
    // ... event-specific fields
  }
}
```

> **[Event Types](/developer-resources/webhooks/intents/webhook-events-guide.md)**
>
> 浏览所有可用的 webhook 事件类型

> **[Event Payloads](/developer-resources/webhooks/intents/payment.md)**
>
> 查看每个事件的详细 payload schemas

> **[Handle Payment Failures](/developer-resources/handle-payment-failures.md)**
>
> 响应 `payment.failed` 并恢复被拒付的款项

## 测试 Webhook

### 发送示例事件

直接从控制面板测试 webhook 集成：

**1. Navigate to Webhooks**

前往 **Developer → Webhooks**，然后点击您的 endpoint。

**2. Open Testing Tab**

点击 **Testing** 标签页。

**3. Send Example**

选择事件类型并点击 **Send example**。示例 payload 会像真实事件一样发送到您的 endpoint URL，并使用相同方式签名。

**4. Check Your Endpoint**

确认事件已到达、签名验证已通过，并且您返回了 `2xx` 状态码。

> [!NOTE]
>
> 从 Testing 标签页发送的失败消息会按照正常的重试计划进行重试，与其他 webhook 一样。

### 实现示例

包含 webhook 验证和处理的完整 Express.js 实现：

```typescript Express.js Webhook Handler theme={null}
import { Webhook } from "standardwebhooks";
import express from "express";

const app = express();

const webhook = new Webhook(process.env.DODO_PAYMENTS_WEBHOOK_KEY);

// Use express.raw() for the webhook route so the exact bytes Dodo Payments
// signed are preserved. Parsing with express.json() first and then calling
// JSON.stringify(req.body) can alter the payload (e.g. whitespace), which
// breaks signature verification.
app.post(
  '/webhook/dodo-payments',
  express.raw({ type: "application/json" }),
  async (req, res) => {
    try {
      // Extract webhook headers
      const webhookHeaders = {
        "webhook-id": req.headers["webhook-id"] as string,
        "webhook-signature": req.headers["webhook-signature"] as string,
        "webhook-timestamp": req.headers["webhook-timestamp"] as string,
      };

      // Verify the webhook signature against the raw, unmodified body
      const payload = req.body.toString("utf8");
      await webhook.verify(payload, webhookHeaders);

      // Parse the payload only after verification succeeds
      const data = JSON.parse(payload);

      // Acknowledge receipt immediately
      res.status(200).json({ received: true });

      // Process webhook asynchronously
      processWebhookAsync(data).catch(console.error);

    } catch (error) {
      console.error('Webhook verification failed:', error);
      res.status(401).json({ error: 'Invalid signature' });
    }
  }
);

// Register express.json() for other routes after the webhook route so it
// doesn't consume the raw body needed for signature verification above.
app.use(express.json());

async function processWebhookAsync(data: any) {
  // Handle the webhook event based on type
  switch (data.type) {
    case 'payment.succeeded':
      await handlePaymentSucceeded(data);
      break;
    case 'subscription.active':
      await handleSubscriptionActive(data);
      break;
    // Add more event handlers...
  }
}
```

```javascript Testing Webhooks Locally theme={null}
const { Webhook } = require("standardwebhooks");
const axios = require("axios");
const crypto = require("crypto");

// Generate test webhook
const timestamp = new Date();
const webhookId = crypto.randomUUID();
const payload = {
  business_id: "bus_H4ekzPSlcg",
  type: "payment.succeeded",
  timestamp: timestamp.toISOString(),
  data: {
    payload_type: "Payment",
    payment_id: "pay_test456",
    amount: 2999
  }
};

// Sign the payload
const webhookSecret = process.env.DODO_PAYMENTS_WEBHOOK_KEY;
const webhookInstance = new Webhook(webhookSecret);
const payloadString = JSON.stringify(payload);
const signature = webhookInstance.sign(webhookId, timestamp, payloadString);

// Send test webhook. CommonJS scripts don't support top-level await, so use .then().
axios.post('http://localhost:3000/webhook/dodo-payments', payload, {
  headers: {
    "webhook-id": webhookId,
    "webhook-timestamp": Math.floor(timestamp.getTime() / 1000),
    "webhook-signature": signature,
    "Content-Type": "application/json"
  }
}).then((response) => {
  console.log("Test webhook sent successfully:", response.data);
});
```

> [!TIP]
>
> 在处理 production events 之前，使用控制面板测试界面彻底测试您的 webhook handler。这有助于及早发现并修复问题。

## 使用 CLI 测试 Webhook

[Dodo Payments CLI](/developer-resources/sdks/cli.md) 提供两个命令，用于在本地开发期间测试 webhook。

### 在本地监听实时 Webhook

将 test mode account 中的真实 webhook 事件转发到本地开发服务器：

```bash theme={null}
dodo wh listen
```

CLI 会打开 WebSocket connection，并将每个 webhook 事件转发到您的本地 endpoint（例如 `http://localhost:3000/webhook`），同时保留所有 headers，以便测试签名验证。

> [!NOTE]
>
> listener 仅适用于 test mode API keys。请运行 `dodo login`，并先选择 Test Mode。

### 触发模拟 Webhook 事件

向任意 endpoint 发送模拟 webhook payload，而无需创建真实交易：

```bash theme={null}
dodo wh trigger
```

此 interactive tool 允许您选择事件类型，并向 endpoint 发送逼真的模拟 payload。它会循环运行，因此您可以在一次 session 中测试多个事件。

trigger command 涵盖 subscription、payment、refund、dispute、license key、payout、credit、abandoned checkout、dunning 和 entitlement grant 系列。不发送 `subscription.past_due` 或 `subscription.unpaused`。确切列表请参阅[支持的 Webhook 事件](/developer-resources/sdks/cli.md#supported-webhook-events)。

> [!WARNING]
>
> 来自 `dodo wh trigger` 的模拟 webhook payload 未经过签名。仅在测试期间，在 webhook handler 中使用未经验证的 parse method（TypeScript 中为 `unsafeUnwrap`，Python 中为 `unsafe_unwrap`，Go 中为 `UnsafeUnwrap`）。

> **[CLI Webhook Testing Docs](/developer-resources/sdks/cli.md#webhooks)**
>
> 查看完整的 CLI webhook 测试文档

## 高级设置

**Advanced** 标签页提供其他配置选项，用于微调 webhook endpoint 的行为。

### Rate Limiting（Throttling）

控制 webhook 事件发送到 endpoint 的速率。默认情况下，webhook 不设速率限制，事件发生后会立即发送。

**1. Open Advanced Tab**

在 endpoint details 页面中，点击 **Advanced** 标签页。

**2. Configure Rate Limit**

展开 **Endpoint throttling** 部分。

**3. Set Your Limit**

输入每秒的最大消息数，然后点击 **Save**。超过此速率的投递会进入队列，而不会被丢弃。

### Custom Headers

向发送到 endpoint 的所有 webhook requests 添加 custom HTTP headers。适用于 authentication、routing 或添加 metadata。

**1. Add Headers**

在 **Custom headers** 部分输入 header name 和 value。

**2. Add Multiple Headers**

每个额外的 header 都点击一次 **Add header**，然后点击 **Save**。

### Transformations

Transformations 允许您修改 webhook 的 payload，并可选择将其重定向到其他 URL。使用 transformations 可以：

* 在处理前修改 payload structure
* 根据内容将 webhook 路由到不同的 endpoints
* 从 payload 中添加或移除 fields
* 转换 data formats

**1. Enable Transformations**

在 **Transformation** 部分启用 **Enable transformation**。

**2. Configure Transformation**

在 code editor 中使用 JavaScript 编写 transformation rules，然后点击 **Save**。代码必须从 `handler()` 返回 webhook object。

**3. Test Transformation**

使用 transformation test interface 验证 transformation 在上线前能够正常工作。

> [!WARNING]
>
> Transformations 可能会影响 webhook delivery performance。请进行充分测试，并保持 transformation logic 简单高效。

## 监控 Webhook Logs

**Logs** 标签页提供 webhook delivery status 的可见性。

**1. Navigate to Logs Tab**

前往 **Developer → Webhooks**，然后打开 **Logs** 标签页。

**2. Browse Delivery History**

查看所有 webhook delivery attempts 的表格，其中包含 Event type、Message ID、Event ID、Sent at、Attempted at、Response code 和 Duration 列。

**3. Search and Filter**

使用 search bar 按 ID 或 event type 查找特定消息。按 status（Succeeded、Failed、Pending 等）筛选，以聚焦于需要调查的事件。

**4. View Message Details**

点击任意消息打开 message detail 页面，其中显示：

* 完整的 webhook payload
* 每次 delivery attempt 及其 response code 和 duration
* 每次 attempt 的 timestamp
* endpoint 返回的任何 error messages

每次 attempt 都带有 **Replay** 操作，无需离开页面即可重新驱动该消息。

## Activity Monitoring

前往 **Developer → Webhooks**，然后打开 **Activity** 标签页，查看各个 endpoints 的 delivery performance。

**Delivery activity** 按时间绘制 attempts，根据时间窗口分为 **Attempts per 5 minutes**、**Attempts per hour** 或 **Attempts per day**。每个条形会按结果拆分，将鼠标悬停在某个部分上可查看 status、attempts 数量及其占总数的比例。在某个 endpoint 上，**Overview** 标签页中的 **Delivery stats (last 24h)** 会汇总过去一天的相同信息。

> [!TIP]
>
> **Endpoints** 标签页中的 **Error rate (24h)** 列可以一目了然地显示哪些 endpoints 需要关注。

## 重新发送和恢复消息

重新驱动消息的方式取决于您需要处理的消息数量：

* **一条消息** — 从 **Logs** 标签页打开该消息，并使用 attempt 上的 **Replay** 操作。
* **一系列消息** — 打开 endpoint，因为 bulk modes 一次只作用于单个 endpoint。

### 批量重新发送

从 **Developer → Webhooks** 打开 endpoint。有三种模式可用，每种模式仅作用于该 endpoint：

| 模式 | 功能 | 设置内容 |
| - | - | - |
| **Recover failed messages** | 重新发送所有发送到此 endpoint 但失败的消息。 | 起始点：**8 hours ago**、**Yesterday**、**3 days ago**、**Last week** 或 **2 weeks ago** |
| **Replay missing messages** | 重新发送从未分发到此 endpoint 的消息，例如将 endpoint 订阅到新的 event type 后产生的消息。 | 相同的起始点 |
| **Bulk replay messages** | 重新发送符合所选 filters 的消息，包括已经成功投递的消息。 | **Since** 和 **Until** 边界，默认为最近两周；还可选择 event types、channel 或 tag |

**1. Open More Actions**

在 endpoint 上打开 **More actions**，然后选择上述三种模式之一。

**2. Set the Range**

根据表格中的说明，填写该模式要求的时间范围。

**3. Start the Run**

根据所选模式，点击 **Recover** 或 **Replay**。

每次运行都会显示在 endpoint 的 **Overview** 标签页下的 **Replay history** 中，其中包含其模式、时间范围、status 以及重新发送的消息数量。

## Email Alerts

Webhook dashboard 不提供失败投递的 email alerts。要监控投递情况，请前往 **Developer → Webhooks** 并检查 **Logs** 和 **Activity** 标签页。

## 部署到 Cloud Platforms

将 webhook handlers 部署到常见 cloud providers 的平台专属指南：

> **[Vercel](/developer-resources/webhooks/examples/vercel-example.md)**
>
> 使用 serverless functions 将 webhooks 部署到 Vercel

> **[Cloudflare Workers](/developer-resources/webhooks/examples/cloudflare-example.md)**
>
> 在 Cloudflare 的 edge network 上运行 webhooks

> **[Supabase Edge Functions](/developer-resources/webhooks/examples/supabase-example.md)**
>
> 将 webhooks 与 Supabase 集成

> **[Netlify Functions](/developer-resources/webhooks/examples/netlify-example.md)**
>
> 将 webhooks 部署为 Netlify serverless functions

## 相关 API Reference

> **[Create Webhook](/api-reference/webhooks/create-webhook.md)**
>
> 以编程方式创建和配置 webhook endpoints

> **[List Webhooks](/api-reference/webhooks/list-webhooks.md)**
>
> 获取和管理 webhook endpoints


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.