x402 payment instructions and retry
See what each x402 payment header means, who creates it, and when to forward it.
x402 lets a payer retry a protected HTTP request after the endpoint returns HTTP 402 Payment Required. Your application forwards the three payment headers. It does not create the payer's payment proof.
| Header | Created by | Forwarded by | Sent to |
|---|---|---|---|
PAYMENT-REQUIRED | Recuut | Your application, unchanged | The payer, with HTTP 402 |
PAYMENT-SIGNATURE | The payer's wallet | Your application, unchanged | Recuut, on the retry |
PAYMENT-RESPONSE | Recuut | Your application, unchanged | The payer, after a successful payment |
The exchange has five steps:
- The payer calls your protected endpoint without payment proof.
- Your application asks Recuut for access. Recuut returns HTTP
402andPAYMENT-REQUIRED. Return both to the payer unchanged. - The payer's wallet reads the instructions, creates the payment proof, and retries the same endpoint with
PAYMENT-SIGNATURE. - Your application forwards that exact
PAYMENT-SIGNATUREvalue to Recuut, does the protected work, and tells Recuut whether the work succeeded or failed. - If payment succeeds, Recuut returns
PAYMENT-RESPONSE. Return that exact value with the protected result.
Important: the merchant never creates, signs, edits, or guesses PAYMENT-SIGNATURE. It comes from the payer's wallet. Do not log any of these header values.
What the encoded headers contain
Each header is base64 text. Base64 turns JSON into text that fits in an HTTP header. It is not encryption. Copy the encoded value exactly; decode it only when you need to inspect a custom HTTP integration. The examples below use x402 version 2, Base Sepolia (eip155:84532), and the same envelope version on the challenge and payer proof.
PAYMENT-REQUIRED
Recuut creates this header when payment is needed. Your application returns it to the payer with the HTTP 402 response. Its decoded accepts entry names the network, USDC contract, amount, recipient, timeout, and exact payment scheme.
Inspect the complete encoded and decoded challenge
Encoded header value:
PAYMENT-REQUIRED: eyJhY2NlcHRzIjpbeyJhbW91bnQiOiIxMDAwMCIsImFzc2V0IjoiMHgwMzZDYkQ1Mzg0MmM1NDI2NjM0ZTc5Mjk1NDFlQzIzMThmM2RDRjdlIiwiZXh0cmEiOnsibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9LCJtYXhUaW1lb3V0U2Vjb25kcyI6MzAwLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMyIiwicGF5VG8iOiIweDIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIiLCJzY2hlbWUiOiJleGFjdCJ9XSwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVkIiwiZXh0ZW5zaW9ucyI6eyJyZWN1dXQtcGF5bWVudC1yZXF1ZXN0Ijp7ImluZm8iOnsiaWQiOiJwcnFfMDAwMDAwMDAwMDAwNzAwMDgwMDAwMDAwMDAwMDAwMDAifSwic2NoZW1hIjp7InJlcXVpcmVkIjpbImlkIl0sInR5cGUiOiJvYmplY3QifX19LCJyZXNvdXJjZSI6eyJkZXNjcmlwdGlvbiI6IkdlbmVyYXRlIHBhaWQgdGV4dC4iLCJtaW1lVHlwZSI6ImFwcGxpY2F0aW9uL2pzb24iLCJ1cmwiOiJodHRwczovL21lcmNoYW50LmV4YW1wbGUvZ2VuZXJhdGUifSwieDQwMlZlcnNpb24iOjJ9Decoded JSON:
{ "accepts": [ { "amount": "10000", "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "extra": {"name": "USDC", "version": "2"}, "maxTimeoutSeconds": 300, "network": "eip155:84532", "payTo": "0x2222222222222222222222222222222222222222", "scheme": "exact" } ], "error": "Payment required", "extensions": { "recuut-payment-request": { "info": {"id": "prq_00000000000070008000000000000000"}, "schema": {"required": ["id"], "type": "object"} } }, "resource": { "description": "Generate paid text.", "mimeType": "application/json", "url": "https://merchant.example/generate" }, "x402Version": 2}PAYMENT-SIGNATURE
The payer's wallet creates this header after it accepts one option from PAYMENT-REQUIRED. Your application receives it on the retry and forwards it unchanged to Recuut. It is missing on the first request and present on the paid retry.
Inspect the complete encoded and decoded payer proof
Encoded header value:
PAYMENT-SIGNATURE: eyJhY2NlcHRlZCI6eyJhbW91bnQiOiIxMDAwMCIsImFzc2V0IjoiMHgwMzZDYkQ1Mzg0MmM1NDI2NjM0ZTc5Mjk1NDFlQzIzMThmM2RDRjdlIiwiZXh0cmEiOnsibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9LCJtYXhUaW1lb3V0U2Vjb25kcyI6MzAwLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMyIiwicGF5VG8iOiIweDIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIiLCJzY2hlbWUiOiJleGFjdCJ9LCJleHRlbnNpb25zIjp7InJlY3V1dC1wYXltZW50LXJlcXVlc3QiOnsiaW5mbyI6eyJpZCI6InBycV8wMDAwMDAwMDAwMDA3MDAwODAwMDAwMDAwMDAwMDAwMCJ9fX0sInBheWxvYWQiOnsiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHgxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExIn19LCJ4NDAyVmVyc2lvbiI6Mn0=Decoded JSON:
{ "accepted": { "amount": "10000", "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "extra": {"name": "USDC", "version": "2"}, "maxTimeoutSeconds": 300, "network": "eip155:84532", "payTo": "0x2222222222222222222222222222222222222222", "scheme": "exact" }, "extensions": { "recuut-payment-request": { "info": { "id": "prq_00000000000070008000000000000000" } } }, "payload": { "authorization": { "from": "0x1111111111111111111111111111111111111111" } }, "x402Version": 2}This example shows the accepted payment option, the Recuut payment-request ID, and the payer authorization. Only the payer's wallet creates this JSON and its proof.
PAYMENT-RESPONSE
Recuut creates this header only after payment succeeds. Your application returns it to the payer with the protected result. The Python SDK checks it and returns the receipt as a typed object.
Inspect the complete encoded response and its main decoded fields
Encoded header value:
PAYMENT-RESPONSE: eyJhbW91bnQiOiIxMDAwMDAwIiwiZXJyb3JSZWFzb24iOm51bGwsImV4dGVuc2lvbnMiOnsicmVjdXV0LXJlY2VpcHQiOnsiZW52aXJvbm1lbnQiOiJ0ZXN0IiwiZW52aXJvbm1lbnRfa2V5IjoidGVzdCIsImlkIjoicmN0XzAwMDAwMDAwMDAwMDcwMDA4MDAwMDAwMDAwMDAwMDAwIiwiaXNzdWVkX2F0IjoiMjAyNi0wOC0xMlQwOTozMDowMC4wMDBaIiwicGF5bWVudCI6eyJhc3NldCI6IlVTREMiLCJhc3NldF9pZCI6IjB4MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMyIsImF1dGhvcml6ZWRfYXRvbWljIjoiMTAwMDAwMCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MzIiLCJwYXllciI6IjB4MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMSIsInJlY2lwaWVudCI6IjB4MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMiIsInNjaGVtZSI6ImV4YWN0Iiwic2V0dGxlZF9hdG9taWMiOiIxMDAwMDAwIiwidHJhbnNhY3Rpb25faGFzaCI6IjB4YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYSJ9LCJwcmljZSI6eyJjYWxjdWxhdGlvbl9tb2RlIjoiZml4ZWQiLCJjdXJyZW5jeSI6IlVTRCIsImlkIjoicHJjXzAwMDAwMDAwMDAwMDcwMDA4MDAwMDAwMDAwMDAwMDAwIiwibGFiZWwiOiJRdWFydGVybHkgcmVwb3J0IGFjY2VzcyJ9LCJyZXNvdXJjZSI6eyJpZCI6InJlc18wMDAwMDAwMDAwMDA3MDAwODAwMDAwMDAwMDAwMDAwMCIsIm5hbWUiOiJRdWFydGVybHkgcmVwb3J0IiwidXJsIjoiaHR0cHM6Ly9tZXJjaGFudC5leGFtcGxlL3JlcG9ydHMvcXVhcnRlcmx5In0sInNjaGVtYV92ZXJzaW9uIjoyLCJzZWxsZXIiOnsiaWQiOiJvcmdfMDAwMDAwMDAwMDAwNzAwMDgwMDAwMDAwMDAwMDAwMDAiLCJuYW1lIjoiQWNtZSBSZXNlYXJjaCJ9LCJ0cmFuc2FjdGlvbl9pZCI6InR4bl8wMDAwMDAwMDAwMDA3MDAwODAwMDAwMDAwMDAwMDAwMCJ9fSwibmV0d29yayI6ImVpcDE1NTo4NDUzMiIsInBheWVyIjoiMHgxMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExIiwic3VjY2VzcyI6dHJ1ZSwidHJhbnNhY3Rpb24iOiIweGFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEifQ==Decoded JSON, with the main fields shown:
{ "amount": "1000000", "network": "eip155:84532", "payer": "0x1111111111111111111111111111111111111111", "success": true, "transaction": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "extensions": { "recuut-receipt": { "id": "rct_00000000000070008000000000000000", "transaction_id": "txn_00000000000070008000000000000000", "environment": "test", "environment_key": "test", "schema_version": 2 } }}The full decoded value also contains the receipt's payment, price, resource, and seller information. A custom HTTP integration reads the receipt at extensions["recuut-receipt"]. Do not encode a receipt by itself; return the complete PAYMENT-RESPONSE value from Recuut.