Skip to main content
The Privy Dashboard supports manually initiating transfers from wallets for supported assets on supported chains. A wallet can therefore hold a token that the dashboard displays but does not automatically support sending: an unlisted ERC-20, an SPL token on a network the dashboard does not offer, or a balance in a wallet the dashboard cannot send from. Every one of those balances is still recoverable. The wallet’s keys are unchanged, and Privy’s wallet API supports a far wider range of assets, chains, and wallet types than the dashboard UI exposes. This recipe covers the three recovery paths, in order of increasing effort, and how to choose between them.
Recovery needs no support ticket and no new wallet. Refer to the chains overview for the support tier of the chain in question, since the tier determines whether Privy broadcasts the transaction or the transaction must be broadcast independently.

Why the dashboard cannot send an asset

The dashboard displays balances for a broader set of assets than it can transfer, so a balance appearing in the UI is not a guarantee that the UI can move it. Identify the cause first, because it determines the cheapest recovery path.

Choosing a recovery path

Token watchlist

No code. Registers the token so the dashboard transfer flow can send it. Covers ERC-20, SPL, and Token-2022 on supported networks.

Transfer action API

Recommended. One request moves any ERC-20 or SPL token on a supported chain, from any wallet, with no transaction construction.

Low-level RPC

For chains the transfer API does not cover. Build the transaction, have Privy sign it, and broadcast it if Privy does not.

Transfer intent

For a wallet owned by a key quorum of team members. Propose the transfer, then approve it from the dashboard.
Every option keeps the wallet’s private key inside Privy’s secure enclave. Recovery never requires exporting the key.

Prerequisites

  • The wallet’s ID, from the Wallets page of the dashboard
  • The app’s ID and app secret, from the Basics tab of the app’s settings
  • The token’s contract address (EVM) or mint address (Solana)
  • A destination address that can hold the asset
  • Enough native currency in the wallet to pay gas, unless the app has gas sponsorship configured
A token transfer is a contract call, and the wallet pays its own fees. Fund the wallet with a small amount of ETH, SOL, or the chain’s native gas token before attempting any transfer path. Transfers made through the transfer action API are gas-sponsored by default when the app has sponsorship configured.

Check whether the wallet has an owner

Whether the wallet has an owner determines how each request is authorized, so establish it before starting. Fetch the wallet and check owner_id, which is null for an ownerless wallet:
A wallet owned by a key quorum cannot be recovered with a direct API call, because the request carries no authorization signatures from the quorum. Propose the transfer as an intent instead, then have the quorum’s reviewers approve it in the dashboard. Intents expire 72 hours after creation.

Option 1: Token watchlist

Privy’s transfer flow sends well-known assets such as USDC, USDT, EURC, ETH, and SOL out of the box. Any other ERC-20 or SPL token becomes sendable once it is registered as a custom asset for the app. This is the only recovery path that requires no code, so it is worth attempting first.
1

Register the token

Open the Token watchlist tab of the Wallets page, then add the token’s contract or mint address on the network holding the balance.
2

Confirm the balance appears

Return to the wallet’s page. The token now appears as a selectable asset in the transfer flow rather than a display-only balance.
3

Send the balance

Start a transfer from the wallet, select the newly registered token, and enter the destination address.
If the token still cannot be selected, the blocker is the wallet or the network rather than the asset. Continue to option 2.

Option 2: Transfer action API

The transfer action API accepts a human-readable asset, amount, chain, and destination, and handles the onchain construction. It works from any wallet the caller is authorized to use, including end-user and embedded wallets that the dashboard cannot send from, and it resolves token decimals so the amount is a plain decimal string. Register the token in the token watchlist first, then pass its address as source.asset_address:
For a well-known asset, pass source.asset instead of source.asset_address, using the asset’s API value such as usdc. Provide exactly one of the two fields; sending both is rejected. Refer to transfer usage for the full parameter list and response shape. The response is an action rather than a transaction, returning an id and a status of pending. Poll the action until it reports a terminal status to confirm the recovery landed:
To recover the entire position, read the wallet’s balance for the asset first and pass it as source.amount. Recovering the wallet’s native balance is the same request with asset set to the native token, run last so the earlier transfers still have gas available.
Confirm that no policy on the wallet denies the transfer before debugging further. A policy that allowlists specific contracts will reject a recovery transaction against an unlisted token contract.

Option 3: Low-level RPC

The transfer action API covers a fixed set of chains. Outside that set, build the transaction and use Privy’s signing RPCs directly. Which RPC applies depends on the wallet’s chain_type, not on the token.
For an EVM chain that Privy broadcasts to, encode a transfer call and send it as a normal transaction. The to field is the token contract; the destination is an argument to transfer.
If Privy does not broadcast to the chain, sign the transaction and submit it independently. Use eth_signTransaction, which returns an RLP-encoded signed transaction ready for eth_sendRawTransaction. It takes an explicit chain_id and no caip2, so it works on EVM chains Privy does not broadcast to:
Signing this way means populating nonce, gas_limit, and the fee fields, since Privy is not estimating them.
Recovering a token on a chain type other than EVM or Solana, such as a TRC-20 balance on Tron, uses that chain’s own signing RPC rather than the methods above. Refer to the Tier 2 chains recipe for per-chain examples, and adapt the transaction being built from a native transfer to the chain’s token transfer.

Verify the recovery

Confirm the transaction onchain, then re-check the wallet’s balances in the dashboard. A recovered wallet shows a zero balance for the asset, plus whatever native currency was left behind for gas. Repeat per asset and per network, since each transfer moves one asset on one chain.

Transfer action API

Supported assets and chains, custom assets, and native bridging.

Propose intents

Recovering from a wallet owned by a key quorum of team members.

Chain support

Which chains Privy signs for, and which it also broadcasts for.

Sending ERC-20s

Encoding an ERC-20 transfer across Privy’s SDKs.

Sending SPL tokens

Building SPL transfers and handling associated token accounts.

Using Tier 2 chains

Signing and broadcasting examples for chain types beyond EVM and Solana.