Skip to main content

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​

FieldTypeMandatoryRemarks
x402VersionintegerYesx402 protocol version (currently 1)
paymentPayloadobjectYesThe payment payload to verify
paymentPayload.x402VersionintegerYesProtocol version (must match top-level x402Version)
paymentPayload.schemestringYes"exact"
paymentPayload.networkstringYesCAIP-2 network identifier (e.g. "eip155:56")
paymentPayload.payloadobjectYesSignature and authorization details
paymentPayload.payload.signaturestringYesEIP-712 signature (0x-prefixed hex, 65 bytes)
paymentPayload.payload.authorizationobjectConditionalEIP-3009 mode only. Required when assetTransferMethod is "eip3009"
paymentPayload.payload.authorization.fromstringConditionalPayer wallet address
paymentPayload.payload.authorization.tostringConditionalRecipient wallet address
paymentPayload.payload.authorization.valuestringConditionalAmount in smallest unit (e.g. "1000000" for 1 USDC)
paymentPayload.payload.authorization.validAfterstringConditionalUnix timestamp in seconds — signature valid after
paymentPayload.payload.authorization.validBeforestringConditionalUnix timestamp in seconds — signature valid before
paymentPayload.payload.authorization.noncestringConditionalUnique nonce (32-byte hex)
paymentPayload.payload.permit2AuthorizationobjectConditionalPermit2 mode only. Required when assetTransferMethod is "permit2-exact" or "permit2-upto"
paymentPayload.payload.permit2Authorization.permittedobjectConditionalPermitted token and amount
paymentPayload.payload.permit2Authorization.permitted.tokenstringConditionalToken contract address
paymentPayload.payload.permit2Authorization.permitted.amountstringConditionalMaximum authorized amount (smallest unit)
paymentPayload.payload.permit2Authorization.fromstringConditionalPayer (token owner) address. The signer of the Permit2 signature.
paymentPayload.payload.permit2Authorization.spenderstringConditionalSpender address (proxy contract). Must match facilitatorAddress from /supported.
paymentPayload.payload.permit2Authorization.noncestringConditionalPermit2 nonce value
paymentPayload.payload.permit2Authorization.deadlinestringConditionalUnix timestamp (seconds) when the permit expires
paymentPayload.payload.permit2Authorization.witnessobjectConditionalWitness data included in the EIP-712 signed message
paymentPayload.payload.permit2Authorization.witness.tostringConditionalRecipient address. Must match paymentRequirements.payTo. Enforced on-chain by the proxy contract.
paymentPayload.payload.permit2Authorization.witness.facilitatorstringConditionalFacilitator address bound to this authorization (permit2-upto only). For permit2-exact, this field is absent.
paymentPayload.payload.permit2Authorization.witness.validAfterstringConditionalEarliest Unix timestamp (seconds) at which the transfer can execute. "0" means immediately.
paymentRequirementsobjectYesExpected payment requirements from the resource server
paymentRequirements.schemestringYes"exact"
paymentRequirements.networkstringYesCAIP-2 network identifier
paymentRequirements.amountstringYesRequired payment amount in token's smallest unit
paymentRequirements.resourcestringYesResource URL the payment is for
paymentRequirements.descriptionstringNoHuman-readable description of the resource
paymentRequirements.mimeTypestringNoExpected response MIME type
paymentRequirements.outputSchemaobjectNoExpected JSON structure of the resource response data (x402 standard field)
paymentRequirements.payTostringYesMerchant wallet address
paymentRequirements.maxTimeoutSecondsintegerNoMaximum settlement timeout in seconds
paymentRequirements.assetstringYesToken contract address
paymentRequirements.extraobjectNoMust match a kind from /supported
paymentRequirements.extra.namestringNoToken name
paymentRequirements.extra.versionstringNoToken version (EIP-712 domain)
paymentRequirements.extra.assetTransferMethodstringNo"eip3009", "permit2-exact", or "permit2-upto"
paymentRequirements.extra.facilitatorAddressstringNoFacilitator address

Merchant responsibility — forwarding extra to buyers: The merchant MUST populate paymentRequirements.extra with values copied from the matching kinds[] entry in the /supported response. In particular, extra.facilitatorAddress (the Permit2 proxy contract for permit2-* 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 set permit2Authorization.spender correctly. Omitting it will cause B402 to reject the payment with invalid_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.permit2Authorization must be absent.
  • permit2-exact / permit2-upto → payload.permit2Authorization.* fields are required; payload.authorization must be absent.
  • permit2-upto only → payload.permit2Authorization.witness.facilitator is 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 /settle call via the settleAmount field.

Response Body​

FieldTypePresenceRemarks
isValidbooleanAlwaysWhether the payment payload is valid
payerstringAlwaysRecovered payer wallet address from the signature
invalidReasonstringOnly when isValid=falseMachine-readable failure reason
invalidMessagestringOnly when isValid=falseHuman-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 200 with code: "000000". Check the isValid field in data for the verification outcome. HTTP-level errors (400 / 401 / etc.) indicate request-level problems (missing parameters, authentication failure, etc.).
  • /verify is read-only and off-chain. It can be called repeatedly at no cost.
  • In permit2-upto mode, /verify checks that the authorized amount covers amount. The actual settlement amount (which may be lower) is specified in the subsequent /settle call via the settleAmount field.
  • Calling /verify before /settle is recommended but not mandatory. If you skip /verify, the /settle endpoint will still validate the payment before executing it.

InvalidReason Values​

Machine-readable reasons returned in the invalidReason field when isValid is false:

ValueDescription
insufficient_fundsPayer has insufficient token balance
invalid_schemeUnsupported payment scheme
invalid_networkUnsupported or disabled network
invalid_x402_versionUnsupported x402 protocol version
invalid_payloadMalformed payment payload
invalid_payment_requirementsMalformed payment requirements
invalid_amountPayment amount must be greater than zero
invalid_pay_to_mismatchPayTo address does not match merchant registered address
invalid_exact_evm_payload_signatureInvalid EIP-712 signature
invalid_exact_evm_payload_signature_addressRecovered address does not match authorization.from
invalid_exact_evm_payload_authorization_value_too_lowAuthorized amount is less than required amount
invalid_exact_evm_payload_authorization_valid_beforeAuthorization has expired (validBefore <= now)
invalid_exact_evm_payload_authorization_valid_afterAuthorization is not yet valid (validAfter > now)
invalid_exact_evm_payload_authorization_typed_data_messageEIP-712 typed data message mismatch
invalid_exact_evm_permit2_payload_signatureInvalid Permit2 signature
invalid_exact_evm_permit2_payload_spenderPermit2 spender does not match expected proxy contract
invalid_exact_evm_permit2_payload_recipientPermit2 witness.to does not match payTo
invalid_exact_evm_permit2_payload_amountPermit2 permitted amount is less than required amount
invalid_exact_evm_permit2_payload_deadlinePermit2 deadline has passed
invalid_exact_evm_permit2_payload_valid_afterPermit2 authorization is not yet valid (witness.validAfter > now)
invalid_exact_evm_permit2_payload_allowance_requiredBuyer has not approved Permit2 contract