Skip to content
指导和见解

为 AI API 经销商构建合作伙伴 API Webhook:签名事件、持久交付和安全重播

可靠的合作伙伴 API webhook 的实用架构,涵盖提交的事件生成、租户安全扇出、签名、重试、重播、SSRF 保护、架构演变和协调。

轮询合作伙伴 API 来了解每个余额、客户、密钥、预算和使用情况的变化,这会造成一个尴尬的控制循环:重要的操作迟到,重复获取未更改的资源,并且可以以错误的顺序观察到并发更新。出站 Webhook 可以减少延迟,但前提是它们构建为持久的通知系统,而不是添加到请求处理程序的 HTTP 回调。

基本规则很简单:网络钩子报告已提交的状态转换。它不会创建过渡、取代分类账或成为权威记录。当事件可能冻结客户访问、触发信用操作或影响发票时,这种区别就很重要。

首先定义交付合同

既定事实:常见 Webhook 规范和大型 Webhook 发布商假定至少一次传送。重试可能会产生重复,独立的交付可能会无序到达。

建议:在公开端点管理之前发布以下合同API:

  • 每个业务事件都有一个稳定、不可变的事件 ID。
  • 自动重试和手动重播都会保留相同的事件 ID。
  • 至少交付一次;不承诺一次性交付。
  • 不保证跨端点或事件类型的顺序。
  • 成功的响应意味着接收者持久地接受了该事件,不一定所有下游工作都已完成。
  • 网络钩子是通知;合作伙伴 API 读取和账本导出仍然具有权威性。

此合同适合 AI API 经销商平台,因为访问控制和财务事件需要及时通知和可靠的恢复路径。

从狭窄的活动目录开始

不要将每次数据库更新都变成公共事件。发布可供合作伙伴执行并精确定义的转换。

事件何时发出典型合作伙伴操作
customer.created客户创建提交在转销商中配置客户门户
customer.frozen将访问状态更改为冻结禁用本地登录或通知支持人员
api_key.created关键元数据提交刷新客户的密钥清单
api_key.revoked撤销生效停止向密钥分配工作
balance.low可用余额向下超过配置的阈值请求充值或警告客户
budget.threshold_crossed最终支出超出政策阈值应用本地工作流程或审批规则
usage.finalized使用量从临时改为已完成更新报告或下游结算
credit.adjustment_posted提交分类帐调整刷新显示的余额

阈值事件需要明确的交叉规则。例如,当先前值低于 80% 并且新值等于或高于 80% 时发出,而不是在每个后​​续使用记录上发出。定义反转是否可以重新设置阈值以及调整是否可以生成新的交叉。

建议:避免将完整的API密钥、提供商凭据、原始提示或详细的滥用证据放入有效负载中。更喜欢资源标识符和最小状态快照。接收者可以使用合作伙伴 API 获取敏感或快速变化的详细信息。

根据已提交状态创建事件

直接从请求处理程序发布会创建两个失败窗口。回调可能会在数据库事务提交之前逃逸,或者事务可能会在发送回调之前进程失败时提交。事务发件箱会关闭两个窗口。

交易发件箱序列

  1. 开始控制平面或账本交易。
  2. 应用权威状态更改。
  3. 将不可变事件插入同一事务中的发件箱表中。
  4. 提交一次。
  5. 让发布商工作人员声明已提交的发件箱行。
  6. 使用活动的合作伙伴范围和订阅过滤器解析符合条件的端点。
  7. 为每个事件端点对创建一条传送记录。

对 (event_id, endpoint_id) 的唯一约束可防止工作线程重试时出现重复扇出。应使用比较和设置语义或行锁定来更新发件箱发布状态,以便多个工作人员可以安全地协作。

outbox_event(
  event_id, partner_id, type, subject_id,
  occurred_at, schema_version, payload, correlation_id,
  published_at
)

delivery(
  delivery_id, event_id, endpoint_id, state,
  attempt_count, next_attempt_at, last_status,
  last_latency_ms, last_error_category
)

发件箱添加了数据库写入、清理工作和积压监控。当通知具有财务或访问控制后果时,这些成本是合理的。对于一次性产品遥测,尽力回调可能仍然可以接受,但它不应共享合作伙伴 API webhook 系统的可靠性声明。

使用稳定的事件信封

将业务事件身份与交付尝试身份分开。重试时事件保持不变;尝试时间戳和传送记录发生变化。

{
  "event_id": "evt_01J...",
  "type": "usage.finalized",
  "occurred_at": "2026-10-15T14:22:31Z",
  "partner_id": "partner_42",
  "subject": {
    "type": "usage_record",
    "id": "usage_9182"
  },
  "schema_version": "1",
  "correlation_id": "op_7fd...",
  "data": {
    "customer_id": "cus_204",
    "currency": "USD",
    "final_cost": "1.274800"
  }
}

使用十进制字符串或小单位整数表示货币。记录货币、精度和舍入政策。关联 ID 应将事件连接到原始合作伙伴 API 操作或分类帐发布,而不会成为事件的身份。

强制实施租户安全扇出

在构建特定于端点的负载之前,先解决事件合作伙伴范围内的订阅问题。作业或队列消息提供的端点 ID 不足以授权传送。工作线程应使用相当于 endpoint.partner_id = event.partner_id 的谓词加载端点,并验证它是否处于活动状态并订阅了事件类型。

这是合作伙伴API自动化中租户授权的出站对应项:必须在每个异步边界重新建立对象所有权。

对您发送的确切字节进行签名

既定事实:标准 Webhooks 规范定义了 webhook-id、webhook-timestamp 和 webhook-signature 标头,并一起对事件 ID、尝试时间戳和原始请求正文进行签名。

为每个端点分配一个不同的签名密钥。在最终 JSON 序列化后计算签名,并发送这些确切的字节。签名后重新序列化对象可能会更改空格、转义或属性顺序并使验证无效。

接收方应拒绝超出记录容差的时间戳,计算原始正文的预期 HMAC,并在恒定时间内比较签名。然后,他们应该在确认事件 ID 之前将其保留在唯一约束下。

对于轮换,请暂时允许两个活动密钥并包含两者的签名。仅在合作伙伴确认新秘密后或在记录的重叠期后结束轮换。初始创建后切勿再次暴露现有机密。

让每次交付都可观察且可重播

保留每次尝试的开始时间、状态代码、延迟时间、错误类别、下次重试时间和大小有限的响应摘录。在显示之前编辑标头和响应正文。运营商需要区分 DNS 故障、连接拒绝、TLS 故障、超时、HTTP 拒绝和内部工作器故障。

实用的响应政策是:

  • 200-299:标记已送达。
  • 410:停止传送并禁用端点。
  • 408、425、429 和 500-599:重试,遵守配置限制内的有效 Retry-After 值。
  • 其他 400-499:分类为持续接收器错误,并仅根据已发布的策略重试。
  • 网络和超时故障:使用指数退避和抖动重试。

每个端点的并发上限,以便一个失败的接收器无法使用工作池。在多天内进行重试,通过单独的渠道警告端点所有者,并在记录的故障窗口后禁用端点。

手动重播应该为原始事件端点对创建新的尝试。它不得创建新的业务事件、更改 event_id 或暗示基础操作再次发生。

将回调网址视为 SSRF 边界

合作伙伴控制的回调网址会指示您的基础架构在何处发出网络请求。这使得网络钩子发布者成为 SSRF 目标。

建议: 要求 HTTPS、限制端口、拒绝嵌入凭据并阻止环回、私有、链路本地、多播、保留和云元数据目标。在验证期间解析主机名,并在连接时再次解析主机名。验证每个已解析的地址,而不仅仅是第一个地址。

不要自动遵循重定向。如果支持重定向,请验证每个目标跃点并应用较小的跃点限制。仅连接到经过验证的公共地址,同时保留用于 TLS 验证的预期主机名,从而防止 DNS 重新绑定。

在隔离的网段中运行交付工作人员,无需访问控制平面数据库、内部管理服务、编排 API 或实例元数据。应用请求正文限制、响应大小限制、连接超时和总请求期限。

为重复事件和陈旧事件设计使用者

接收方应验证签名、验证时间戳和架构、将事件 ID 插入持久收件箱、排队处理并快速返回。唯一的事件 ID 约束将并发重复转化为正常的数据库结果。

业务效果需要自己的幂等性控制。如果 credit.adjustment_posted 触发会计导入,则将该导入关键到源调整 ID,而不是假设 Webhook 重复数据删除单独保护分类帐。在接收者提交其效果之后、发布者看到响应之前,可能会发生超时。

对于可变状态(例如键状态),请在应用旧事件之前比较资源版本或获取当前状态。这对于 AI API 预算控制尤其重要,其中延迟的阈值通知不得覆盖较新的限制或执行决策。

版本架构并提供协调

版本网络钩子架构独立于普通合作伙伴API响应。保持附加字段向后兼容,发布 JSON 架构和示例,并为不兼容的语义保留新的事件类型或架构 URI。将每个端点固定到显式事件架构行为,而不是在 API 发生变化时默默地更改负载。

Webhook 还需要基于拉取的恢复:

  • 列出光标后的事件,范围仅限于经过身份验证的合作伙伴。
  • 检索当前客户、密钥、余额和预算状态。
  • 导出某个时间范围内的最终使用情况和分类帐调整。
  • 公开传送保留和重播窗口限制。

预测: 合作伙伴最终将因保留过期、端点禁用、部署错误或长时间中断而错过通知。基于游标的事件源和权威状态 API 将该事件转化为协调工作,而不是手动数据修复。

实施清单

  • 记录事件语义、交叉规则和数据敏感性。
  • 在一笔事务中写入事件和权威更改。
  • 在扇出和交付期间强制执行合作伙伴所有权。
  • 对稳定事件 ID、尝试时间戳和原始负载字节进行签名。
  • 存储每次尝试的证据并公开可搜索的传送历史记录。
  • 支持重叠机密、测试事件、禁用和重播。
  • 在注册和连接时验证目的地。
  • 测试重复项、乱序传送、持久接收后超时、轮换重叠、DNS 重新绑定、专用 IP 重定向、速率限制和延长停机时间。
  • 监控发件箱期限、队列深度、成功率、端点禁用和重试饱和度。
  • 在递送记录过期后保持调节 API 可用。

围绕权威记录构建通知

可靠的设计并不是更激进的重试循环。它是一个持久边界链:提交状态、事务事件创建、租户范围扇出、签名交付、幂等接收和基于拉动的协调。在扩展事件目录之前实施这些边界。然后,合作伙伴可以自动执行配置、访问控制、余额、预算和使用,而无需将不可靠的 HTTP 交换视为记录系统。

相关阅读

FAQ

常见问题

Webhook 负载是否应该包含完整的客户或关键记录?
通常不会。小快照可以减少后续请求,但完整记录会增加隐私暴露,并且在处理之前可能会变得过时。包括稳定资源 ID、发生的转换以及仅公共路由决策所需的字段。让接收者通过合作伙伴 API 检索权威状态。
返回 2xx 响应是否意味着业务操作已完成?
这应该意味着接收者持久地接受了该事件,通常是将其插入收件箱或队列中。异步处理缩短了响应时间并减少了不必要的重试。当下游工作稍后可能失败时,接收者应该公开自己的状态模型。
手动重播是否应该生成新的事件 ID?
否。重播是原始业务事件的另一次传递尝试,因此它应保留原始事件 ID 并使用新的尝试时间戳和传递尝试记录。这保留了消费者重复数据删除。
失败的交付需要多长时间重试?
没有普遍的持续时间。记录跨越瞬时中断的窗口,使用带有抖动的指数退避,遵守有界的重试后指令,并在禁用端点之前警告所有者。将窗口与基于光标的事件检索配对,以便合作伙伴可以在交付保留到期后进行恢复。