# General Translation Platform: Webhooks URL: https://generaltranslation.com/zh/docs/platform/dashboard/reference/webhooks.mdx --- title: Webhooks description: 配置 Webhook 端点、验证签名,并处理来自 General Translation 的翻译事件。 --- Webhooks 会以带签名的 HTTP POST 请求将翻译事件发送到你的后端。你可以在文件或作业发生变更时,用它来触发自己的工作流。 ## 创建 Webhook [#create-webhook] 1. 前往 **Organization Settings > Webhooks**。 2. 点击 **Create Webhook**。 3. 输入要接收事件的端点 URL。该 URL 必须使用 HTTPS。 4. 选择要订阅的事件类型。 5. 点击 **Create**。 创建端点后,打开 Webhook 详情页并复制签名密钥。使用此密钥验证传入请求是否来自 General Translation。 每个 Organization 最多可创建 **5 个 Webhook 端点**。 ## 事件类型 [#event-types] 每个端点都可以订阅一个或多个事件类型。你可以在 Webhook 详情页更新订阅。 * `translated_file.completed` 会在翻译后的文件生成完成并可供下载时触发。 * `translated_file.edited` 会在用户在 Translation Editor 中手动编辑翻译后的文件时触发。 * `translation_job.completed` 会在翻译任务完成时触发。 ## Payload 格式 [#payload-format] 每次 webhook 投递都会通过 HTTP POST 发送一个 JSON 请求体: ```json { "id": "evt_xxx", "type": "translated_file.completed", "created_at": "2026-04-30T12:00:00.000Z", "api_version": "2026-03-06.v1", "data": { "object": { "id": "file_xxx", "org_id": "org_xxx", "project_id": "project_xxx", "branch_id": "branch_xxx", "source_file_id": "src_xxx", "file_id": "file_xxx", "version_id": "ver_xxx", "locale": "fr", "file_format": "json", "data_format": null, "completed_at": "2026-04-30T12:00:00.000Z" } } } ``` 顶层 `id` 是一个稳定的事件标识符,可用于对重复投递进行去重。 ## 验证签名 [#verify-signatures] 每个 Webhook 请求都包含三个请求头: * `webhook-id` 是事件 ID,例如 `evt_xxx`。 * `webhook-timestamp` 是请求发送时的 Unix 时间戳,单位为秒。 * `webhook-signature` 是 `v1,` 签名。 该签名遵循 [Standard Webhooks](https://www.standardwebhooks.com/) 规范。 要验证请求,请按以下步骤操作: 1. 将 `{webhook-id}.{webhook-timestamp}.{raw request body}` 拼接起来。 2. 去掉密钥中的 `whsec_` 前缀后,对其进行 Base64 解码以获取签名密钥。 3. 使用解码后的签名密钥,对拼接后的字符串计算 HMAC-SHA256。 4. 将结果进行 Base64 编码。 5. 将其与 `v1,` 前缀之后的签名值进行比较。 6. 检查时间戳是否与当前时间相差不超过 5 分钟,以防止重放攻击。 ### 示例:Node.js ```js import crypto from "crypto"; function verifyWebhook(payload, headers, secret) { const msgId = headers["webhook-id"]; const timestamp = headers["webhook-timestamp"]; const signature = headers["webhook-signature"]; const now = Math.floor(Date.now() / 1000); if (Math.abs(now - parseInt(timestamp)) > 300) { throw new Error("Timestamp too old"); } const signingKey = Buffer.from(secret.replace("whsec_", ""), "base64"); const signedContent = `${msgId}.${timestamp}.${payload}`; const expected = crypto .createHmac("sha256", signingKey) .update(signedContent) .digest("base64"); const received = signature.split(",")[1]; if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) { throw new Error("Invalid signature"); } return JSON.parse(payload); } ``` 你也可以使用适用于你的编程语言的 [Standard Webhooks](https://www.standardwebhooks.com/) 库,它会自动处理验证。 ## 处理重试与投递 [#handle-retries-delivery] Webhook 使用 **至少一次投递**。 如果你的端点未在 10 秒内返回 `2xx` 响应,系统会以指数退避方式重试此次投递,最多重试 **10 次**。 你可以在仪表板的 Webhook 详情页手动重试失败的投递。 ## 处理重复事件 [#handle-duplicate-events] 由于事件投递至少会发生一次,你的端点可能会多次收到同一事件。请使用请求体中的 `id` 字段进行去重。 ```js app.post("/webhooks/gt", (req, res) => { const eventId = req.body.id; if (alreadyProcessed(eventId)) { return res.status(200).send("OK"); } // 处理事件... markProcessed(eventId); res.status(200).send("OK"); }); ``` ## 管理端点 [#manage-endpoints] 在 webhook 页面中,您可以: * 在不删除端点的情况下启用或禁用它 * 更新已订阅的事件类型 * 查看投递历史 * 查看单次投递尝试的详情 * 重试失败的投递 * 查看签名密钥 * 删除端点 ## 最佳实践 [#best-practices] * 尽快返回 `2xx` 响应,然后异步处理事件。 * 在信任请求体之前,先验证 `webhook-signature` header。 * 存储已处理事件的 ID,并跳过重复事件。 * 生产环境中的 Webhook 端点应使用 HTTPS。 * 监控投递历史,排查持续发生的故障。 ## 权限 [#permissions] 管理 Webhook 端点需要 `org:webhooks:write` 权限。 查看投递历史需要 `org:webhooks:read` 权限。 默认情况下,**Admin** 角色同时具有这两项权限。详情请参阅[角色和权限](/docs/platform/dashboard/reference/roles-and-permissions)。