Documentation Index
Fetch the complete documentation index at: https://docs.topify.ai/llms.txt
Use this file to discover all available pages before exploring further.
Webhook 让您可以在操作工作流到达检查点或完成时接收实时通知。new_content 操作所使用的 执行工作流 必须使用 Webhook。
注册 Webhook
注册一个新的 Webhook 端点以接收事件通知。
请求体
| 字段 | 类型 | 必填 | 描述 |
|---|
url | string | Yes | 用于接收 Webhook 事件的 HTTPS 端点 URL |
events | string[] | Yes | 要订阅的事件类型数组 |
有效事件
| 事件 | 描述 |
|---|
action.checkpoint | 工作流在审核检查点暂停(大纲、草稿或终稿审核) |
action.completed | 工作流成功完成 |
action.failed | 工作流遇到错误 |
curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/webhooks" \
-H "X-API-Key: tk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/topify",
"events": ["action.checkpoint", "action.completed", "action.failed"]
}'
resp = client.post("/webhooks", json={
"url": "https://example.com/webhooks/topify",
"events": ["action.checkpoint", "action.completed", "action.failed"],
})
const resp = await fetch(`${BASE}/webhooks`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
url: "https://example.com/webhooks/topify",
events: ["action.checkpoint", "action.completed", "action.failed"],
}),
});
响应(201 Created)
{
"success": true,
"data": {
"id": "wh_a1b2c3d4-...",
"url": "https://example.com/webhooks/topify",
"events": ["action.checkpoint", "action.completed", "action.failed"],
"is_active": true,
"secret": "whsec_k7m9p2...",
"created_at": "2026-04-08T12:00:00Z"
}
}
secret 字段仅在创建时返回一次。请妥善保管 —— 您将需要它来验证 Webhook 签名。
错误响应
| 状态 | 原因 |
|---|
400 | 缺少 url、URL 不是 HTTPS、events 数组为空,或事件类型无效 |
403 | API 密钥具有只读权限 |
409 | 已存在使用此 URL 的 Webhook |
列出 Webhook
返回您账户下注册的所有 Webhook。
curl "https://topify-customer-api-production.up.railway.app/api/public/v1/webhooks" \
-H "X-API-Key: tk_live_..."
resp = client.get("/webhooks")
const resp = await fetch(`${BASE}/webhooks`, { headers });
{
"success": true,
"data": [
{
"id": "wh_a1b2c3d4-...",
"url": "https://example.com/webhooks/topify",
"events": ["action.checkpoint", "action.completed", "action.failed"],
"is_active": true,
"created_at": "2026-04-08T12:00:00Z"
}
]
}
列表响应中不包含 secret 字段。它仅在 Webhook 首次创建时返回。
删除 Webhook
DELETE /webhooks/{webhook_id}
永久删除一个 Webhook。事件将不再发送至该端点。
路径参数
| 参数 | 类型 | 描述 |
|---|
webhook_id | string | Webhook ID |
curl -X DELETE "https://topify-customer-api-production.up.railway.app/api/public/v1/webhooks/{webhook_id}" \
-H "X-API-Key: tk_live_..."
resp = client.delete(f"/webhooks/{webhook_id}")
const resp = await fetch(`${BASE}/webhooks/${webhookId}`, {
method: "DELETE",
headers,
});
{
"success": true,
"data": {
"id": "wh_a1b2c3d4-...",
"message": "Webhook deleted successfully"
}
}
错误响应
| 状态 | 原因 |
|---|
403 | API 密钥具有只读权限 |
404 | 未找到 Webhook |
Webhook 载荷格式
事件触发时,Topify 会向您注册的 URL 发送一个 POST 请求,JSON 请求体如下。
action.checkpoint 载荷
{
"event": "action.checkpoint",
"timestamp": "2026-04-08T21:19:13.463Z",
"data": {
"action_id": "a1b2c3d4-...",
"workflow_id": "w1x2y3z4-...",
"checkpoint": "outline",
"content": "## Article Outline\n\n1. Introduction to project management tools...",
"options": [
{ "action": "approve", "label": "Approve & Write" },
{ "action": "edit", "label": "Edit Outline" },
{ "action": "regenerate", "label": "Regenerate" },
{ "action": "reject", "label": "Cancel" }
]
}
}
action.completed 载荷
{
"event": "action.completed",
"timestamp": "2026-04-08T21:25:00.000Z",
"data": {
"action_id": "a1b2c3d4-...",
"workflow_id": "w1x2y3z4-...",
"result": {
"title": "Best Project Management Tools in 2026",
"content": "..."
}
}
}
action.failed 载荷
{
"event": "action.failed",
"timestamp": "2026-04-08T21:22:00.000Z",
"data": {
"action_id": "a1b2c3d4-...",
"workflow_id": "w1x2y3z4-...",
"error": "LLM generation timed out after 120 seconds"
}
}
载荷字段
| 字段 | 类型 | 描述 |
|---|
event | string | 事件类型(action.checkpoint、action.completed、action.failed) |
timestamp | string | 事件发生的 ISO 8601 时间戳 |
data.action_id | string (UUID) | 触发事件的操作 |
data.workflow_id | string (UUID) | 当前活跃的工作流 ID |
data.checkpoint | string | 检查点名称(仅 action.checkpoint) |
data.content | string | 待审核内容(仅 action.checkpoint) |
data.options | object[] | 可用的响应操作(仅 action.checkpoint) |
data.result | object | 最终输出(仅 action.completed) |
data.error | string | 错误消息(仅 action.failed) |
验证签名
每个 Webhook 请求都包含一个 X-Webhook-Signature 请求头,其中是基于原始请求体并使用创建时返回的 Webhook 密钥计算得出的 HMAC-SHA256 签名。
请始终在处理载荷之前验证此签名,以确保请求的真实性。
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
# In your webhook handler:
signature = request.headers["X-Webhook-Signature"]
is_valid = verify_webhook(request.body, signature, WEBHOOK_SECRET)
import { createHmac, timingSafeEqual } from "crypto";
function verifyWebhook(body, signature, secret) {
const expected = createHmac("sha256", secret)
.update(body)
.digest("hex");
return timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}
// In your webhook handler:
const signature = req.headers["x-webhook-signature"];
const isValid = verifyWebhook(req.rawBody, signature, WEBHOOK_SECRET);
您的端点应在 10 秒内返回 200 状态码。如果端点未能响应或返回非 2xx 状态,Topify 将以指数退避策略最多重试 3 次。