API 参考
生产接入需要的细节:错误长什么样、会触发哪些 webhook 事件、幂等如何工作,以及限流规则。
每个非 2xx 响应都是一个带类型的错误,含有稳定的、机器可读的 code 以及对应的 HTTP 状态。SDK 会把它作为 AgentbankError 抛出(err.code、err.status) —— 捕获这一个类即可统一处理各种失败。
{ "error": { "code": "PAYMENT_DECLINED", "message": "card declined", "details": { "reason": "insufficient_funds" } } }按 code 分支处理,永远不要按 message 文本判断(message 可能变,code 才是契约)。
在 SDK 里,捕获单一的 AgentbankError 类并读取 err.code / err.status。优先用静态方法 AgentbankError.is(err) 而不是 err instanceof AgentbankError:在包边界上,当一个 node_modules 里共存了两份 SDK core(不同的类对象)时,instanceof 会静默失败,而 is() 检查的是形状,能识别任意版本抛出的错误。传输层失败(没有 HTTP 响应 —— 超时、abort、DNS)会以 AgentbankError 的形式出现,status === 0,code 为 timeout、aborted 或 network_error。
try { await agentbank.checkout.openACP({ currency, items });} catch (err) { if (AgentbankError.is(err) && err.status === 429) { // back off and retry — see Rate limits below }}code |
HTTP | 含义 |
|---|---|---|
VALIDATION_ERROR |
400 | 请求格式错误 —— 字段错误或缺失。details 携带各字段的错误信息。 |
UNAUTHORIZED |
401 | 缺少或无效的凭据。 |
PAYMENT_DECLINED |
402 | 支付通道拒绝了扣款。details.reason 是通道给出的原因。 |
FORBIDDEN |
403 | 已认证,但无权访问该资源。 |
SANCTIONS_BLOCKED |
403 | 被制裁筛查拦截。 |
NOT_FOUND |
404 | 无此资源 —— 或你的权限范围看不到它。 |
CONFLICT |
409 | 状态冲突 —— 例如对已过期的结账做 capture,或幂等键在进行中被复用。 |
GONE |
410 | 资源曾存在,但已不可用。 |
RATE_LIMITED |
429 | 请求过多 —— 见 限流。 |
INVARIANT_VIOLATION |
500 | 服务端一致性校验失败。请上报。 |
UPSTREAM_ERROR |
502 | 某个依赖(支付通道、Curless)失败。可能可重试。 |
CIRCUIT_OPEN |
503 | 某个依赖的熔断器已打开(多次失败后快速失败)。短暂退避后重试。 |
Webhook 事件
Section titled “Webhook 事件”注册一个端点(POST /v1/webhook-endpoints)并订阅事件类型(不订阅任何类型 = 接收全部)。每次投递为 { id, type, data },签名为 x-agentbank-signature: t=…,v1=…。用 merchant SDK 的 verifyWebhook 校验 —— 校验与至少一次去重见 Webhooks 一节。
商户端点会收到以下事件:
| 事件 | 触发时机 |
|---|---|
order.paid |
一次结账成功扣款。 |
order.refunded |
一笔订单被退款(全额或部分)。 |
order.canceled |
一笔订单在未付款的情况下结束 —— 结账过期、被取消,或扣款失败。 |
payment_intent.captured |
底层 PaymentIntent 完成 capture(order.paid 的细粒度孪生事件)。 |
payment_intent.settled |
资金已结算(从 pending 变为可支付)。 |
payment_intent.refunded |
PaymentIntent 被退款。 |
payment_intent.failed |
尝试扣款并失败。 |
payment_intent.expired |
一次未付款的结账超过了截止时间 —— 终态,从未扣款。 |
payment_intent.canceled |
PaymentIntent 在任何扣款前被取消。 |
大多数接入需要的是订单级事件(order.*);payment_intent.* 孪生事件以更细的粒度承载相同的生命周期,用于直接对账 PaymentIntent。
投递是至少一次的 —— 按 event.id 去重,并用 2xx 应答重试(返回错误会让事件重新入队)。完整指引见 merchant SDK 的 Webhooks 一节。
任何会动钱的变更请求 —— 开启结账、capture、退款 —— 都可以通过发送 Idempotency-Key header 来安全重试(Stripe 风格)。用相同的键重试会重放原始响应,而不是把工作做两遍。
curl -X POST https://mcp.curless.ai/v1/... \ -H "Authorization: Bearer $KEY" \ -H "Idempotency-Key: order-8f3a-2026-07-19" \ -H "content-type: application/json" \ -d '{ ... }'行为:
- 相同键、相同请求 → 重放已存储的响应(相同状态与 body),并带上
Idempotent-Replayed: trueheader。工作只执行一次。 - 相同键、不同请求 body →
400 VALIDATION_ERROR(“Idempotency-Key was already used with different request parameters”)。一个键会绑定到首次使用它的那个请求。 - 相同键、原请求仍在进行中 →
409 CONFLICT(“still being processed; retry shortly”)。稍后重试。 - 处理器失败(5xx / 崩溃) → 占用会被释放,因此用相同键重试会被当作全新请求(失败永远不会“卡住”)。
键按凭据且按 mode 隔离,所以一个 test 键和一个 live 键永远不会共享同一条幂等记录(两者都从你的 Curless 钱包 拿 —— test 模式不动真钱,用于端到端测试)。为每个逻辑操作选一个唯一的键(例如你自己的 order id),而不是每次 HTTP 尝试各用一个。
注意:若干协议流程也接受协议级的幂等键,网关内部会对并发的同键 PaymentIntent 创建做去重 —— 但上面这个 header 是 REST API 的通用机制。
令牌桶限流,每 60 秒 120 次请求(RATE_LIMIT_MAX / RATE_LIMIT_WINDOW_MS),突发上限为 120。
- 已认证路由 ——
/v1/*及各协议路径 —— 按 API key(按主体)限流,所以一个商户的流量绝不会吃掉另一个商户的额度。 - 公开端点(OAuth token/authorize/register、
/mcp资源服务器、托管的/pay、/v1/integrations)按 IP 限流。
超过限额返回 429 RATE_LIMITED。退避后重试;配合 Idempotency-Key,让被重试的写操作不会重复扣款。
健康/就绪探针(/health、/ready)以及 Curless 入站 webhook 有意不限流(对它们限流会让负载均衡器误判服务已宕机,或丢掉真实的退款事件)。
以上是默认值;如果你的接入需要更高上限,请联系我们。

