Skip to main content

Send, Swap, and Receive

Embedded wallet users move funds through treasury wallet endpoints. Every send and swap runs validate_wallet_send() against effective spend policies before signing. Step-up authentication is required: account password (X-Auth-Confirm) or passkey tx token.

Human-only

These endpoints reject agent JWTs with 403. Your backend should call them with the end-user's JWT, or use the React widget which holds the user session.

Send native currency

const { data } = await client.treasuryWallets.sendFromWallet(
"ethereum",
{
to: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
amount: "0.01", // major units (ETH, SOL, BTC, …)
},
userPassword, // X-Auth-Confirm
);

console.log(data.tx_hash, data.status);

EVM token transfers

Pass token_contract (ERC-20) or use token_mint on non-EVM chains:

await client.treasuryWallets.sendFromWallet(
"ethereum",
{
to: "0xRecipient...",
amount: "100.0",
token_contract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
},
userPassword,
);

Chain-specific fields

ChainExtra fields
Bitcoinfee_rate_sat_per_vbyte
Solanatoken_mint, memo
XRPdestination_tag, xrpl_tx_json
Cardanotoken_mint (policy.asset), ttl
Trontoken_mint, fee_limit_sun

Amounts are major-unit decimal strings (e.g. "0.001" BTC, "10.5" SOL).

Gasless sends (ERC-4337)

On EVM chains with Pimlico configured, wrap the send as a sponsored UserOperation:

await client.treasuryWallets.sendFromWallet(
"ethereum",
{
to: "0x...",
amount: "0.01",
gasless: true,
},
userPassword,
);

Response may include user_op_hash. Users do not need native ETH for gas; the paymaster sponsors fees. Supported on Ethereum, Base, Optimism, Arbitrum, and Polygon when RPC + paymaster are configured.

// wallet-react
await send({ chain: "ethereum", to: "0x...", amount: "0.01", gasless: true });

Passkey transaction authorization

Alternative to password re-auth — bind WebAuthn to the transaction digest:

// wallet-react — full flow
await sendWithPasskey({
chain: "ethereum",
to: "0x...",
amount: "1.0",
});

Under the hood:

  1. Client computes tx_digest = SHA256(chain|to|value_wei|data)
  2. Passkey ceremony via /v1/auth/passkeys/tx-assert/begin + .../complete
  3. Send with X-Passkey-Token header instead of X-Auth-Confirm

Manual API usage mirrors the widget; see Authentication.

Swap via 0x

Swaps fetch quotes from the 0x aggregator, sign, and broadcast server-side. Requires ZERO_X_API_KEY on Vault.

const { data } = await client.treasuryWallets.swapFromWallet(
"ethereum",
{
sell_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH
buy_token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC
sell_amount: "0.1",
slippage_percentage: "0.5",
},
userPassword,
);

console.log(data.tx_hash, data.buy_amount);

Spend policies apply to swaps the same as sends (allowed_tokens, daily limits, etc.).

Receive

Receiving is address-based — no dedicated API call:

  1. List wallets: GET /v1/treasury/wallets or widget Receive view
  2. Display address (and chain-specific memo/tag for XRP)
  3. Optionally create deposit destinations for tracked inbound payments + webhooks

The widget's Receive feature shows QR codes and copyable addresses per chain.

Policy violations

When a send or swap violates spend policy, the API returns 403 with a descriptive error (e.g. destination not in allowlist, daily limit exceeded). The React widget surfaces this as a toast — it never attempts to sign blocked transactions.

Users can inspect effective policy:

const { data } = await client.treasuryWallets.getEffectiveSpendPolicy();
console.log(data.source); // e.g. app default vs user override when present

Audit & webhooks

Successful sends emit audit events (treasury_wallet.send) and webhooks when configured:

  • wallet.transfer.sent
  • wallet.transfer.received (deposit monitoring)

See Platform webhooks.