Incarna文档GitHub控制台

x402 —— 付费调用

其他所有接口都是向人收费。这一个是在完成工作的同一个请求里,向调用方收费。

一个 agent 调用某个端点,拿到 402 Payment Required 和一份机器可读的条款, 签署一份支付授权,带着 X-PAYMENT 头重试,动作随即执行 —— 没有发票,没有套餐,事先也不需要建立任何计费关系。 密钥依然是需要的,因为这个动作操作的是某个特定客户的身份; 见支付是计量,不是认证

这就是 x402,一个开放协议。Incarna 是收款方。

网络        eip155:84532  (Base Sepolia)
资产        USDC
Facilitator https://x402.org/facilitator
收款地址    0xCa1fBb1900e1C17Cc443e34f312720960E72a83F

测试网。 价格以真实美元计价,但今天在 Base Sepolia 的 USDC 上结算。 主网是一个单独的决定,不是翻一个开关。

支付是计量,不是认证

值得直说,因为相反的假设是一个安全漏洞:

付钱不会让你拿到别人的账号。

一次身份动作操作的是某个特定客户的身份,所以付费路由仍然要解析出调用主体。 认证回答的是这个身份可不可以行动;支付回答的是谁为它买单。 两者彼此独立,而且缺一不可。

发现价格

curl $BASE/x402/tools
{
  "enabled": true,
  "network": "eip155:84532",
  "tools": {
    "identity.x.post": "$0.05",
    "identity.email.send": "$0.02",
    "identity.email.inbox": "$0.01",
    "identity.github.act": "$0.05",
    "identity.reddit.act": "$0.05"
  }
}

不在这里列出的工具无法被收费 —— 正是这一点让一个新加的端点不会悄悄地以免费形式上线。

GET /x402/quote/{tool} 会返回某个工具的完整 402 文档,不需要发起真实请求, 这样付款方可以提前查看条款。

付费端点

端点 工具 价格
POST /x402/agents/{id}/accounts/{acct}/tweet identity.x.post $0.05
POST /x402/agents/{id}/email/send identity.email.send $0.02
POST /x402/agents/{id}/inbox identity.email.inbox $0.01
POST /x402/agents/{id}/accounts/{acct}/github identity.github.act $0.05
POST /x402/agents/{id}/accounts/{acct}/reddit identity.reddit.act $0.05

请求体与 REST 参考中对应的免费端点完全一致。

一次完整交换

1 —— 不带支付调用

curl -X POST $BASE/x402/agents/$AGENT/email/send \
  -H 'Content-Type: application/json' \
  -d '{"to":"someone@example.com","subject":"Hi","body":"..."}'
{
  "x402Version": 2,
  "error": "payment required",
  "resource": {
    "url": "https://api.incarna.io/x402/agents/{agent_id}/email/send",
    "description": "Send email from the agent's own inbox. Body: {\"to\", \"subject\", \"body\"}. POST with an Authorization: Bearer key for the org that owns the agent — payment meters the action, it does not authorise it.",
    "mimeType": "application/json",
    "serviceName": "Incarna",
    "tags": ["identity", "agents", "email"]
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
      "amount": "20000",
      "payTo": "0xCa1fBb1900e1C17Cc443e34f312720960E72a83F",
      "maxTimeoutSeconds": 120
    }
  ]
}

amount 使用该资产自身的小数位 —— USDC 是 6 位,所以 20000 就是 $0.02。

resource 这一段同时充当服务的目录条目,一个从没听说过 Incarna 的 agent 正是靠它找到我们 —— 所以 url 就是它接下来要调用的路径。它是一个模板:{agent_id}{account_id} 是调用方自己的, 而 x402 的 ResourceInfo 没有可以声明它们的 schema 字段, 这就是为什么 description 里点名了它们,并说明仍然需要 bearer 令牌。

2 —— 签名并重试

from x402 import x402Client
from x402.schemas.payments import PaymentRequired

required = PaymentRequired.model_validate(r.json())
payload  = await client.create_payment_payload(required)
header   = base64.b64encode(
    json.dumps(payload.model_dump(mode="json", by_alias=True)).encode()).decode()

r2 = await http.post(url, headers={**headers, "X-PAYMENT": header}, json=body)

这份授权是 EIP-3009 的 transferWithAuthorization —— 一个签名,不是一次转账。 在结算之前什么都不会动。

3 —— 回执

一次成功的调用返回 200、动作本身的正常响应体,外加一个 base64 编码的 X-PAYMENT-RESPONSE 头:

{
  "success": true,
  "transaction": "0xd8b176786a3bbbf7b5f2b9ee66512e9b0ecec8be43f075c2057f1d551579403a",
  "network": "eip155:84532"
}

验证 → 执行 → 结算

这个顺序是这里最值得了解的设计决定。

验证支付 ────────► 执行动作 ────────► 结算支付
     │                 │                  │
  无效则            这里失败,          钱只在工作
  直接拒绝          则不扣费            发生之后才动

支付在动作之前验证,在动作之后结算。支付无效的调用方在任何工作开始之前就被拒绝; 动作失败的调用方不会被收费。「已经干完活但还没收到钱」的那个窗口由我们承担, 而不是客户承担 —— 这个方向才是对的。

maxTimeoutSeconds 设为 120,是为了让一次真实世界里较慢的写入 (比如通过住宅连接发出的一条帖子)能在授权有效期内从容完成。 设得太短,付款方会发现结算时授权已经过期。

账本

curl "$BASE/x402/payments?agent_id=$AGENT&limit=50" \
  -H "Authorization: Bearer $INCARNA_KEY"

每一笔已结算的支付都连同它的链上交易一起记录。金额以字符串存储 —— 钱永远不是浮点数。 结算按交易哈希幂等,所以重复的回执不会被重复计数。

记录一笔支付从不导致请求失败。 在写账本的时候,动作已经发生、钱已经动了; 把一个记账错误变成客户可见的 500,会让一次成功且已付费的动作看起来像失败了。

一次完整运行

针对生产环境的真实输出,为一封确实发出去了的邮件付款:

payer 0x9c0071bc0F70C45565d42a9469C05bad1dCEDc75  USDC 1.990000
payee 0xCa1fBb1900e1C17Cc443e34f312720960E72a83F  USDC 0.010000

[1] unpaid   → HTTP 402   terms: 20000 USDC on eip155:84532
[2] signed   → X-PAYMENT 1296 bytes
[3] paid     → HTTP 200   {"message_id":"<...@email.amazonses.com>"}
[4] settled  → tx 0x57453d22acbb24bb4f7bce3e0f004cacc534087b942d271265314b8f976d6f0a

[5] payer USDC 1.990000 → 1.970000
    payee USDC 0.010000 → 0.030000

回执是一项主张;余额才是证据。