x402 v2

The PAYMENT-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:
It follows the x402 extension convention ({ 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.
Only added when settlement succeeded. Facilitator fields (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 every accepts[].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. accepts is byte-for-byte the same. v2 matches payments on accepts, and v1 servers match against their own requirements.
  • Your extensions are untouched, bazaar included, in the same order.
  • Echoes are safe. Clients send extensions back with the payment. @x402/core validates echoes only for keys the server itself advertised, so forge-feedback doesn’t affect verification. Tested with the real @x402/core client.
  • Browser paywall HTML is never modified.