x402 is the HTTP 402 payments protocol started by Coinbase and now under the
x402 Foundation: a server answers a request with 402 Payment Required and a machine-readable price, the client pays, and
the request goes through. The TON part of the specification (specs/schemes/exact/scheme_exact_ton.md) covers Jetton
payments and lists native TON as planned. We run a TON-native 402 flow for AI agents (GRAM-402),
so we wrote the missing part and opened it as draft pull request #4104.
It is a draft under review, not an accepted change; this post explains what it proposes and why.
What the proposal adds
Spec only, as the repository's CONTRIBUTING asks (specification first, implementation after). Native payments reuse the
existing W5 internal_signed relay flow, the payload shape, the signature, the replay and duplicate-settlement rules.
What changes:
asset: "native"as the reserved value (the way Hedera uses0.0.0and XRPL usesXRP), withamountin nanotons.- The payment intent is exactly one
action_send_msg, mode 1 or 3,dest == payTo,value == amount, and a body equal toextra.forwardPayload— for example a text comment with an invoice id. - A balance rule: the payer's balance, excluding the TON attached by the relay, must cover
amountplus the forward fee. Without it a zero-balance wallet could sign a transfer smaller than the relay's gas allowance, and the facilitator would end up paying the resource server out of its own gas budget. - Settlement succeeds only if the trace shows the transfer to
payTowith the exact value; a skipped (mode 3) or bounced transfer issuccess: false.
Where the exact-value rule comes from
Running GRAM-402 we met wallets that deduct the network fee from the value they send. The server then sees an underpayment by exactly that fee and has to decide whether to accept it. Requiring mode 1 and the exact value moves that decision into the specification, where every implementation answers it the same way.
Open questions we asked the reviewers
Whether "native" is acceptable as the reserved asset identifier, and whether extra.forwardPayload should be limited to
text comments for native transfers or stay an arbitrary cell as it is for Jettons. The example payload was built and decoded
with pytoniq-core; send-mode values were checked against the repository's TVM constants. The pull request still needs
signed commits before it can be reviewed for merge; we will update this post with the outcome either way.