curl -G "https://ifandonlyif.io/api/v3/evidence/check" \
--data-urlencode \
"url=https://api.example.com/paid-resource"
observed 卡片可在拥有者认领前公开,条件是 endpoint 启用、未 opt-out 且已有 discovery observation。卡片带有“拥有权未验证”标示与固定 disclaimer。verified tier 首次发布需完成拥有权验证,且至少三笔通过的 initial 或 scheduled observation。404 表示没有公开卡片,不是安全 verdict。查询只读取已储存的证据,不启动 probe。
首页的 提交以供监测 会呼叫 POST /api/v3/discovery/submit,内容为 {"url":"https://api.example.com/paid"}。此公开 route 需启用 discovery ingestion,除通用 rate limit 外,每个 IP 每分钟最多 10 次提交。202 加 queued 表示已记录 candidate(立即排入伫列仅尽力而为);known 不会再次排入。两者都不保证监测或发布。worker 观察前会套用 robots.txt、opt-out、资格及 backoff 规则。payment-network selector 只比较已观察的选项,不限制 worker 验证哪些选项。
提交后,页面每五秒轮询一次 GET /api/v3/discovery/status?url=…,最多一分钟,可用时载入公开卡片。此唯读 route 返回 {"url":"…","status":"pending"},可能附带 retry_after_seconds。其他状态:available、deferred、unavailable、unobserved、disabled。no-store,每个 IP 每分钟 30 次。查看进度 可不重新提交而继续读取。延迟、错误与 unavailable 会停止自动检查。拒绝与 opt-out 细节保持私密。
使用者发起的查询返回 404 时,会读取一次提交状态。只有已确认的 unobserved 会提供提交;pending 或 deferred 则提供进度。URL 格式有效不等于具备监测资格。查询不会自动提交,提交也不要求认领拥有权。
成功回应封装
{
"success": true,
"data": {
"endpoint": {
"id": "ENDPOINT_UUID",
"url": "https://api.example.com/paid-resource",
"name": "Example x402 API"
},
"operational_status": "passing",
"ownership": {
"status": "verified",
"method": "iff-well-known"
},
"protocol": {
"status": "pass",
"version": 2,
"payment_options": [],
"check_codes": ["x402_v2_valid"]
},
"availability": [
{
"window": "24h",
"samples": 288,
"reachable_rate": 100,
"protocol_pass_rate": 99.7,
"p50_latency_ms": 84,
"p95_latency_ms": 190
}
],
"freshness": {
"status": "fresh",
"observed_at": "2026-07-12T04:30:00Z",
"interval_seconds": 300
},
"evidence": {
"report_hash": "HEX_SHA256",
"monitor_id": "iff-production-monitor",
"monitor_version": "3.0.0",
"monitor_signature_algorithm": "ML-DSA-65",
"monitor_signature": "BASE64_ML_DSA_65_SIGNATURE",
"monitor_public_key": "BASE64_ML_DSA_65_PUBLIC_KEY"
}
}
}
02 · Authentication
使用钱包登入。
拥有者设置使用 API,没有 wallet-connect dashboard。你需要能产生 65-byte EIP-191 personal_sign signature 的 Ethereum externally owned account(EOA),以及 endpoint origin 的部署存取权。不支援 contract-wallet signature(EIP-1271 / EIP-6492)。拥有者 route 使用登入 challenge 后核发的短效 access token。
-
要求 challenge
challenge 会绑定 domain、URI、chain、wallet、nonce 与到期时间。
-
签署完全相同的讯息
以同一钱包地址,将返回的
message原封不动交给personal_sign。请在提示中核对 domain、wallet 与 expiry。这是登入 signature,不是 payment 或 token approval。IFF 永远不需要你的 private key 或 recovery phrase。 -
交换并轮替
取得
access_token、refresh_token、token_type与expires_in。下方范例以$ACCESS_TOKEN代表 access token。每个 refresh token 仅能使用一次:refresh 后请替换两个 token。
curl "https://ifandonlyif.io/api/v3/auth/challenge" \
-H "content-type: application/json" \
-d '{"wallet_address":"0xYOUR_WALLET"}'
curl "https://ifandonlyif.io/api/v3/auth/verify" \
-H "content-type: application/json" \
-d '{
"challenge_id":"CHALLENGE_UUID",
"wallet_address":"0xYOUR_WALLET",
"message":"EXACT_MESSAGE_FROM_SERVER",
"signature":"0xSIGNATURE"
}'
curl 无法签署,signature 来自你的钱包。上例是 request template。请提交原始的 challenge_id 与 message 及钱包的 signature,并以 JSON serializer 建立 body,例如 JSON.stringify({ challenge_id: challenge.challenge_id, wallet_address: wallet, message: challenge.message, signature })。不要将 literal newline 贴入 JSON,也不要自行重建 message。token 请勿出现在 URL、截图、source code 与 shell history。
curl "https://ifandonlyif.io/api/v3/auth/refresh" \
-H "content-type: application/json" \
-d '{"refresh_token":"REFRESH_TOKEN"}'
challenge 五分钟后到期且只能使用一次。拥有者回应使用 Cache-Control: no-store。
03 · 拥有权
注册并证明对 endpoint 的控制权。
注册只接受 port 443 的公开 HTTPS endpoint,且不得含 credentials、query string、fragment 或 redirect。它建立 claim attempt,不授予拥有者存取权。
回应含唯一的 claim_attempt_id 与 ownership object(challenge、expires_at、verification_url、document)。challenge 有效 24 小时。任何已验证钱包都可建立独立 attempt;pending attempt 不能读取 private observation、手动 probe、管理 webhook、连结 ERC-8004 或取消发布。
curl "https://ifandonlyif.io/api/v3/endpoints" \
-H "authorization: Bearer $ACCESS_TOKEN" \
-H "content-type: application/json" \
-d '{
"url":"https://api.example.com/paid-resource",
"name":"Example x402 API",
"description":"Payment-gated resource"
}'
201 Created:新 endpoint 与 claim attempt。200 OK:既有 endpoint 新增 attempt。请将 endpoint_id 存为 ENDPOINT_UUID,并保留 claim_attempt_id 供 polling。两者都不排入 verification,也不授予 owner access。等待时请勿重复注册。
对於单一 endpoint,请将回应中的 ownership.document 原封不动发布在 ownership.verification_url,例如:
https://api.example.com/.well-known/iff-trust.json同一 origin 的 endpoint 共用此档案。新增前请备份目前的 document。将既有 single claim 包成 {"claims":[...]} 的第一项并附加新的 ownership.document,或直接附加到既有 array。保留其他 endpoint 的 claim 与完全相同的 challenge。切勿以最新回应取代整个档案;IFF 不会合并。
旧版 single-claim format 仍受支援。multi-endpoint format 只有一个 top-level field claims:1–64 个 object,每个恰含 endpoint_id、owner_wallet 与 challenge,使用 API 提供的非空值(canonical lowercase UUID、Ethereum wallet address、完全相同的 challenge)。wallet 比对忽略大小写。body 须在 16 KiB 以内。unknown 或重复的 JSON field、重复 endpoint ID、混用 format 或任何 malformed entry 都会使整个档案被拒绝。更新时只替换该 endpoint 的项目。请持续保留有效 claim,供定期 domain-control recheck。
curl -X POST \
"https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID/verify-ownership" \
-H "authorization: Bearer $ACCESS_TOKEN"
此 verification request 返回 202 Accepted:job 已排入伫列,尚未验证。请遵循 Retry-After(目前为 5 秒),再用同一钱包的 access token polling:
curl "https://ifandonlyif.io/api/v3/endpoints?page=1&page_size=20" \
-H "authorization: Bearer $ACCESS_TOKEN"
于 data.claim_attempts[].id 找到已保存的 attempt ID(必要时查看后续页面),读取 status、outcome_code 与 verification_failures。pending 不是成功;verified 表示 proof 通过;failed、expired、superseded 已结案。data.endpoints 列出 authoritative ownership。后续注册不会轮替另一钱包的 challenge。只有成功的 same-origin proof 可取代 owner;transfer 会停用旧 webhook 并重设发布资格。
检查失败后 attempt 仍为 pending 时,请修正已部署的档案,并在 24 小时内重新排入 verification。attempt 已到期或结案时,请重新注册该 URL、发布新 document 并排入新 attempt。verification 返回 409:没有可用的 pending attempt。503:queue 不可用,不代表通过;请以 backoff 重试。401:更新 token 或重新登入。
拥有权验证通过后
owner access 与首次发布是不同步骤。verified-tier card 在 ownership verification 完成且至少三笔通过的 initial 或 scheduled observation 后才公开;manual probe 不计入。等待期间公开 404 不代表 ownership 验证失败。请用仅限 owner 的 history 诊断:
curl "https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID/observations?page=1&page_size=20" \
-H "authorization: Bearer $ACCESS_TOKEN"
读取 data.observations 与各 probe_type。要求 private diagnostic 时,执行下方命令,再 polling 此 history 等候新的 manual observation。202 仅确认已排入伫列。manual 结果不会变更 public card、发布资格、manifest 或通知。
curl -X POST \
"https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID/probes" \
-H "authorization: Bearer $ACCESS_TOKEN"
接著请设置变更通知。要停止监测并隐藏卡片,请使用下方仅限 owner 的动作。它也会停用 webhook 与 pending delivery,但保留 immutable observation、claim-attempt audit 与 transparency-log entry。快取的卡片最多可再显示 30 秒。要恢复,请重新注册该 URL 并再次证明控制权。
curl -X DELETE \
"https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID" \
-H "authorization: Bearer $ACCESS_TOKEN"
成功停止返回 204 No Content。wallet 没有目前的 verified 或 stale claim 时,owner management route 返回 404(仅有 pending attempt 不足)。claim 为 stale 时,history 与停止仍可用,但建立 webhook 或 manual probe 会返回 409,直到 ownership 再次验证。
ownership 只证明对受监测 origin 的控制权,不证明谁操作 backend、付款后会发生什么,或 endpoint 是否安全。
04 · Reference
endpoint 参考数据。
以 :id 结尾的路径参数为 UUID。分页接受 page 与最大 100 的 page_size。每个 GET route 也回应 HEAD。公开 route 与 POST /api/v3/verify 返回 Access-Control-Allow-Origin: *;需 authentication 的 route 不返回。
公开与验证
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /health | Liveness 与依赖是否可用的概略布林值(不含服务名称、版本或元件标签)。 |
| GET | /api/v3/evidence/check?url=… | 依 normalized endpoint URL 取得已发布的 Evidence Card。 |
| GET | /api/v3/evidence/endpoints/:id | 依 endpoint ID 取得已发布的 Evidence Card。 |
| GET | /api/v3/evidence/manifests/:id | 依 manifest ID 取得附有 ETag、不可变的每日 evidence manifest。它与上述 endpoint ID 使用不同的 ID 空间。请从 Evidence Card 的 anchor.manifest_uri 取得 manifest ID;若在此传入 endpoint ID,会返回 404 并说明原因。 |
| GET | /api/v3/log/anchors/latest | 已签署 transparency-log tree head 最新提交或已确认的 ERC-8004 anchor。 |
| POST | /api/v3/auth/challenge | 建立一次性的 wallet challenge。 |
| POST | /api/v3/auth/verify | 验证 EIP-191 signature 并签发 token pair。 |
| POST | /api/v3/auth/refresh | 轮替只能使用一次的 refresh token。 |
| POST | /api/v3/verify | 将收到的 x402 requirement 与独立 observation 进行 Preflight 比较。 |
| GET | /api/v3/receipts/keys | 可选实验性功能 Service Receipt v2 目前使用与保留的 public key(v1 key 列为 previous)。 |
| GET | /api/v3/badge/:id.json | 用于 shields.io endpoint badge 的 JSON,描述 endpoint 目前的 requirement consistency。 |
已验证的钱包与 endpoint 拥有者
下方每个 route 都需要 Authorization: Bearer <access_token>。registration、polling 与 verification 限建立 attempt 的 wallet;management route 需 verified 或 stale ownership。
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /api/v3/endpoints | 建立独立的 claim attempt 与 ownership challenge,不会授予权限。 |
| GET | /api/v3/endpoints | 列出具有效拥有权的 endpoint 与此钱包的 claim-attempt 稽核状态。 |
| DELETE | /api/v3/endpoints/:id | 取消发布 endpoint,并保留不可变的 evidence。 |
| POST | /api/v3/endpoints/:id/verify-ownership | 将 well-known ownership verification 排入伫列。 |
| POST | /api/v3/endpoints/:id/probes | 将仅限 owner 的 manual observation 排入伫列。 |
| GET | /api/v3/endpoints/:id/observations | 分页读取完整的 observation history。 |
| POST | /api/v3/endpoints/:id/webhooks | 建立用于 status 或 requirement 变更的 owner Webhook;secret 只会返回一次。 |
| GET | /api/v3/endpoints/:id/webhooks | 列出作用中的 target,不含 secret。 |
| DELETE | /api/v3/endpoints/:id/webhooks/:webhookId | 停用 Webhook 与待传送项目。 |
| POST | /api/v3/endpoints/:id/erc8004 | 连结 owner 持有的 ERC-8004 agent identity。 |
05 · 回应模型
分别解读各项维度。
ownership- 目前的 domain-control 状态、方式与验证时间。
protocol- 最新的 x402 解析结果、版本、各项具名检查与宣告的 payment option。
availability- 24 小时与 7 天的样本、百分比与 p50/p95 latency。
freshness- 最新 observation 是否仍在发布所需的 freshness window 内。
evidence- Canonical report hash、monitor identity、ML-DSA-65 signature(旧 report 为 Ed25519)与 public key。
anchor- 可选的每日 manifest 链上状态与 transaction provenance。
rate 为 0 至 100 的百分比。缺少的 measurement 会略去或为 null;请勿推论为通过。
observed tier 的 probe 也会按 UTC 日计数。每个结束的日子会封存成一份每日确认文件,其 hash 以一个 leaf 进入 transparency log;启用 change-only storage 后,只有 observed state 变动时才保存完整的签名 observation。GET /api/v3/evidence/observed-confirmations/:day(或 /latest)返回原始 body、document_hash、leaf_hash 与承诺等级:sealed、logged 或 anchored。这是 monitor 观测到的统计,不证明每次 probe 都实际执行。
06 · Webhook
验证每一次传送。
衍生的 operational status 改变时,owner subscription 收到 endpoint.status_changed;合格 observation 显示不同 payment requirement fingerprint 时,收到 endpoint.requirement_changed(不证明攻击)。manual probe 不触发任何事件;observed-tier card 不授予 subscription 权限。
curl "https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID/webhooks" \
-H "authorization: Bearer $ACCESS_TOKEN" \
-H "content-type: application/json" \
-d '{"target_url":"https://hooks.example.com/iff"}'
建立订阅需 verified ownership,返回 201 Created,含 id、target_url 与 secret。请将 ID 存为 WEBHOOK_UUID,并把 secret 存放于接收端服务器;secret 只显示一次,无法取回。target 须为公开 HTTPS/443,不得含 credentials、query string、fragment 或 redirect。作用中 target 重复或作用中订阅超过 10 个,返回 409。
建立订阅不传送测试事件。事件随符合资格的 observation 变更而触发,不随每次 probe。依赖通知前,请先验证下列 header。
X-IFF-Timestamp: 1700000000
X-IFF-Signature: sha256=<hex digest>
Idempotency-Key: <delivery uuid>
- 拒绝超出允许 clock-skew 范围的 timestamp。
- 使用仅显示一次的 secret,对
timestamp + "." + raw_body计算 HMAC-SHA256。 - 以 constant-time 比较 hex digest。
- 依
Idempotency-Key去重;重复传送仍应回应 2xx。
curl "https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID/webhooks" \
-H "authorization: Bearer $ACCESS_TOKEN"
curl -X DELETE \
"https://ifandonlyif.io/api/v3/endpoints/ENDPOINT_UUID/webhooks/WEBHOOK_UUID" \
-H "authorization: Bearer $ACCESS_TOKEN"
清单位于 data。删除返回 204 No Content,停止待传送项目,不取消发布 endpoint。遗失 secret 时请删除并重建订阅。切勿在 client-side code 暴露 secret。
07 · Preflight
付款前先检查。
对收到的 x402 payment requirement 采取行动前,先与 IFF 独立且已签署的 observation 比较。它不 probe URL,且仅在尚无 observation 时储存 normalized URL。verdict 为 consistent、diverged、unobserved 或 stale:没有 composite score,也不作 safety、delivery 或 trust 主张。
curl "https://ifandonlyif.io/api/v3/verify" \
-H "content-type: application/json" \
-d '{
"url":"https://api.example.com/v1/quote",
"payment_required":{"x402Version":2,"accepts":[{
"scheme":"exact",
"network":"eip155:8453",
"asset":"0xab12cd34ab12cd34ab12cd34ab12cd34ab12cd34",
"amount":"1000",
"payTo":"0xef56ab78ef56ab78ef56ab78ef56ab78ef56ab78",
"maxTimeoutSeconds":60
}]}
}'
- TypeScript SDK
npm install @ifandonlyif/x402-preflight— 使用verify()、wrapFetch()或内含的 MCP command。阅读 SDK 指南.- Direct API
- 可从任何程序语言使用
POST /api/v3/verify。公开 Preflight 检查不需要 API key。 - Badge

实验性功能展示:Service Receipt v2
Service Receipt v2 是单一 verification result 的可选 signed artifact,不属于 core preflight contract。它将 response、verification-request digest 与五分钟 action window 绑定于 IFF 专用 ML-DSA-65 receipt key。略去 receipt 或传送 null 不改变 response;{} 或 "version":"2" 签发 v2,"1" 返回 422。既有 v1 receipt 仍可验证。
"receipt":{"version":"2","nonce":"checkout_7f3a"}
- 已签署的 artifact
- 成功选用此功能后会新增
service_receipt,不会变更其他 response field。 - 本机 verifier
- 将独立 envelope 或完整 API response 贴入Receipt 展示页(英文)。验证会在浏览器本机执行。
curl "https://ifandonlyif.io/api/v3/receipts/keys"
Service Receipt 不证明 payment、service delivery、caller identity、remote execution、result correctness 或 safety,只记录某个 receipt key 签署了内含的 result。
有效的 ML-DSA-65(v2)或 Ed25519(v1)signature 表示内含的 key 签署了 receipt(provenance,不是 attestation),但不表示该 key 属于声称的 issuer。符合 same-origin HTTPS key directory 的 key 可辨识;最严格的 policy 还会比对确切 issuer 与独立钉选的完整 sha256: fingerprint。解码后的 signed subject 才具权威,须与外层 response 比较。引用的 evidence 在其专属 proof 通过前仍未验证。IFF 目前将 compute_proof 设为 null。
08 · 错误与限制
失败时不要推论证据。
{
"error": true,
"message": "human-readable reason"
}
| 状态 | 含义 |
|---|---|
400 | JSON、UUID、URL、分页或输入结构格式错误。 |
401 | token 缺少、无效、过期、重放或类型错误。 |
403 | 已通过 authentication 的钱包不拥有所请求的资源。 |
404 | 公开 evidence 或所请求的 owner 资源不存在。 |
409 | ownership 状态、重复 target 或资源状态冲突。 |
422 | x402 requirement、receipt version 或 receipt nonce 无效。 |
429 | 已达 rate limit;请以 backoff 重试。 |
503 | Evidence repository、queue、registry、receipt 签发或其他依赖不可用。 |
IFF 是外部 observer。它不执行 paid canary transaction、不作 payment-safety 主张、不保证付款后的 delivery,也不判定 endpoint 是否值得信任。monitor signature 显示 provenance 与 tamper evidence,不是 attestation。