x402-negotiator

Agent-to-agent price negotiation as signed instruments. Open an offer, counter it, accept it — each move returns a signed artifact that links to the one before it, so an entire negotiation can be verified offline, in order, without trusting the server that produced it.

USDC on BaseUSDC on SolanaHTTP 402Apache-2.0hash-linked chainconcession analysisself-contained

Pay on either rail — your client picks. Every 402 challenge lists USDC on Base and USDC on Solana at the same price.

git clone https://github.com/nirholas/x402-negotiator && cd x402-negotiator
npm install && cp .env.example .env   # pre-filled — runs with no edits
npm run dev                            # http://localhost:4045

Pricing

RoutePriceReturns
POST /offers$0.001Signed offer instrument + thread + counter/accept links
POST /counter/:offerId$0.001Signed counter with concession analysis
POST /accept/:offerId$0.001Signed agreement + settlement terms + full chain
GET /threads/:idfreeThe thread and a server-side chain check
POST /verify-chainfreePer-instrument signature and link verification
POST /verifyfreeSignature check for one instrument

Payment rails

USDC on Base

EIP-3009 transferWithAuthorization, signed in the wallet. Nothing on-chain until the facilitator settles.

network
base-sepolia (default) · base
payTo
0x40252CFDF8B20Ed757D61ff157719F33Ec332402
facilitator
https://x402.org/facilitator

USDC on Solana

Fee-sponsored SPL transferChecked. The facilitator's feePayer covers the SOL fee — you need only USDC.

network
solana (default) · solana-devnet
payTo
WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW
facilitator
https://facilitator.payai.network

A rail with no configured address is dropped from the challenge instead of failing the request, so a single-rail deployment is a one-line change.

How a paid call works

1 · RequestPOST /counter/cnt_… with your price
2 · 402accepts[]: Base and Solana, same price
3 · Paypick a rail, sign, send X-PAYMENT
4 · 200signed instrument in-body + X-PAYMENT-RESPONSE

The chain is the product

Every instrument carries the previous one’s signature and a SHA-256 of its payload. Editing any price breaks two links at once — its own signature and the next instrument’s prevHash — so the tamper is visible without asking anyone:

POST /verify-chain   ← free, no server state consulted
{ "valid": true, "length": 4, "problems": [] }

// after editing seq 3’s price:
{ "valid": false,
  "problems": [ "seq 3: invalid signature",
                "seq 4: prevHash does not match the previous instrument’s payload" ] }

// after reordering two instruments:
{ "valid": false, "problems": [ "seq 3: seq 3 does not follow 1" ] }

Both counterparties hold the same chain. Neither needs this server to prove what was agreed.

Counters tell you whether to keep going

Every counter carries a concession analysis — how far you moved, how far apart the sides still are, who has been doing the conceding, and where the two land if the current rate holds:

"concession": {
  "movedUsd": 90, "movedPct": 30,
  "gapUsd": 90, "gapPct": 20.69,
  "totalMoved": { "buyer": 90, "seller": 0 },
  "projectedSettleUsd": 435, "projectedRounds": 1,
  "note": "At the current rate of concession the sides meet near $435 in about 1 more round(s)."
}

totalMoved is the honest field. A thread where one side has moved $90 and the other $0 is not a negotiation, and the projection refuses to guess rather than flattering you.

Refusals are artifacts too

Counter out of turn, act on a stale instrument, or accept your own position and you get a signed rejection instrument naming the reason — chain-linked like any other, and presentable as proof the move was refused. You paid for the call; you get an artifact for it.

It does not move money

An agreement is the instrument of record: what was agreed, by whom, at what price, by when, and on which rails settlement should happen. The transfer itself happens on a named rail — often an x402 call to the seller’s own endpoint. Keeping the two separate is what lets the same agreement travel between parties, escrow agents and auditors unchanged.