Send from the wallet
Relay a send the dashboard signed in the browser, or record one made in an external wallet's app.
Sends coins from your wallet, one chain transaction per request. The dashboard makes these requests when someone sends from Wallet, so it takes a person signed in there, never an API key, which gets 403 session_required. A send that draws on several of the wallet's addresses is several requests, each with its own Idempotency-Key.
From a wallet created or imported in 402pay, the browser reads the send context, signs each transaction with the recovery phrase, and sends it as signed_transaction. 402pay checks that it pays exactly amount of asset to to_address from one of the wallet's addresses, records it under its hash and relays it to the network. The phrase and the encryption password never leave the browser. Before you sign, the dashboard shows the most the network fees can cost; if they rose by the time you sign, it shows the new most and asks again. A USDC send from an address with no gas goes by an EIP-3009 authorization that another of the wallet's addresses submits and pays for, as in the example. Sends are signed on Solana, Polygon and Ethereum.
From an external wallet, send in its own app, then record the send here with its tx_hash. The hash starts an unverified record: it does not reserve or deduct funds, and value_usd is zero until the chain proves the sender, recipient, asset and amount match. A mismatch fails the record. network_fee_usd stays zero because these records have no verified fee amount.
The transaction is pending until the network confirms it, then confirmed, or failed if it never lands. Follow it with GET /wallet/transactions/{id}. A simulated business broadcasts nothing. Signed sends confirm on a timer; external hash records fail on that timer because the simulation has no chain evidence to verify them.
Body
assetstring RequiredThe coin, such asUSDC.networkstring RequiredThe network, such aspolygon.amountstring RequiredWhat this transaction paysto_address, as a decimal string in the coin, such as"25.00", with no more decimal places than the coin's. An external wallet's record states exactly what its app sent, to as many decimal places as the coin counts on chain: USDC 6, USDT 6, BTC 8, ETH 18 and SOL 9.to_addressstring RequiredThe recipient's address on the network.speedstringslow,standardorfast, as signed. Defaults tostandard.notestringUp to 280 characters for your team, such as what the send was for.signed_transactionstringRequired from a wallet created or imported in 402pay. EVM: the0xhex of the signed EIP-1559 transaction. Solana: the base64 wire transaction.funding_transactionstringEVM only: a signed transaction from another of the wallet's addresses that sends the gassigned_transactionneeds, relayed first. The dashboard adds it for a token with no authorization, such as USDT on Ethereum, at an address without the network's own coin.tx_hashstringRequired from an external wallet: the hash of the send made in its own app. A hex hash is recorded in lower case.
Errors
- 400
idempotency_key_requiredThe request has noIdempotency-Keyheader. - 400
invalid_requestA field is missing or invalid,signed_transactiondoesn't pay exactlyamounttoto_addressfrom one of the wallet's addresses, or a field is for the other kind of wallet, such astx_hashfrom a wallet created in 402pay.fieldnames it. - 400
invalid_addressto_addressisn't valid for the network, or is one of the wallet's own addresses. - 403
session_requiredAn API key sent the request. Only a person signed in to the dashboard can send. - 404
not_foundYour business has no wallet yet. - 409
insufficient_fundsThe send is for more than the address holds. - 409
insufficient_fee_fundsThe wallet doesn't hold enough of the network's own coin to pay the fee. - 409
duplicate_transactionThat transaction was already sent: its hash is on the wallet's history, or another business recorded it from the same address. - 409
transaction_rejectedThe network refused it, such as when another transaction from the address got there first or the fee is too low right now. Nothing was sent, so read a fresh send context and sign again. - 503
network_unavailableThe network couldn't be reached. Retry with the sameIdempotency-Key: the same signed transaction never goes out twice.
Any request can also fail on its key or its body. See errors.
// On a dashboard page, whose session cookie the browser sends.const response = await fetch("/api/v1/wallet/transactions", { method: "POST", headers: { "402pay-Business": businessId, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ asset: "USDC", network: "polygon", amount: "25.00", to_address: "0x136e44a2738dea5217e8d6745463d2a9a84d1421", speed: "standard", note: "Refund for order 1042.", signed_transaction: "0x<signed-transaction-from-your-browser>" }),});const { data } = await response.json();{ "data": { "id": "wtx_XJPPvt18plFGNmuW", "kind": "wallet_transaction", "direction": "out", "status": "pending", "asset": "USDC", "network": "polygon", "amount": "25.00", "value_usd": 2500, "network_fee_usd": 2, "address": "0x6F00bCaA08D1A6De8Ce7724dd643823c978bc81F", "from_address": "0x6F00bCaA08D1A6De8Ce7724dd643823c978bc81F", "to_address": "0x136e44a2738dea5217e8d6745463d2a9a84d1421", "tx_hash": "0xcb6a65a380c1deecb982539aed5ca831acf85b94cfee5dc624cd34451cba0538", "explorer_url": "https://polygonscan.com/tx/0xcb6a65a380c1deecb982539aed5ca831acf85b94cfee5dc624cd34451cba0538", "payment_id": null, "note": "Refund for order 1042.", "created_at": "2026-09-29T09:37:20.808Z" }}