Verify Payment
This is the V1 API. For new integrations we recommend Verify Payment (V2), which is conformant with the x402 v2 specification.
POST /papi/v1/b402/verify
Validates a transaction payload off-chain. No gas is consumed and no on-chain transaction is submitted. Use this endpoint to check whether a signed payment is well-formed and the signature is valid before calling /settle.
Request Body
| Field | Type | Mandatory | Remarks |
|---|---|---|---|
| x402Version | integer | Yes | x402 protocol version (currently 1) |
| paymentPayload | object | Yes | The payment payload to verify |
| paymentPayload.x402Version | integer | Yes | Protocol version (must match top-level x402Version) |
| paymentPayload.scheme | string | Yes | "exact" |
| paymentPayload.network | string | Yes | CAIP-2 network identifier (e.g. "eip155:56") |
| paymentPayload.payload | object | Yes | Signature and authorization details |
| paymentPayload.payload.signature | string | Yes | EIP-712 signature (0x-prefixed hex, 65 bytes) |
| paymentPayload.payload.authorization | object | Conditional | EIP-3009 mode only. Required when assetTransferMethod is "eip3009" |
| paymentPayload.payload.authorization.from | string | Conditional | Payer wallet address |
| paymentPayload.payload.authorization.to | string | Conditional | Recipient wallet address |
| paymentPayload.payload.authorization.value | string | Conditional | Amount in smallest unit (e.g. "1000000" for 1 USDC) |
| paymentPayload.payload.authorization.validAfter | string | Conditional | Unix timestamp in seconds — signature valid after |
| paymentPayload.payload.authorization.validBefore | string | Conditional | Unix timestamp in seconds — signature valid before |
| paymentPayload.payload.authorization.nonce | string | Conditional | Unique nonce (32-byte hex) |
| paymentPayload.payload.permit2Authorization | object | Conditional | Permit2 mode only. Required when assetTransferMethod is "permit2-exact" or "permit2-upto" |
| paymentPayload.payload.permit2Authorization.permitted | object | Conditional | Permitted token and amount |
| paymentPayload.payload.permit2Authorization.permitted.token | string | Conditional | Token contract address |
| paymentPayload.payload.permit2Authorization.permitted.amount | string | Conditional | Maximum authorized amount (smallest unit) |
| paymentPayload.payload.permit2Authorization.from | string | Conditional | Payer (token owner) address. The signer of the Permit2 signature. |
| paymentPayload.payload.permit2Authorization.spender | string | Conditional | Spender address (proxy contract). Must match facilitatorAddress from /supported. |
| paymentPayload.payload.permit2Authorization.nonce | string | Conditional | Permit2 nonce value |
| paymentPayload.payload.permit2Authorization.deadline | string | Conditional | Unix timestamp (seconds) when the permit expires |
| paymentPayload.payload.permit2Authorization.witness | object | Conditional | Witness data included in the EIP-712 signed message |
| paymentPayload.payload.permit2Authorization.witness.to | string | Conditional | Recipient address. Must match paymentRequirements.payTo. Enforced on-chain by the proxy contract. |
| paymentPayload.payload.permit2Authorization.witness.facilitator | string | Conditional | Facilitator address bound to this authorization (permit2-upto only). For permit2-exact, this field is absent. |
| paymentPayload.payload.permit2Authorization.witness.validAfter | string | Conditional | Earliest Unix timestamp (seconds) at which the transfer can execute. "0" means immediately. |
| paymentRequirements | object | Yes | Expected payment requirements from the resource server |
| paymentRequirements.scheme | string | Yes | "exact" |
| paymentRequirements.network | string | Yes | CAIP-2 network identifier |
| paymentRequirements.amount | string | Yes | Required payment amount in token's smallest unit |
| paymentRequirements.resource | string | Yes | Resource URL the payment is for |
| paymentRequirements.description | string | No | Human-readable description of the resource |
| paymentRequirements.mimeType | string | No | Expected response MIME type |
| paymentRequirements.outputSchema | object | No | Expected JSON structure of the resource response data (x402 standard field) |
| paymentRequirements.payTo | string | Yes | Merchant wallet address |
| paymentRequirements.maxTimeoutSeconds | integer | No | Maximum settlement timeout in seconds |
| paymentRequirements.asset | string | Yes | Token contract address |
| paymentRequirements.extra | object | No | Must match a kind from /supported |
| paymentRequirements.extra.name | string | No | Token name |
| paymentRequirements.extra.version | string | No | Token version (EIP-712 domain) |
| paymentRequirements.extra.assetTransferMethod | string | No | "eip3009", "permit2-exact", or "permit2-upto" |
| paymentRequirements.extra.facilitatorAddress | string | No | Facilitator address |
Merchant responsibility — forwarding
extrato buyers: The merchant MUST populatepaymentRequirements.extrawith values copied from the matchingkinds[]entry in the/supportedresponse. In particular,extra.facilitatorAddress(the Permit2 proxy contract forpermit2-*methods) must be surfaced to the buyer in the upstream HTTP 402 response — buyers have no other channel to discover it, and without it they cannot setpermit2Authorization.spendercorrectly. Omitting it will cause B402 to reject the payment withinvalid_exact_evm_permit2_payload_spender. See Forwarding facilitatorAddress to Buyers.
Conditional fields: Fields marked "Conditional" are required depending on the
assetTransferMethod:
eip3009→payload.authorization.*fields are required;payload.permit2Authorizationmust be absent.permit2-exact/permit2-upto→payload.permit2Authorization.*fields are required;payload.authorizationmust be absent.permit2-uptoonly →payload.permit2Authorization.witness.facilitatoris additionally required.
Example — EIP-3009 (U / USD1)
{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:56",
"payload": {
"signature": "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f134801234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b",
"authorization": {
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0x8B3a350e2f3E6B9cC6FB10Fd106bA08f08bec5D2",
"value": "1000000",
"validAfter": "0",
"validBefore": "1710000600",
"nonce": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:56",
"amount": "1000000",
"resource": "https://api.example.com/premium/data",
"description": "Premium API access",
"payTo": "0x8B3a350e2f3E6B9cC6FB10Fd106bA08f08bec5D2",
"asset": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
"maxTimeoutSeconds": 300,
"extra": {
"name": "USD Coin",
"version": "2",
"assetTransferMethod": "eip3009",
"facilitatorAddress": "0x1111111111111111111111111111111111111111"
}
}
}
Example — Permit2 Exact (any ERC-20)
{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:56",
"payload": {
"signature": "0xaabbccdd...65bytes...eeff",
"permit2Authorization": {
"permitted": {
"token": "0x55d398326f99059ff775485246999027b3197955",
"amount": "5000000"
},
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"spender": "0x2222222222222222222222222222222222222222",
"nonce": "1",
"deadline": "1710000600",
"witness": {
"to": "0x8B3a350e2f3E6B9cC6FB10Fd106bA08f08bec5D2",
"validAfter": "0"
}
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:56",
"amount": "5000000",
"resource": "https://api.example.com/premium/data",
"description": "Premium API access",
"payTo": "0x8B3a350e2f3E6B9cC6FB10Fd106bA08f08bec5D2",
"asset": "0x55d398326f99059ff775485246999027b3197955",
"maxTimeoutSeconds": 300,
"extra": {
"name": "Tether USD",
"version": "1",
"assetTransferMethod": "permit2-exact",
"facilitatorAddress": "0x2222222222222222222222222222222222222222"
}
}
}
Example — Permit2 Upto (pay-as-you-go)
{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "eip155:56",
"payload": {
"signature": "0x112233...65bytes...4455",
"permit2Authorization": {
"permitted": {
"token": "0x55d398326f99059ff775485246999027b3197955",
"amount": "100000000"
},
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"spender": "0x3333333333333333333333333333333333333333",
"nonce": "5",
"deadline": "1710086400",
"witness": {
"to": "0x8B3a350e2f3E6B9cC6FB10Fd106bA08f08bec5D2",
"facilitator": "0x1111111111111111111111111111111111111111",
"validAfter": "0"
}
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:56",
"amount": "100000000",
"resource": "https://ai.example.com/api/v1/chat",
"description": "AI Agent API - per-request billing",
"payTo": "0x8B3a350e2f3E6B9cC6FB10Fd106bA08f08bec5D2",
"asset": "0x55d398326f99059ff775485246999027b3197955",
"maxTimeoutSeconds": 300,
"extra": {
"name": "Tether USD",
"version": "1",
"assetTransferMethod": "permit2-upto",
"facilitatorAddress": "0x3333333333333333333333333333333333333333"
}
}
}
In this example, the buyer authorized up to 100 USDT (
"100000000"). The actual amount to charge is specified later in the/settlecall via thesettleAmountfield.
Response Body
| Field | Type | Presence | Remarks |
|---|---|---|---|
| isValid | boolean | Always | Whether the payment payload is valid |
| payer | string | Always | Recovered payer wallet address from the signature |
| invalidReason | string | Only when isValid=false | Machine-readable failure reason |
| invalidMessage | string | Only when isValid=false | Human-readable failure description |
Example — Valid Payment
{
"code": "000000",
"message": "success",
"data": {
"isValid": true,
"payer": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
}
}
Example — Invalid Payment
{
"code": "000000",
"message": "success",
"data": {
"isValid": false,
"invalidReason": "invalid_exact_evm_payload_signature_address",
"invalidMessage": "Recovered address does not match authorization.from",
"payer": "0x0000000000000000000000000000000000000000"
}
}
Notes:
- An invalid payment still returns HTTP
200withcode: "000000". Check theisValidfield indatafor the verification outcome. HTTP-level errors (400 / 401 / etc.) indicate request-level problems (missing parameters, authentication failure, etc.)./verifyis read-only and off-chain. It can be called repeatedly at no cost.- In
permit2-uptomode,/verifychecks that the authorized amount coversamount. The actual settlement amount (which may be lower) is specified in the subsequent/settlecall via thesettleAmountfield.- Calling
/verifybefore/settleis recommended but not mandatory. If you skip/verify, the/settleendpoint will still validate the payment before executing it.
InvalidReason Values
Machine-readable reasons returned in the invalidReason field when isValid is false:
| Value | Description |
|---|---|
insufficient_funds | Payer has insufficient token balance |
invalid_scheme | Unsupported payment scheme |
invalid_network | Unsupported or disabled network |
invalid_x402_version | Unsupported x402 protocol version |
invalid_payload | Malformed payment payload |
invalid_payment_requirements | Malformed payment requirements |
invalid_amount | Payment amount must be greater than zero |
invalid_pay_to_mismatch | PayTo address does not match merchant registered address |
invalid_exact_evm_payload_signature | Invalid EIP-712 signature |
invalid_exact_evm_payload_signature_address | Recovered address does not match authorization.from |
invalid_exact_evm_payload_authorization_value_too_low | Authorized amount is less than required amount |
invalid_exact_evm_payload_authorization_valid_before | Authorization has expired (validBefore <= now) |
invalid_exact_evm_payload_authorization_valid_after | Authorization is not yet valid (validAfter > now) |
invalid_exact_evm_payload_authorization_typed_data_message | EIP-712 typed data message mismatch |
invalid_exact_evm_permit2_payload_signature | Invalid Permit2 signature |
invalid_exact_evm_permit2_payload_spender | Permit2 spender does not match expected proxy contract |
invalid_exact_evm_permit2_payload_recipient | Permit2 witness.to does not match payTo |
invalid_exact_evm_permit2_payload_amount | Permit2 permitted amount is less than required amount |
invalid_exact_evm_permit2_payload_deadline | Permit2 deadline has passed |
invalid_exact_evm_permit2_payload_valid_after | Permit2 authorization is not yet valid (witness.validAfter > now) |
invalid_exact_evm_permit2_payload_allowance_required | Buyer has not approved Permit2 contract |