x402Sellers and guard receipts
Sellers and guard receipts
Bind payments to sellers' signed offers, and let sellers verify that a payment came from a guarded wallet.
Hold every payment to the seller's own signed price, and give the seller a receipt it can check against Exchain's public key. Both apply to x402 payments.
Seller binding
Seller binding applies only when the wallet registered sellers (Register a Safe). Each offer arrives in the payment's intent.offer, as eip712 or jws. Exchain takes the host of the offer's resourceUrl, finds that host in sellers, and checks who signed the offer.
| Field | Values |
|---|---|
sellers | Up to 64 { domain, keys? } entries. domain is a lowercase host |
sellers[].keys | Up to 8. EVM addresses for eip712 offers, did:web key ids for jws offers |
unsignedSellers | "refuse" (default, also when absent) or { "allowUnder": "<minor units>" } |
Seller keys
A seller publishes its offer keys in its own did:web document. When a check needs it, Exchain fetches https://<domain>/.well-known/did.json for the offer's host at co-sign time and keeps it for 10 minutes. The document's id must be did:web:<domain>.
| Offer | keys set | keys empty |
|---|---|---|
eip712 | The recovered signer must be one of keys. The did.json is not fetched | The signer must match the address in a verificationMethod's blockchainAccountId in the did.json |
jws | kid must be one of keys. The public key comes from the did.json | The public key comes from the did.json |
A jws offer always needs the seller's did.json: its kid must start with did:web:<domain>#, and the signature must verify with the publicKeyJwk of the verificationMethod with that id (Ed25519 for EdDSA, secp256k1 for ES256K). An eip712 offer with keys set is checked against keys alone.
| Case | Code |
|---|---|
No offer, and no allowUnder cap or the amount is above it | registry_gate |
| Offer cannot be decoded, or the resource URL has no host | body_mismatch |
Host not in sellers | payee_unpinned |
Signer not authorized as above, or a needed did.json cannot be fetched | payee_unpinned |
Offer scheme is not exact, or its network, asset or payee differ from the authorization | body_mismatch |
| Offer expired | body_mismatch |
| Amount above the offer | price_above_published |
Paying at or below the offer passes. These codes arrive at member level in the 409.
A direct payment (POST /v1/evm/cosign) carries no offer. On a wallet with sellers, it passes only under unsignedSellers.allowUnder.
Guard receipt
Every co-signed x402 payment returns a compact JWS in receipt. Direct payments and shadow wallets get no receipt. The agent attaches it to the payment at extensions["exchain-guard"].receipt. The receipt is Exchain's own field, not part of the x402 protocol.
{ "alg": "EdDSA", "kid": "did:web:<host>#journal", "typ": "exchain-guard+jws" }{
"v": 1,
"payer": "eip155:84532:0xSafe…",
"network": "eip155:84532",
"asset": "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
"amount": "500000",
"payTo": "0xPayTo…",
"resourceUrl": "https://api.seller.example/forecast",
"nonce": "0x0909…09",
"decisionId": "dec_01J9XK3M4N5P6Q7R8S9T0V1W2X",
"journalLeaf": "<64 hex>",
"mandateHash": "<64 hex>",
"ownerBound": true,
"issuedAt": 1762596000
}| Field | Meaning |
|---|---|
payer | The guarded Safe, as CAIP-10 |
asset | The token contract that was paid |
resourceUrl | From the signed offer, else the intent's resource |
journalLeaf | The decision's journal leaf, or null if not yet available |
mandateHash | The constitutionId of the paid asset's mandate |
ownerBound | The owner address is bound to a console account |
issuedAt | Unix seconds |
Verify a receipt
Exchain's own did.json has nothing to do with seller keys. It publishes one key, #journal: the Ed25519 key that signs guard receipts and bound statements.
- Fetch
https://cosign.exchain.network/.well-known/did.json. - Check that
kidisdid:web:cosign.exchain.network#journal. Thekidnames the host that issued the receipt, so trust only that issuer. - Verify the Ed25519 signature with the
#journalkey. - Compare
payTo,amount,payer,networkandresourceUrlwith the payment you received.
The receipt proves Exchain co-signed this payment. It does not prove settlement. Check the decision behind decisionId on Verify a decision.