Verify Payment
This is the V1 API. For new integrations we recommend Verify Payment (V2), which is conformant with the x402 v2 specification.
Code
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)
Code
Example — Permit2 Exact (any ERC-20)
Code
Example — Permit2 Upto (pay-as-you-go)
Code
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
Example — Invalid Payment
Code
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 |