开发者文档

Webhooks

Webhook 是带签名的变更通知,不包含笔记正文。请通过 REST API 获取最新内容。

Webhook 事件

事件触发时机
note.ended录音会话结束且最终转录可以通过 API 获取时触发。每次录音会话触发一次,因此包含多个会话的笔记可能多次触发。
note.summary.generated摘要生成完成且可以通过 API 获取时触发。
note.updated已结束笔记中的标题、内容等 API 可见信息发生变更时触发。短时间内的多次编辑会合并为一个事件。
note.deleted笔记被删除(data.reason: "deleted"),或离开 API 密钥的可见范围(data.reason: "access_lost")时触发。

另有两个与 Webhook 端点本身相关的事件。endpoint.verification 会在创建端点或修改 URL 时发送;返回 2xx 后即可启用端点。endpoint.test 会在控制台点击“发送测试”或调用测试 API 时发送。

请求正文结构

Webhook 请求只包含事件标识符和处理状态,不包含转录或摘要正文。收到通知后,请使用 note_id 从 REST API 获取最新内容。

JSON
{
  "event_id": "5f8c0a5e-...",
  "event_type": "note.ended",
  "occurred_at": "2026-08-12T05:20:00Z",
  "data": {
    "note_id": "note_abc123",
    "recording_session_id": "rec_456",
    "revision": 7,
    "transcript_status": "ready",
    "summary_status": "pending"
  }
}

验证签名

Alt 使用创建端点时签发的 whsec_... 密钥为每个请求签名。签名格式遵循 Standard Webhooks 规范:

HTTP
POST /webhooks/alt HTTP/1.1
Content-Type: application/json
webhook-id: 5f8c0a5e-...
webhook-timestamp: 1765515600
webhook-signature: v1,K5oZfzN95Z9UVu1EsPQmSmZQGGVfCM0jaZ0lPI4dvLU=
signature
signed_content = "{webhook-id}.{webhook-timestamp}.{raw request body}"
signature     = base64( HMAC-SHA256( base64url_decode(secret_after_whsec_), signed_content ) )
  • 请在框架解析 JSON 之前,使用原始请求体(raw body)验证签名。重新序列化解析后的 JSON 可能改变正文并导致验签失败。
  • 使用常量时间比较函数验证签名,以防止时序攻击。
  • 拒绝过期的时间戳以防止重放攻击。建议只接受与当前时间相差 ±5 分钟以内的时间戳。
  • 签名不匹配的请求返回 4xx 拒绝。可直接使用的接收端代码见快速开始第 4 步

投递失败与重试

  • 请在 10 秒内返回 2xx。其他状态码或响应超时都会被视为投递失败。请先返回响应,再异步处理事件。
  • 投递失败后,Alt 会逐步延长重试间隔(30 秒 → 5 分钟 → 30 分钟 → 2 小时 → 12 小时 → 12 小时,最多共 7 次)。
  • 连续多次投递失败的端点会被自动停用。
  • 不会跟随重定向;webhook URL 必须通过 HTTPS 直接响应。

处理重复事件

Webhook 采用至少一次投递(at-least-once),因此重试或在控制台重新发送时,同一事件可能到达多次。请短期保存已处理的 event_id,并跳过已处理的事件。

处理乱序事件

重试和并行投递可能导致事件的到达顺序与发生顺序不同。不要假设最后到达的事件包含最新状态。每当 API 可见的笔记信息发生变更,服务端都会将 revision 更新为更大的值。请为每篇笔记保存最后应用的 revision,并忽略相同或更小的值。若无法判断正确顺序,请通过 REST API 重新获取笔记;API 始终返回当前 revision。

检查遗漏的变更

  • 如果接收端停机时间超过重试窗口,或端点被停用,Webhook 可能会遗漏。请定期调用 GET /v1/notes?updated_after=<last sync>,获取上次同步后发生变更的笔记。
  • reason: "access_lost"note.deleted 表示该笔记已离开你的凭据范围(例如个人笔记被移入团队空间)。请完全按删除处理:删除你保存的副本或阻断其访问。
  • 仅靠增量查询无法发现已删除或离开可见范围的笔记。请定期重新获取完整笔记列表。使用 include_deleted=true 可包含删除标记(tombstone)。如果已保存的笔记 ID 未出现在完整列表中,说明该笔记已被删除或已离开可见范围。