# Send assets

A payment is a standard transfer of funds from your wallet to a counterparty or to another wallet. Ripple Custody (Wallet) supports payments on *all blockchains*. In the API, a payment is a transfer transaction.

New wallets are deposit-only. Before a wallet can send, it needs a transaction policy, an address book entry for the destination, and outgoing transactions enabled. See [Unlock outgoing transactions](/products/wallet/get-started/console/unlock-outgoing-transactions).

To give a transaction your own ID, set an **External ID** in the console or an `externalId` on a transfer. Reusing the same `externalId` makes a transfer request safe to retry. See [External IDs](/products/wallet/api-and-developer-experience/api-conventions#external-ids).

## Payment actions

| Action | Console role | Console procedure | API procedure |
|  --- | --- | --- | --- |
| Estimate the fee | - | - | [Estimate the fee](#estimate-the-fee-via-the-api) |
| Send a payment | Owner, Administrator, Proposer | [Send a payment](#send-a-payment-via-the-console) | [Create a transfer](#create-a-transfer-via-the-api) |
| Send from an EVM wallet on another EVM chain | - | - | [EVM cross-chain transfers](#evm-cross-chain-transfers-via-the-api) |
| Track the transaction | All roles | [View a transaction](/products/wallet/transactions/view-and-track-transactions#view-a-transaction-via-the-console) | [Track the transaction lifecycle](/products/wallet/transactions/view-and-track-transactions#track-the-transaction-lifecycle-via-the-api) |


**Permissions:** In the console, owners, administrators, and proposers can create transactions, and owners, administrators, proposers, and approvers can approve them. See [User roles and permissions](/products/wallet/organization-and-access/users-and-roles#transactions). The API procedures need an API credential of type `TRANSACTIONS`. See [Prerequisites](#prerequisites).

## Network fees

Each transaction pays a network fee in the blockchain's base asset (for example, ETH on Ethereum). Fee estimation returns speed tiers (`slowest`, `slow`, `average`, `fast`, `fastest`): EVM chains, Bitcoin, and Solana return all five tiers, and TRON and XRP Ledger return only `average`. You can pay a higher fee for the transaction to be processed more quickly. The fee you set on a transfer is the maximum fee the transaction can pay. Fee estimation is currently API only. See [Estimate the fee](#estimate-the-fee-via-the-api).

## Prerequisites

To send from the console or the API, you need:

- **A funded wallet.** For the API procedures, you need the wallet's `vaultId` and `walletId`. See [Create your first wallet](/products/wallet/get-started/console/create-your-first-wallet).
- **An active policy that permits outgoing transactions.** Without one, the policy check rejects transfers. See [Unlock outgoing transactions](/products/wallet/get-started/console/unlock-outgoing-transactions).
- **A destination address allowed by your policies**, for example an address registered to a counterparty. See [Manage address book](/products/wallet/governance-and-controls/address-book/manage-address-book).


For the API procedures, you also need:

- **An API credential of type `TRANSACTIONS`.** It grants access to creating and managing transactions, including transfers and fee estimation. Create credentials in the console. See [Manage API credentials](/products/wallet/organization-and-access/api-credentials/manage-api-credentials) and [Credential types](/products/wallet/organization-and-access/api-credentials#credential-types).
- **An access token.** Exchange your credential's `clientId` and `clientSecret` for an access token. See [Authenticate](/products/wallet/get-started/use-the-api#step-2-authenticate).


For the base URLs, token lifetime, pagination, filtering, and error format that every endpoint shares, see [API conventions](/products/wallet/api-and-developer-experience/api-conventions).

## Send a payment via the console

**Required role:** Owner, Administrator, or Proposer.

To send a payment from the console:

1. In the console, open the wallet you want to send from and click **Send transaction**.
2. On the **Type** step, choose a **Transaction type** and click **Next**. The default is **Payment**.
![The Type step of the New Transaction form, with Payment selected as the transaction type](/assets/send-assets-transaction-type.a3bfd4eea3c578a710bbf5cef3061b50c8a25e3bd629549857b97f35ec6e2454.9fccc40d.png)
For XRP Ledger wallets, the **Transaction type** list also offers XRP Ledger–specific types such as OfferCreate, OfferCancel, and TrustLine. See [XRP Ledger transactions in the console](/products/wallet/transactions/xrpl/xrpl-in-the-console).
3. On the **Details** step, enter the payment details:
  1. Under **Asset**, select the asset you want to send. All assets in the wallet appear as options.
  2. Under **Sending to**, select the wallet or address book entry you want to send the payment to. If the external address you want doesn't appear, add it to the address book first.
  3. Enter the **Amount to send**, excluding blockchain fees. The console also shows the amount in USD.
![The Details step of the New Transaction form, with Asset, Sending to, and Amount to send filled in](/assets/get-started-send-transaction.5510872bb1751d633731d65e356079fd971ba7fd5db2dc392318c7b95567aadc.9fccc40d.png)
4. Click **Next**.
5. Optional: On the **Advanced** step, enter a **Correlation ID** and an **External ID**. For XRP Ledger wallets, you can also enter a **Source tag** and a **Destination tag**.
6. Click **Review transaction**.
7. On the **Confirm** step, check that the transaction details are correct. You can also view the transaction amount in USD. Once confirmed, the transaction details can't be changed.
8. Click **Confirm transaction** to submit the transaction.
![The Confirm step of the New Transaction form, showing the transaction details and the Confirm transaction button](/assets/send-assets-confirm-transaction.99f4fb0e85a21f277a7f1c98099d26589824bc98344dd1ca13e343de5b9bee2a.9fccc40d.png)


If you have approvals or MPC signing set up, the console sends the transaction for approval or signing. To follow the payment through to confirmation, see [View a transaction](/products/wallet/transactions/view-and-track-transactions#view-a-transaction-via-the-console).

## Estimate the fee via the API

**API reference:** [Estimate the fee for a transfer transaction](/products/wallet/api-docs/palisade-api/palisade-api/transactions/transactionsservice_estimatetransferfee)

Ripple Custody (Wallet)'s transaction fee estimation lets you preview network fees before you execute a transfer, so you can budget for transaction costs and choose the fee level that fits the urgency of the transaction.

API only
This feature is currently API only. It will be available in the Ripple Custody (Wallet) console soon.

Before you send, estimate the network fee for a transfer with `POST /v2/transactions/transfer/estimate-fee`. `blockchain`, `symbol`, and `originAddress` are required. Include `contract` for token transfers. Include `destinationAddress` for a more accurate estimate on chains where transfer costs vary based on recipient state, such as TRON and Solana.

```bash
curl -X POST "https://api.sandbox.palisade.co/v2/transactions/transfer/estimate-fee" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "blockchain": "ETHEREUM",
    "symbol": "ETH",
    "originAddress": "0x2352d20fC81225c8ECD8f6FAA1B37F24FED450c9"
  }'
```

The response returns `networkFees`, a map of fee options keyed by speed (`slowest`, `slow`, `average`, `fast`, `fastest`) with values in base asset units (for example, ETH on Ethereum). EVM chains, Bitcoin, and Solana return all five tiers. TRON and XRP Ledger return only `average`. You can pay a higher fee for the transaction to be processed more quickly. For Ethereum and compatible EVM chains, it also returns an `evm` object with detailed gas information: `gasPrice`, `baseFee`, `priorityFee` (all in wei), and the estimated `gasLimit`. `gasPrice` is `baseFee` plus `priorityFee`.

```json
{
  "evm": {
    "gasPrice": "5000000000",
    "baseFee": "3000000000",
    "priorityFee": "2000000000",
    "gasLimit": "21000"
  },
  "networkFees": {
    "slowest": "0.00005",
    "slow": "0.000084",
    "average": "0.000105",
    "fast": "0.00015",
    "fastest": "0.00021"
  }
}
```

On some EVM chains, several tiers can hold the same value. For an XRP Ledger wallet, the response has no `evm` object and a single tier:

```json
{
  "networkFees": {
    "average": "0.00001"
  }
}
```

A request that omits a required field returns HTTP 400 with the reason `PAL000.020`. On Ethereum, an estimate for an origin address that can't fund the transfer, such as a new, unfunded address, can return HTTP 400 with the message `connector data request rejected` and the reason `PAL020.004`.

For a token transfer, include the token's `contract`. For example, to estimate the fee for a USDT transfer on Ethereum:

```json
{
    "blockchain": "ETHEREUM",
    "symbol": "USDT",
    "contract": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
    "originAddress": "0x55502b9d5a68b0F8a48384352295BeD968aD8AA4"
}
```

The response has the same shape:

```json
{
    "evm": {
        "gasPrice": "300000000",
        "baseFee": "100000000",
        "priorityFee": "200000000",
        "gasLimit": "65156"
    },
    "networkFees": {
        "slowest": "0.0000065156",
        "slow": "0.0000195468",
        "average": "0.0000195468",
        "fast": "0.0001042496",
        "fastest": "0.0001694056"
    }
}
```

After you preview the fee options, pass one of the `networkFees` values as `networkFee` when you create the transfer. `networkFee` is the maximum fee the transaction can pay. For more about fee tiers, see [Network fees](#network-fees).

## Create a transfer via the API

**API reference:** [Create a new transfer transaction](/products/wallet/api-docs/palisade-api/palisade-api/transactions/transactionsservice_transfertransaction)

Create a transfer with `POST /v2/vaults/{vaultId}/wallets/{walletId}/transactions/transfer`. `destinationAddress`, `symbol`, and `qty` are required. Optional fields:

- `contract` — the token contract or issuing address, for token transfers.
- `networkFee` — the maximum network fee to pay for this transaction, in base asset units (for example, ETH on Ethereum). The transaction returns it as `feeQtyLimit`. On the XRP Ledger, the transaction pays exactly this fee. If you omit it, Ripple Custody (Wallet) sets the fee.
- `sequence` — the blockchain nonce or sequence to use.
- `externalId` — your ID for this transaction, 1 to 64 characters, unique within your organization. Reusing the same `externalId` makes the request safe to retry: the API returns the existing transaction instead of creating another. See [External IDs](/products/wallet/api-and-developer-experience/api-conventions#external-ids).
- `correlationId` — an ID you use to correlate related actions.
- `config` — XRP Ledger options: `sourceTag` and `destinationTag`. Pass the tags as strings, for example `"destinationTag": "12345"`. A JSON number returns HTTP 400 with the message `invalid value for string field destinationTag`.
- `blockchain` — the EVM chain to execute the transfer on, if different from the wallet's blockchain. See [EVM cross-chain transfers](#evm-cross-chain-transfers-via-the-api).


```bash
curl -X POST "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/transactions/transfer" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationAddress": "0x55502b9d5a68b0F8a48384352295BeD968aD8AA4",
    "symbol": "USDC",
    "contract": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
    "qty": "155.672",
    "networkFee": "0.00015",
    "externalId": "ce4918bf-a199-4ce2-85a3-d0d296855384"
  }'
```

The response is the new transaction, with HTTP 200 and a `status` of `REQUESTED`. Store its `id` to track the transfer. The following example is abridged: the `asset` and `feeAsset` objects carry more asset registry fields, and the response also includes `attributes`, `snapshotRevision`, and `signingPayloads`.

```json
{
  "id": "1d6f7a52-8c3e-4b09-9a41-2f6a0c1e9b77",
  "walletId": "3b8e2c14-5f6d-4a1b-8c2e-9d0f1a2b3c4d",
  "vaultId": "9a7b6c5d-4e3f-2a1b-0c9d-8e7f6a5b4c3d",
  "organizationId": "a9a1de05-0b19-4fca-8597-1199353bd569",
  "createdBy": "a55df9e0-e14a-4410-a983-12afae46662f",
  "createdAt": "2026-07-23T10:22:22.420Z",
  "updatedAt": "2026-07-23T10:22:22.420Z",
  "status": "REQUESTED",
  "action": "PALISADE_TRANSFER",
  "externalId": "ce4918bf-a199-4ce2-85a3-d0d296855384",
  "asset": {
    "id": "2:0x1c7d4b196cb0c7b01d743fbc6116a902379c7238:USDC",
    "standard": "ERC20",
    "symbol": "USDC",
    "blockchain": "ETHEREUM",
    "decimals": 6,
    "contract": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
    "createdAt": "2025-01-10T09:00:00.000Z"
  },
  "feeAsset": {
    "id": "2::ETH",
    "standard": "NATIVE",
    "symbol": "ETH",
    "blockchain": "ETHEREUM",
    "decimals": 18,
    "createdAt": "2025-01-10T09:00:00.000Z"
  },
  "blockchain": "ETHEREUM",
  "destinationAddress": "0x55502b9d5a68b0F8a48384352295BeD968aD8AA4",
  "originAddress": "0x2352d20fC81225c8ECD8f6FAA1B37F24FED450c9",
  "origin": {
    "id": "3b8e2c14-5f6d-4a1b-8c2e-9d0f1a2b3c4d",
    "vaultId": "9a7b6c5d-4e3f-2a1b-0c9d-8e7f6a5b4c3d",
    "type": "WALLET",
    "address": "0x2352d20fC81225c8ECD8f6FAA1B37F24FED450c9"
  },
  "destination": {
    "id": "6f1e2d3c-4b5a-4987-8a6b-5c4d3e2f1a0b",
    "counterpartyId": "0c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
    "type": "COUNTERPARTY",
    "address": "0x55502b9d5a68b0F8a48384352295BeD968aD8AA4"
  },
  "qty": "155.672",
  "feeQtyLimit": "0.00015",
  "signOnly": false,
  "freezeInfo": {
    "isFrozen": false,
    "history": [],
    "authority": "FREEZE_AUTHORITY_UNSPECIFIED",
    "freezeRevision": "0"
  },
  "history": [],
  "transactionType": "Transfer",
  "reasons": [],
  "proposedAssetChanges": [],
  "confirmedAssetChanges": [],
  "problems": []
}
```

A request that omits a required field returns HTTP 400 with the reason `PAL000.020`. A transfer that your policies don't allow is still created: the response returns HTTP 200, and the transaction moves to `REJECTED` within moments. Read the cause from its `problems` array. See [Track the transaction lifecycle](/products/wallet/transactions/view-and-track-transactions#track-the-transaction-lifecycle-via-the-api).

Token transfers after compilation
For a contract-token transfer such as USDC, once the transaction reaches `COMPILED` the top-level `destination` becomes the token contract and the top-level `qty` can be `"0"`. Read the recipient and token quantity from the `TRANSFER` entry in `proposedAssetChanges` or `confirmedAssetChanges`. Use `destination` rather than the deprecated `destinationAddress`. See [Read contract-token transfers](/products/wallet/transactions/view-and-track-transactions#read-contract-token-transfers-via-the-api).

### EVM cross-chain transfers via the API

For EVM wallets, you can create transactions on any supported EVM chain where you have a balance, because your address is the same on every EVM chain. The optional `blockchain` field sets the blockchain the transfer executes on, in place of the wallet's configured blockchain. It's valid only for EVM wallets executing on another EVM chain, and lets you recover deposits sent to your address on another chain.

When creating a transfer from an EVM wallet, include the optional `blockchain` parameter to specify the target chain:

```
POST /v2/vaults/{vault_id}/wallets/{wallet_id}/transactions/transfer
{
  "destinationAddress": "0x...",
  "symbol": "USDC",
  "qty": "100",
  "blockchain": "ARBITRUM"
}
```

Default behavior
If you don't provide a `blockchain` parameter for an EVM wallet, the transaction defaults to the wallet's `blockchain`.

EVM only
The `blockchain` parameter only applies to EVM wallets. Setting it to a chain other than the wallet's own on a non-EVM wallet, or to a non-EVM chain, returns HTTP 400 with the message `blockchain override is only allowed between EVM-compatible chains` and the reason `PAL010.009`. A `blockchain` value that the API doesn't recognize isn't rejected: the transfer runs on the wallet's own blockchain. See [Behaviors to know](/products/wallet/api-and-developer-experience/api-conventions#behaviors-to-know).

Raw transactions don't use the `blockchain` parameter. For EVM wallets, the platform detects the target chain from the encoded transaction's chain ID. See [Automatic chain routing (EVM)](/products/wallet/transactions/raw-signing#automatic-chain-routing-evm).

Raw and XRP Ledger transactions
To sign and submit a pre-encoded transaction instead of a transfer, use `POST /v2/vaults/{vaultId}/wallets/{walletId}/transactions/raw`. See [Raw signing](/products/wallet/transactions/raw-signing), and [Create a new raw transaction](/products/wallet/api-docs/palisade-api/palisade-api/transactions/transactionsservice_rawtransaction) in the API reference. For XRP Ledger transaction types such as trust lines and DEX offers, see [Submit XRP Ledger transactions via the API](/products/wallet/transactions/xrpl/xrpl-via-the-api).

## Related topics

- [View and track transactions](/products/wallet/transactions/view-and-track-transactions) — Follow a payment through to confirmation in the console or via the API
- [How transactions work](/products/wallet/transactions) — Transaction stages and statuses
- [External IDs](/products/wallet/api-and-developer-experience/api-conventions#external-ids) — Make transfer requests safe to retry
- [Ripple Custody (Wallet) API reference](/products/wallet/api-docs/palisade-api/palisade-api) — Full request and response schemas