x402 v2
ThePAYMENT-REQUIRED header is base64 JSON. Forge decodes it, makes two additions, and re-encodes it.
resource.description: the sentence is appended after a space:
extensions["forge-feedback"]: added after your own extensions:
{ info }) and stays under 1 KB, because challenges travel in a header.
With tone: "lifecycle", the ask describes the flow as steps and the extension adds "flow": ["402 challenge", "pay", "response", "rate"].
The receipt
After a successful v2 payment, the settlement header (PAYMENT-RESPONSE, base64 JSON) gets the same extension with the real ID, so the rating follows the payment on the wire: challenge → pay → receipt → rate.
success, transaction, network, payer) are never changed. Some clients (awal) don’t show headers to the model, which is why the response body carries the ID too.
x402 v1
The challenge is the JSON body. The sentence is appended to everyaccepts[].description. v1 has no extensions, so none are added.
Guarantees
- Added once. A challenge that already contains the rate URL (or already has
forge-feedback) is left alone. - Payment terms are untouched.
acceptsis byte-for-byte the same. v2 matches payments onaccepts, and v1 servers match against their own requirements. - Your extensions are untouched,
bazaarincluded, in the same order. - Echoes are safe. Clients send extensions back with the payment.
@x402/corevalidates echoes only for keys the server itself advertised, soforge-feedbackdoesn’t affect verification. Tested with the real@x402/coreclient. - Browser paywall HTML is never modified.