Skip to content
Docs menu

Endpoints

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

  • asset string Required
    The coin, such as USDC.
  • network string Required
    The network, such as polygon.
  • amount string Required
    What this transaction pays to_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_address string Required
    The recipient's address on the network.
  • speed string
    slow, standard or fast, as signed. Defaults to standard.
  • note string
    Up to 280 characters for your team, such as what the send was for.
  • signed_transaction string
    Required from a wallet created or imported in 402pay. EVM: the 0x hex of the signed EIP-1559 transaction. Solana: the base64 wire transaction.
  • funding_transaction string
    EVM only: a signed transaction from another of the wallet's addresses that sends the gas signed_transaction needs, 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_hash string
    Required 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_required
    The request has no Idempotency-Key header.
  • 400 invalid_request
    A field is missing or invalid, signed_transaction doesn't pay exactly amount to to_address from one of the wallet's addresses, or a field is for the other kind of wallet, such as tx_hash from a wallet created in 402pay. field names it.
  • 400 invalid_address
    to_address isn't valid for the network, or is one of the wallet's own addresses.
  • 403 session_required
    An API key sent the request. Only a person signed in to the dashboard can send.
  • 404 not_found
    Your business has no wallet yet.
  • 409 insufficient_funds
    The send is for more than the address holds.
  • 409 insufficient_fee_funds
    The wallet doesn't hold enough of the network's own coin to pay the fee.
  • 409 duplicate_transaction
    That transaction was already sent: its hash is on the wallet's history, or another business recorded it from the same address.
  • 409 transaction_rejected
    The 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_unavailable
    The network couldn't be reached. Retry with the same Idempotency-Key: the same signed transaction never goes out twice.

Any request can also fail on its key or its body. See errors.

RequestBrowser
// 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();
Response201 Created
{  "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"  }}