Agents & buyers
Treat a paid Gonka URL like any other HTTP resource. On 402, sign a transfer locally and retry. Never send the private key to the facilitator or the seller.
Request cycle
- Call the paid URL with whatever method the seller documents (
GET /demois the example). - If the status is not 402, you are done — the resource was free or already paid.
- Read
PAYMENT-REQUIRED. It is standard base64 JSON. The body is the same object, useful for curl. - Pick one entry from
accepts(GNK or USDC unless the seller only lists one). - Build and sign a Gonka
TxRawthat payspayToat leastamountofasset. - Repeat the original request with
PAYMENT-SIGNATUREset to the base64 payment payload. - On 200, read
PAYMENT-RESPONSEfor the tx hash. Look it up on gonka.gg.
A live cycle against /demo produced 23B76D1281F9EFCC7AFC90659480CF3219A7099959C0513720769F700E9B73B2. Command, RPC checks, and the explorer link are on Get started.
Choose an accept
Each accepts[] item is a price quote. You must satisfy one of them exactly as advertised:
scheme | Must be exact. |
|---|---|
network | Must be cosmos:gonka-mainnet. |
amount | Atomic units. Paying more is allowed; paying less is rejected. |
asset | ngonka or the USDC CW-20 address. |
payTo | Merchant gonka1…. The tx recipient must match. |
maxTimeoutSeconds | Set authorization.timeoutAt to now plus a value below this (the CLI uses 55s). |
extra.paymentFlow | upfront — expect the seller to settle before serving you. |
Sign the payment
GNK
One /cosmos.bank.v1beta1.MsgSend: from your address, to payTo, amount amount, denom ngonka. Fee is 0ngonka. Gas limit 120000 is what the facilitator expects.
USDC
One /cosmwasm.wasm.v1.MsgExecuteContract against the USDC contract, message:
{
"transfer": {
"recipient": "<payTo>",
"amount": "<atomic amount>"
}
}Signing rules
- SIGN_MODE_DIRECT over
sha256(SignDoc). - secp256k1, 64-byte compact signature (r||s).
- Pubkey in AuthInfo must derive to the
fromaddress. chain_idisgonka-mainnet.account_numberandsequencemust be the live values from LCD/cosmos/auth/v1beta1/accounts/{from}. A stale sequence is rejected asstale_sequence.
The CLI in this repo already builds that tx. Roll your own signer if you already have CosmJS or inferenced.
Retry with the header
PAYMENT-SIGNATURE is base64 of:
{
"x402Version": 2,
"accepted": { "...the PaymentRequirements you chose" },
"payload": {
"signature": "<base64 64-byte secp256k1 signature>",
"authorization": {
"from": "gonka1…",
"to": "gonka1…",
"amount": "1000",
"denom": "ngonka",
"timeoutAt": 1710000000
},
"signedTx": "<base64 protobuf TxRaw>"
}
}For USDC, accepted.asset and authorization.denom are the CW-20 contract address. Field-level meaning is on Protocol.
Fetch wrapper
Sketch for an agent runtime. Sign buildAndSign(accepted) with your existing Gonka wallet.
async function paidFetch(url, key, opts = {}) {
const first = await fetch(url, opts);
if (first.status !== 402) return first;
const required = JSON.parse(atob(first.headers.get("PAYMENT-REQUIRED")));
const accepted = required.accepts[0];
const payload = await buildAndSign(accepted, key);
return fetch(url, {
...opts,
headers: {
...(opts.headers || {}),
"PAYMENT-SIGNATURE": btoa(JSON.stringify(payload)),
},
});
}Retries and failures
- Do not broadcast the tx yourself. The facilitator broadcasts the
signedTxyou attached. - If settle returns
stale_sequence, re-query the account and sign again. Do not reuse the old payload. - If you get
replayed_payment, that exactTxRawwas already settled. Sign a new tx (new sequence). settlement_pendingmeans the tx was broadcast but inclusion was not confirmed in time. Check the hash on the explorer before paying again.- The paying account must have a sufficient balance.
/verifychecks this;/settlewill fail on-chain if it does not.
Every code is listed on Errors.