> ## Documentation Index
> Fetch the complete documentation index at: https://hedera-0c6e0218-add-json-rpc-release-notes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfer cryptocurrency

A transaction that transfers HBAR and tokens between Hedera accounts. You can enter multiple transfers in a single transaction. The net value of HBAR between the sending accounts and receiving accounts must equal zero.

For a CryptoTransferTransactionBody:

<Warning>
  * Max of 10 balance adjustments in its HBAR transfer list.
  * Max of 10 fungible token balance adjustments across all its token transfer list.
  * Max of 10 NFT ownership changes across all its token transfer list.
  * Max of 20 balance adjustments or NFT ownership changes implied by a transaction (including custom fees).
  * If you are transferring a token with custom fees, only two levels of nesting fees are allowed.
  * The sending account is responsible to pay for the custom token fees.
</Warning>

**Transaction Fees**

* Please see the transaction and query [fees](/hedera/networks/mainnet/fees#transaction-and-query-fees) table for the base transaction fee
* Please use the [Hedera fee estimator](https://hedera.com/fees) to estimate your transaction fee cost

**Spender Account Allowances**

An account can have [another account](/hedera/sdks-and-apis/sdks/accounts-and-hbar/approve-an-allowance) spend tokens on its behalf. If the delegated spender account is transacting tokens from the owner account that authorized the allowance, the owner account needs to be specified in the transfer transaction by calling one of the following:

* `addApprovedHbarTransfer()`
* `addApprovedTokenTransfer()`
* `addApprovedNftTransfer()`
* `addApprovedTokenTransferWithDecimals()`

The debiting account is the owner's account when using this feature.

<Info>
  **Note**: The allowance spender must pay the fee for the transaction.
</Info>

**Account Allowance Hooks ([HIP-1195](https://hips.hedera.com/hip/hip-1195))**

An account can have [Hiero Hooks](/hedera/core-concepts/accounts/hiero-hooks) that act as programmable allowances. Instead of using traditional ERC-style allowances, a `TransferTransaction` can reference a hook on the sending or receiving account. The hook's EVM bytecode runs and must return `true` for the transfer to proceed. This enables custom validation logic like one-time passcodes, compliance rules, or conditional transfer approvals.

To invoke a hook, use the `WithHook` variants of the transfer methods:

* `addHbarTransferWithHook()` — HBAR transfers with a [`FungibleHookCall`](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks#hookcall-base)
* `addTokenTransferWithHook()` — Fungible token transfers with a [`FungibleHookCall`](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks#hookcall-base)
* `addNftTransferWithHook()` — NFT transfers with optional sender and receiver [`NftHookCall`](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks#hookcall-base)

The counterpart transfer entry (the other side of the zero-sum) is added separately with the standard `addHbarTransfer()`, `addTokenTransfer()`, or `addNftTransfer()` methods.

<Info>
  **Hook Invocation Limits**

  Child records generated by hook calls are capped at 50 per transaction (`consensus.handle.maxFollowingRecords=50`). Hook executions are not supported in batch or scheduled transactions.
</Info>

**Transaction Signing Requirements**

* The accounts the tokens are being debited from are required to sign the transaction
  * If an authorized spender account is spending on behalf of the account that owns the tokens then the spending account is required to sign
* The transaction fee-paying account is required to sign the transaction

### Methods

| Method                                                                                        | Type                                                                                                                                                                                                                                                                                                                                                 | Description                                                                                                                                                                                                                       |
| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addHbarTransfer(<accountId>, <evmAddress>, <value>)`                                         | [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), string, HBAR                                                                                                                                                                                                                                                                    | The account involved in the transfer and the number of HBAR. The sender and recipient values must net zero.                                                                                                                       |
| `addTokenTransfer(<tokenId>, <accountId>, <evmAddress>, <value>)`                             | [TokenId](../token-service/token-id), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), string, long                                                                                                                                                                                                                              | The ID of the token, the account ID involved in the transfer, and the number of tokens to transfer. The sender and recipient values must net zero.                                                                                |
| `addNftTransfer(<nftId>, <sender>, <receiver>)`                                               | [NftId](../token-service/nft-id), [AccountId](../specialized-types), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid)                                                                                                                                                                                                             | The NFT ID (token + serial number), the sending account, and receiving account.                                                                                                                                                   |
| `addTokenTransferWithDecimals(<tokenId>, <accountId>, <value>, <int>)`                        | [TokenId](../token-service/token-id), AccountId, long, decimals                                                                                                                                                                                                                                                                                      | The ID of the token, the account ID involved in the transfer, the number of tokens to transfer, the decimals of the token. The sender and recipient values must net zero.                                                         |
| `addApprovedHbarTransfer(<ownerAccountId>, <amount>)`                                         | [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), Hbar                                                                                                                                                                                                                                                                            | The owner account ID the spender is authorized to transfer from and the amount. Applicable to allowance transfers only.                                                                                                           |
| `addApprovedTokenTransfer(<tokenId>, <accountId>, <value>)`                                   | [TokenId](../token-service/token-id), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), long                                                                                                                                                                                                                                      | The owner account ID and token the spender is authorized to transfer from. The debiting account is the owner account. Applicable to allowance transfers only.                                                                     |
| `addApprovedTokenTransferWithDecimals(<tokenId>, <accountId>, <value>, <decimals>)`           | [TokenId](../token-service/token-id), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), long, int                                                                                                                                                                                                                                 | The owner account ID and token ID (with decimals) the spender is authorized to transfer from. The debit account is the account ID of the sender. Applicable to allowance transfers only.                                          |
| `addApprovedNftTransfer(<nftId>, <sender>, <receiver>)`                                       | [NftId](../token-service/nft-id), [AccountId](../specialized-types), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid)                                                                                                                                                                                                             | The NFT ID the spender is authorized to transfer. The sender is the owner account and receiver is the receiving account. Applicable to allowance transfers only.                                                                  |
| `addHbarTransferWithHook(<accountId>, <amount>, <hookCall>)`                                  | [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), Hbar, [FungibleHookCall](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks)                                                                                                                                                                                  | Adds an HBAR transfer with an attached account allowance hook call. The `FungibleHookType` on the hook call determines sender/receiver role and pre/pre-post mode. See [Hiero Hooks](/hedera/core-concepts/accounts/hiero-hooks). |
| `addTokenTransferWithHook(<tokenId>, <accountId>, <amount>, <hookCall>)`                      | [TokenId](../token-service/token-id), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), long, [FungibleHookCall](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks)                                                                                                                                            | Adds a fungible token transfer with an attached account allowance hook call. See [Hiero Hooks](/hedera/core-concepts/accounts/hiero-hooks).                                                                                       |
| `addNftTransferWithHook(<nftId>, <sender>, <receiver>, <senderHookCall>, <receiverHookCall>)` | [NftId](../token-service/nft-id), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), [AccountId](/hedera/sdks-and-apis/sdks/specialized-types#accountid), [NftHookCall](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks), [NftHookCall](/hedera/sdks-and-apis/sdks/accounts-and-hbar/create-and-manage-hooks) | Adds an NFT transfer with optional sender and/or receiver hook calls. Pass `null` for either hook call if not needed. See [Hiero Hooks](/hedera/core-concepts/accounts/hiero-hooks).                                              |

<CodeGroup>
  ```java Java theme={null}
  // Create a transaction to transfer 1 HBAR 
  TransferTransaction transaction = new TransferTransaction()
       .addHbarTransfer(OPERATOR_ID, new Hbar(-1))
       .addHbarTransfer(newAccountId, evmAddress, new Hbar(1));

  //Submit the transaction to a Hedera network
  TransactionResponse txResponse = transaction.execute(client);

  //Request the receipt of the transaction
  TransactionReceipt receipt = txResponse.getReceipt(client);

  //Get the transaction consensus status
  Status transactionStatus = receipt.status;

  System.out.println("The transaction consensus status is " +transactionStatus);

  //Version 2.0.0
  ```

  ```javascript JavaScript theme={null}
  // Create a transaction to transfer 1 HBAR
  const transaction = new TransferTransaction()
      .addHbarTransfer(OPERATOR_ID, new Hbar(-1))
      .addHbarTransfer(newAccountId, evmAddress, new Hbar(1));
      
  //Submit the transaction to a Hedera network
  const txResponse = await transaction.execute(client);

  //Request the receipt of the transaction
  const receipt = await txResponse.getReceipt(client);

  //Get the transaction consensus status
  const transactionStatus = receipt.status;

  console.log("The transaction consensus status is " +transactionStatus.toString());

  //v2.0.0
  ```

  ```go Go theme={null}
  // Create a transaction to transfer 1 HBAR
  transaction := hedera.NewTransferTransaction().
  		AddHbarTransfer(client.GetOperatorAccountID(), hedera.NewHbar(-1)).
  		AddHbarTransfer(hedera.AccountID{Account: 3}, hedera.NewHbar(1))

  //Submit the transaction to a Hedera network
  txResponse, err := transaction.Execute(client)

  if err != nil {
      panic(err)
  }

  //Request the receipt of the transaction
  receipt, err := txResponse.GetReceipt(client)

  if err != nil {
      panic(err)
  }

  //Get the transaction consensus status
  transactionStatus := receipt.Status

  fmt.Printf("The transaction consensus status is %v\n", transactionReceipt.Status)

  //Version 2.0.0
  ```

  ```rust Rust theme={null}
  // Create the transfer transaction
  let transaction = TransferTransaction::new()
      .hbar_transfer(account_id, Hbar::from(-10))
      .hbar_transfer(recipient_id, Hbar::from(10));

  // Freeze the transaction for signing, sign with the private key of the account that is sending hbars
  let tx_response = transaction
      .freeze_with(&client)?
      .sign(account_key)
      .execute(&client).await?;

  // Request the receipt of the transaction
  let receipt = tx_response.get_receipt(&client).await?;

  // Get the transaction consensus status
  let status = receipt.status;

  println!("The transaction consensus status is {:?}", status);

  // v0.34.0
  ```
</CodeGroup>

***

## Transfer with account allowance hooks

The following examples demonstrate how to invoke [Hiero Hooks](/hedera/core-concepts/accounts/hiero-hooks) during a transfer. Each `WithHook` method attaches a hook call to a specific transfer entry. The hook's EVM bytecode executes and must return `true` for the transfer to succeed.

### Hook call types

Hooks use typed call objects that extend a base `HookCall`:

| Class              | Used For                          | Hook Type Enum                                                                                                        |
| ------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `FungibleHookCall` | HBAR and fungible token transfers | `FungibleHookType`: `PRE_TX_ALLOWANCE_HOOK`, `PRE_POST_TX_ALLOWANCE_HOOK` (Java/JS); `PRE_HOOK`, `PRE_POST_HOOK` (Go) |
| `NftHookCall`      | NFT transfers                     | `NftHookType`: `PRE_HOOK_SENDER`, `PRE_POST_HOOK_SENDER`, `PRE_HOOK_RECEIVER`, `PRE_POST_HOOK_RECEIVER`               |

Each hook call requires a `hookId` (the 64-bit ID of the hook on the owning account) and an `EvmHookCall` containing `data` (extra bytes passed to the hook) and `gasLimit` (maximum gas the payer will pay for this hook).

### Example: HBAR transfer with a pre-hook

<CodeGroup>
  ```java Java theme={null}
  // Define the hook call
  FungibleHookCall senderHook = new FungibleHookCall(
      1001L,
      new EvmHookCall(new byte[]{0x01, 0x02}, 50000L),
      FungibleHookType.PRE_TX_ALLOWANCE_HOOK
  );

  // Build the transfer with the hook reference
  TransactionResponse response = new TransferTransaction()
      .addHbarTransferWithHook(senderAccountId, Hbar.from(-100), senderHook)
      .addHbarTransfer(receiverAccountId, Hbar.from(100))
      .freezeWith(client)
      .sign(senderKey)
      .execute(client);

  TransactionReceipt receipt = response.getReceipt(client);
  System.out.println("Transfer with hook: " + receipt.status);
  ```

  ```javascript JavaScript theme={null}
  import {
      TransferTransaction,
      FungibleHookCall,
      EvmHookCall,
      FungibleHookType,
      Hbar,
  } from "@hiero-ledger/sdk";

  // Define the hook call
  const senderHook = new FungibleHookCall({
      hookId: 1001,
      evmHookCall: new EvmHookCall()
          .setData(new Uint8Array([0x01, 0x02]))
          .setGasLimit(50000),
      type: FungibleHookType.PRE_TX_ALLOWANCE_HOOK,
  });

  // Build the transfer with the hook reference
  const tx = await (
      await new TransferTransaction()
          .addHbarTransferWithHook(senderAccountId, Hbar.from(-100), senderHook)
          .addHbarTransfer(receiverAccountId, Hbar.from(100))
          .freezeWith(client)
          .sign(senderKey)
  ).execute(client);

  const receipt = await tx.getReceipt(client);
  console.log(`Transfer with hook: ${receipt.status}`);
  ```

  ```go Go theme={null}
  senderHook := hedera.NewFungibleHookCall(
      1001,
      *hedera.NewEvmHookCall().
          SetData([]byte{0x01, 0x02}).
          SetGasLimit(50000),
      hedera.PRE_HOOK,
  )

  response, err := hedera.NewTransferTransaction().
      AddHbarTransferWithHook(senderAccountID, hedera.HbarFrom(-100, hedera.HbarUnits.Hbar), senderHook).
      AddHbarTransfer(receiverAccountID, hedera.HbarFrom(100, hedera.HbarUnits.Hbar)).
      FreezeWith(client).
      Sign(senderKey).
      Execute(client)

  receipt, err := response.GetReceipt(client)
  fmt.Printf("Transfer with hook: %v\n", receipt.Status)
  ```
</CodeGroup>

### Example: NFT transfer with sender and receiver hooks

<CodeGroup>
  ```java Java theme={null}
  NftId nftId = new NftId(TokenId.fromString("0.0.12345"), 1);

  NftHookCall senderHook = new NftHookCall(
      1002L,
      new EvmHookCall(new byte[]{0x03, 0x04}, 60000L),
      NftHookType.PRE_HOOK_SENDER
  );

  NftHookCall receiverHook = new NftHookCall(
      1003L,
      new EvmHookCall(new byte[]{0x05, 0x06}, 40000L),
      NftHookType.PRE_HOOK_RECEIVER
  );

  TransactionResponse response = new TransferTransaction()
      .addNftTransferWithHook(
          nftId,
          senderAccountId,
          receiverAccountId,
          senderHook,     // Sender hook (or null if not needed)
          receiverHook    // Receiver hook (or null if not needed)
      )
      .freezeWith(client)
      .sign(senderKey)
      .execute(client);

  System.out.println("NFT transfer with hooks: " + response.getReceipt(client).status);
  ```

  ```javascript JavaScript theme={null}
  import {
      TransferTransaction,
      NftHookCall,
      EvmHookCall,
      NftHookType,
      NftId,
      TokenId,
  } from "@hiero-ledger/sdk";

  const nftId = new NftId(TokenId.fromString("0.0.12345"), 1);

  const senderHook = new NftHookCall({
      hookId: 1002,
      evmHookCall: new EvmHookCall()
          .setData(new Uint8Array([0x03, 0x04]))
          .setGasLimit(60000),
      type: NftHookType.PRE_HOOK_SENDER,
  });

  const receiverHook = new NftHookCall({
      hookId: 1003,
      evmHookCall: new EvmHookCall()
          .setData(new Uint8Array([0x05, 0x06]))
          .setGasLimit(40000),
      type: NftHookType.PRE_HOOK_RECEIVER,
  });

  const tx = await (
      await new TransferTransaction()
          .addNftTransferWithHook(
              nftId,
              senderAccountId,
              receiverAccountId,
              senderHook,
              receiverHook
          )
          .freezeWith(client)
          .sign(senderKey)
  ).execute(client);

  console.log(`NFT transfer with hooks: ${(await tx.getReceipt(client)).status}`);
  ```

  ```go Go theme={null}
  nftID := hedera.NftID{TokenID: hedera.TokenIDFromString("0.0.12345"), SerialNumber: 1}

  senderHook := hedera.NewNftHookCall(
      1002,
      *hedera.NewEvmHookCall().
          SetData([]byte{0x03, 0x04}).
          SetGasLimit(60000),
      hedera.PRE_HOOK_SENDER,
  )

  receiverHook := hedera.NewNftHookCall(
      1003,
      *hedera.NewEvmHookCall().
          SetData([]byte{0x05, 0x06}).
          SetGasLimit(40000),
      hedera.PRE_HOOK_RECEIVER,
  )

  response, err := hedera.NewTransferTransaction().
      AddNftTransferWithHook(nftID, senderAccountID, receiverAccountID, senderHook, receiverHook).
      FreezeWith(client).
      Sign(senderKey).
      Execute(client)

  receipt, err := response.GetReceipt(client)
  fmt.Printf("NFT transfer with hooks: %v\n", receipt.Status)
  ```
</CodeGroup>

### Example: Fungible token transfer with pre/post hook

<CodeGroup>
  ```java Java theme={null}
  TokenId tokenId = TokenId.fromString("0.0.54321");

  FungibleHookCall prePostHook = new FungibleHookCall(
      1004L,
      new EvmHookCall(new byte[]{0x07, 0x08}, 70000L),
      FungibleHookType.PRE_POST_TX_ALLOWANCE_HOOK
  );

  TransactionResponse response = new TransferTransaction()
      .addTokenTransferWithHook(tokenId, senderAccountId, -1000L, prePostHook)
      .addTokenTransfer(tokenId, receiverAccountId, 1000L)
      .freezeWith(client)
      .sign(senderKey)
      .execute(client);

  System.out.println("Token transfer with pre/post hook: " + response.getReceipt(client).status);
  ```

  ```javascript JavaScript theme={null}
  import {
      TransferTransaction,
      FungibleHookCall,
      EvmHookCall,
      FungibleHookType,
      TokenId,
  } from "@hiero-ledger/sdk";

  const tokenId = TokenId.fromString("0.0.54321");

  const prePostHook = new FungibleHookCall({
      hookId: 1004,
      evmHookCall: new EvmHookCall()
          .setData(new Uint8Array([0x07, 0x08]))
          .setGasLimit(70000),
      type: FungibleHookType.PRE_POST_TX_ALLOWANCE_HOOK,
  });

  const tx = await (
      await new TransferTransaction()
          .addTokenTransferWithHook(tokenId, senderAccountId, -1000, prePostHook)
          .addTokenTransfer(tokenId, receiverAccountId, 1000)
          .freezeWith(client)
          .sign(senderKey)
  ).execute(client);

  console.log(`Token transfer with pre/post hook: ${(await tx.getReceipt(client)).status}`);
  ```

  ```go Go theme={null}
  tokenID := hedera.TokenIDFromString("0.0.54321")

  prePostHook := hedera.NewFungibleHookCall(
      1004,
      *hedera.NewEvmHookCall().
          SetData([]byte{0x07, 0x08}).
          SetGasLimit(70000),
      hedera.PRE_POST_HOOK,
  )

  response, err := hedera.NewTransferTransaction().
      AddTokenTransferWithHook(tokenID, senderAccountID, -1000, prePostHook).
      AddTokenTransfer(tokenID, receiverAccountID, 1000).
      FreezeWith(client).
      Sign(senderKey).
      Execute(client)

  receipt, err := response.GetReceipt(client)
  fmt.Printf("Token transfer with pre/post hook: %v\n", receipt.Status)
  ```
</CodeGroup>

### Hook execution order

When a `TransferTransaction` invokes multiple hooks, the network executes them in a strict order:

1. **Pre-hooks**: All `PRE_HOOK_SENDER` / `PRE_HOOK_RECEIVER` hooks execute in the order their transfers appear (HBAR first, then each token transfer list). For NFT transfers with both sender and receiver hooks, the sender hook executes first.
2. **Pre/Post hooks (pre-transfer call)**: All `PRE_POST_HOOK` variants execute their `allowPre(...)` function, in the same transfer order.
3. **Main transfer logic executes.**
4. **Pre/Post hooks (post-transfer call)**: The same hooks execute their `allowPost(...)` function, in the same order as step 2.

***

## Get transaction values

| Method                | Type                                 | Description                                              |
| --------------------- | ------------------------------------ | -------------------------------------------------------- |
| `getHbarTransfers()`  | Map\<AccountId, Hbar>                | Returns a list of the hbar transfers in this transaction |
| `getTokenTransfers()` | Map\<TokenId, Map\<AccountId, long>> | Returns the list of token transfers in the transaction   |

<CodeGroup>
  ```java Java theme={null}
  // Create a transaction 
  CryptoTransferTransaction transaction = new CryptoTransferTransaction()
      .addSender(OPERATOR_ID, new Hbar(1)
      .addRecipient(newAccountId, new Hbar(1));

  //Get transfers
  List<Transfer> transfers = transaction.getTransfers();

  //v2.0.0
  ```

  ```javascript JavaScript theme={null}
  // Create a transaction 
  const transaction = new CryptoTransferTransaction()
      .addSender(OPERATOR_ID, new Hbar(1))
      .addRecipient(newAccountId, new Hbar(1));

  //Get transfers
  const transfers = transaction.getTransfers();

  //v2.0.0
  ```

  ```go Go theme={null}
  // Create a transaction 
  transaction := hedera.NewTransferTransaction().
  		AddHbarTransfer(client.GetOperatorAccountID(), hedera.NewHbar(-1)).
  		AddHbarTransfer(hedera.AccountID{Account: 3}, hedera.NewHbar(1))
  //Get transfers
  transfers := transaction.GetTransfers()

  //v2.0.0
  ```
</CodeGroup>
