APICo-signCo-sign a direct EVM payment
Co-sign a direct EVM payment
Co-sign a direct payment from a guarded Safe on an EVM chain.
- Auth Node Token
- Rate-limited Yes
- Idempotency
Idempotency-Keyequal tobatchId - Guide Co-sign a payment
Co-sign one direct payment from a guarded Safe to any address, outside x402: an EIP-3009 TransferWithAuthorization from the Safe, valid at most 600 seconds, with agentSignature the agent’s signature over its Safe message hash. Permit2 is refused here.
In a token without an EIP-3009 a Safe can sign (Ethereum USDT, Polygon USDT0), the payment is a Safe SafeTx instead, on the Safe’s domain {chainId, verifyingContract: Safe}, with exactly to the token, value 0, data the token’s transfer(payee, amount) (selector 0xa9059cbb, nothing more), operation 0 (CALL), safeTxGas, baseGas and gasPrice 0, gasToken and refundReceiver the zero address, and nonce the Safe’s current nonce (else 409 body_mismatch, detail safe_nonce). agentSignature is then the agent’s signature over the SafeTx hash.
Exchain verifies the typed data against intent and checks the mandate of the asset intent.asset names; with seller binding, the payee counts as an unsigned seller. The answer carries transaction, the call that moves the payment (transferWithAuthorization to the token, or execTransaction to the Safe), which any account can send. With relay, Exchain sends it and answers once it is mined. A Safe transaction lands when the Safe logs ExecutionSuccess for its hash; its budget returns once another transaction takes its nonce at a finalized block.
nodeTokenAuthorizationBearer <token>Node Token from the Exchain console. Works for wallets this node registered; the node’s network (preprod or mainnet) decides the network of the request. Use a preprod token in the playground.
Idempotency-Key*stringMust equal batchId, which is the authorization nonce (for a SafeTx, the Safe nonce): 0x and 64 hex characters.
^0x[0-9a-fA-F]{64}$application/json- body
batchId*stringThe authorization nonce (for a SafeTx, the Safe nonce) as 0x and 64 hex characters. Equal to the Idempotency-Key header.
^0x[0-9a-fA-F]{64}$walletId*WalletId^wal_[0-9A-HJKMNP-TV-Z]{26}$typedData*An EIP-3009 TransferWithAuthorization from the Safe on the token’s domain or, in a token without an EIP-3009 a Safe can sign (Ethereum USDT, Polygon USDT0), a SafeTx on the Safe’s domain calling the token’s transfer(payee, amount). Permit2 is refused here.
agentSignature*stringThe agent’s 65-byte signature over the Safe message hash of typedData.
^0x[0-9a-f]{130}$relay?booleanExchain sends the signed transfer and answers once it is mined, so the agent needs no ETH.
intent*context?Allowed. With relay, relayed.txHash is the transaction that moved the payment; otherwise send transaction from any account before the authorization expires (a SafeTx, until the Safe’s nonce moves past it).
application/json- response
decisionId*DecisionId^dec_[0-9A-HJKMNP-TV-Z]{26}$safeMessageHash*Hex32^0x[0-9a-f]{64}$walletAddress*EvmAddress^0x[0-9a-fA-F]{40}$digest*Hex32^0x[0-9a-f]{64}$nonce*Hex32^0x[0-9a-f]{64}$quorumMembers?array<string>signature?stringEvery owner’s 65-byte signature, sorted by owner address, ready for the payment payload.
^0x([0-9a-f]{130})+$receipt?stringGuard receipt: a compact EdDSA JWS by the journal key (kid did:web:<host>#journal, published at /.well-known/did.json) over {v, payer, network, asset, amount, payTo, resourceUrl, nonce, decisionId, journalLeaf, mandateHash, ownerBound, issuedAt}. Attach it to the payment payload at extensions["exchain-guard"].receipt.
shadow?booleanmembers*array<|||||||>1 <= items <= 1boundsRemaining*journalRef*stringasOf*string^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$date-timeschemaVersion*stringWire schema version, "1.1" today.
"1.0""1.1"chainTip*|nulltransaction*The call that moves the payment, carrying every owner’s signature. It is transferWithAuthorization to the token, or for a SafeTx execTransaction to the Safe. Any account can send it.
relayed?With relay, the transaction Exchain sent.
relayError?stringWith relay, why Exchain could not send it. transaction can still be sent until it expires, or for a SafeTx until the Safe’s nonce moves past it.
curl -X POST 'https://cosign.exchain.network/v1/evm/cosign' \ -H 'Idempotency-Key: 0xd830b1c61b024118f4e1dce0c305f81290222d28c2b830accf22c60b0304f946' \ -H 'Content-Type: application/json' \ -d '{ "batchId": "0xd830b1c61b024118f4e1dce0c305f81290222d28c2b830accf22c60b0304f946", "walletId": "wal_01M53M3ZNQQ4DDNPE4262QAZ28", "typedData": { "domain": { "name": "USDC", "version": "2", "chainId": 84532, "verifyingContract": "0x036cbd53842c5426634e7929541ec2318f3dcf7e" }, "types": { "TransferWithAuthorization": [ { "name": "from", "type": "address" }, { "name": "to", "type": "address" }, { "name": "value", "type": "uint256" }, { "name": "validAfter", "type": "uint256" }, { "name": "validBefore", "type": "uint256" }, { "name": "nonce", "type": "bytes32" } ] }, "primaryType": "TransferWithAuthorization", "message": { "from": "0x9f2b6c1e4d7a3058b2e6f1c9d4a7b3e5f0c8a216", "to": "0x6d2f8a4c1e9b3075d8a2f6c4e1b9d3a7f5c0e284", "value": "500000", "validAfter": "0", "validBefore": "1793974027", "nonce": "0xd830b1c61b024118f4e1dce0c305f81290222d28c2b830accf22c60b0304f946" } }, "relay": true, "agentSignature": "0x8a52bb619f12c4eb51b5297ac6bffd2eba6e81ff075e35d917b104352f93eae2b218c2eaa10c4aee2d8e8128de363bda0c853a72f6f1cfd1b7596ea3c862c7e41c", "intent": { "purchaseId": "att_01M53M3ZR1088SNXVGAJ9SFR2T", "counterparty": "eip155:84532:0x6d2f8a4c1e9b3075d8a2f6c4e1b9d3a7f5c0e284", "amount": "500000", "asset": "eip155:84532/erc20:0x036cbd53842c5426634e7929541ec2318f3dcf7e", "agentIdentifier": "research-agent", "reference": "invoice 2026-118" }}'{ "decisionId": "dec_01M53M3ZR1088SNXVGAJ9SFR2V", "safeMessageHash": "0x26ebe1acc814c822010eb2abe7e27178aebe0fcf2467a5e58e6b50be46ab7fab", "walletAddress": "0x9f2b6c1e4d7a3058b2e6f1c9d4a7b3e5f0c8a216", "digest": "0x9132f370787dedf90c6d69f0ff12a9f7a8471d58cfb86c0a842d69f4e72bd81f", "nonce": "0xd830b1c61b024118f4e1dce0c305f81290222d28c2b830accf22c60b0304f946", "quorumMembers": [ "exchain-eu-1a", "exchain-eu-1b", "exchain-eu-1c" ], "signature": "0x32d0fed30f3ccd8de52142a203227ea2d59c5f7ff529ab014b9d95023082dcafe190023975d13442ade43fa17a5ee4424a773791a1e4809d83ef786dbf6703491b7ad110a292193bfd59e7a8edb3e3d8fec0963c9c5b49b5e6e5c334bd1a9c803e58aa61dc2b210d7497844e5be671c1fd5a7f57f3545ffdbd8ef5b5b5788dc7f41b55570466ad06c009dd4dff5135a8e5b88cb20680a5e235b4d85c51d91ad1211213556442ee8a489a860e51048aab630d8695ed509393bbf1e49e8be23eae8f811b9d111cda2f3f27e05c6f3d19ea01e5176fe1c214e1f8bf0847577ba989a1ba3bf5ca770272fe92850b3cd7814c9b8be6202943343daf015e095ac3ee734d11fa1b", "transaction": { "to": "0x036cbd53842c5426634e7929541ec2318f3dcf7e", "data": "0xe3ee160e0000000000000000000000009f2b6c1e4d7a3058b2e6f1c9d4a7b3e5f0c8a2160000000000000000000000006d2f8a4c1e9b3075d8a2f6c4e1b9d3a7f5c0e284000000000000000000000000000000000000000000000000000000000007a1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006aeddf0bd830b1c61b024118f4e1dce0c305f81290222d28c2b830accf22c60b0304f94600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010432d0fed30f3ccd8de52142a203227ea2d59c5f7ff529ab014b9d95023082dcafe190023975d13442ade43fa17a5ee4424a773791a1e4809d83ef786dbf6703491b7ad110a292193bfd59e7a8edb3e3d8fec0963c9c5b49b5e6e5c334bd1a9c803e58aa61dc2b210d7497844e5be671c1fd5a7f57f3545ffdbd8ef5b5b5788dc7f41b55570466ad06c009dd4dff5135a8e5b88cb20680a5e235b4d85c51d91ad1211213556442ee8a489a860e51048aab630d8695ed509393bbf1e49e8be23eae8f811b9d111cda2f3f27e05c6f3d19ea01e5176fe1c214e1f8bf0847577ba989a1ba3bf5ca770272fe92850b3cd7814c9b8be6202943343daf015e095ac3ee734d11fa1b00000000000000000000000000000000000000000000000000000000" }, "relayed": { "txHash": "0x4b1e9c27d05a8f3e6b2d71c49a0e8f53d6c2b7a19e4f0d83c5a6b2e17f9d0c48" }, "members": [ { "purchaseId": "att_01M53M3ZR1088SNXVGAJ9SFR2T", "outputIndex": 0, "verdict": "allowed" } ], "boundsRemaining": { "per_tx_cap": { "limit": "1000000", "used": "500000", "remaining": "500000" }, "velocity_burst": { "limit": "60", "used": "1", "remaining": "59", "windowResetsAt": "2026-11-05T14:23:08Z" }, "daily_outflow": { "limit": "500000000", "used": "12500000", "remaining": "487500000", "windowResetsAt": "2026-11-06T08:41:19Z" }, "per_seller_cap": { "limit": "50000000", "used": "3500000", "remaining": "46500000", "windowResetsAt": "2026-11-06T08:41:19Z", "scope": "eip155:84532:0x6d2f8a4c1e9b3075d8a2f6c4e1b9d3a7f5c0e284" }, "per_agent_cap": { "limit": "250000000", "used": "12500000", "remaining": "237500000", "windowResetsAt": "2026-11-06T08:41:19Z", "scope": "0x3c8e1f7a2b9d4e6c0a5f8b1d7e3c9a2f4b6d8e01" }, "envelope": { "limit": "20000000000", "used": "86500000", "remaining": "19913500000" } }, "journalRef": "https://cosign.exchain.network/v1/decisions/dec_01M53M3ZR1088SNXVGAJ9SFR2V", "asOf": "2026-11-05T14:22:07.318Z", "schemaVersion": "1.1", "chainTip": null}Errors
| Status | Code | Meaning |
|---|---|---|
400 | bad_request | The body fails the schema, intent.amount is outside 1..10^18, or intent.counterparty is malformed. |
400 | idempotency_key_mismatch | The Idempotency-Key header differs from batchId. |
400 | batch_id_mismatch | batchId differs from the authorization nonce (for a SafeTx, the Safe nonce). |
400 | clock_rejected | Exchain’s clock check failed. Contact Exchain. |
401 | unauthorized | The bearer credential is missing, unknown or revoked. |
401 | agent_signature_invalid | agentSignature does not recover to the registered agent over the Safe message hash. |
402 | fee_due | Sign fee.typedData’s Safe message hash with the agent key, send it to POST /v1/x402/fees, then send the payment again. |
404 | not_found | The wallet is not a guarded Safe of this node. |
409 | member_denied | The payment was refused; members[0].denied has the code (mandate caps, counterparty_blocked, or seller binding: payee_unpinned, registry_gate, price_above_published, body_mismatch). |
409 | asset_not_listed | intent.asset is not one of the wallet’s governed assets, or Exchain disabled it on the network. |
409 | body_mismatch | The typed data disagrees with intent or uses a method this route refuses; detail says how (for example payee, amount, window, method, nonce_reused, safe_nonce). Do not retry. |
409 | clock_skew | The authorization window is not open now. |
409 | reservation_conflict | Another payment holds a reservation; retry. |
409 | idempotency_conflict | This nonce was decided before with different content (an Error body, not a refusal). |
413 | payload_too_large | The request body is larger than 64 KiB. |
429 | rate_limited | Over the limit; retry after Retry-After seconds (also in retryAfterSec). |
502 | rpc_failed | The chain could not be read (the Safe’s nonce, for a SafeTx payment or a fee Safe transaction); retry. |
503 | quorum_unavailable | Fewer enclaves reachable than the threshold (Retry-After: 5), or detail: "stopped" when Exchain stopped signing (reachable: 0, no retryAfterSec and no Retry-After header). |
503 | unavailable | detail: "record", the audit record could not be written (Retry-After: 5). |
Every code and what to do about it: Errors and codes.