> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ton.org/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.ton.org/feedback

```json
{
  "path": "/ecosystem/ton-pay/payment-integration/transfer",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# How to build a transfer

export const Aside = ({type = "note", title = "", icon = "", iconType = "regular", children}) => {
  const asideVariants = ["note", "tip", "caution", "danger"];
  const asideComponents = {
    note: {
      outerStyle: "border-sky-500/20 bg-sky-50/50 dark:border-sky-500/30 dark:bg-sky-500/10",
      innerStyle: "text-sky-900 dark:text-sky-200",
      calloutType: "note",
      icon: <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-sky-500" aria-label="Note">
          <path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.3C10.14 1.3 12.7 3.86 12.7 7C12.7 10.14 10.14 12.7 7 12.7C5.48908 12.6974 4.0408 12.096 2.97241 11.0276C1.90403 9.9592 1.30264 8.51092 1.3 7C1.3 3.86 3.86 1.3 7 1.3ZM7 0C3.14 0 0 3.14 0 7C0 10.86 3.14 14 7 14C10.86 14 14 10.86 14 7C14 3.14 10.86 0 7 0ZM8 3H6V8H8V3ZM8 9H6V11H8V9Z"></path>
        </svg>
    },
    tip: {
      outerStyle: "border-emerald-500/20 bg-emerald-50/50 dark:border-emerald-500/30 dark:bg-emerald-500/10",
      innerStyle: "text-emerald-900 dark:text-emerald-200",
      calloutType: "tip",
      icon: <svg width="11" height="14" viewBox="0 0 11 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="text-emerald-600 dark:text-emerald-400/80 w-3.5 h-auto" aria-label="Tip">
          <path d="M3.12794 12.4232C3.12794 12.5954 3.1776 12.7634 3.27244 12.907L3.74114 13.6095C3.88471 13.8248 4.21067 14 4.46964 14H6.15606C6.41415 14 6.74017 13.825 6.88373 13.6095L7.3508 12.9073C7.43114 12.7859 7.49705 12.569 7.49705 12.4232L7.50055 11.3513H3.12521L3.12794 12.4232ZM5.31288 0C2.52414 0.00875889 0.5 2.26889 0.5 4.78826C0.5 6.00188 0.949566 7.10829 1.69119 7.95492C2.14321 8.47011 2.84901 9.54727 3.11919 10.4557C3.12005 10.4625 3.12175 10.4698 3.12261 10.4771H7.50342C7.50427 10.4698 7.50598 10.463 7.50684 10.4557C7.77688 9.54727 8.48281 8.47011 8.93484 7.95492C9.67728 7.13181 10.1258 6.02703 10.1258 4.78826C10.1258 2.15486 7.9709 0.000106649 5.31288 0ZM7.94902 7.11267C7.52078 7.60079 6.99082 8.37878 6.6077 9.18794H4.02051C3.63739 8.37878 3.10743 7.60079 2.67947 7.11294C2.11997 6.47551 1.8126 5.63599 1.8126 4.78826C1.8126 3.09829 3.12794 1.31944 5.28827 1.3126C7.2435 1.3126 8.81315 2.88226 8.81315 4.78826C8.81315 5.63599 8.50688 6.47551 7.94902 7.11267ZM4.87534 2.18767C3.66939 2.18767 2.68767 3.16939 2.68767 4.37534C2.68767 4.61719 2.88336 4.81288 3.12521 4.81288C3.36705 4.81288 3.56274 4.61599 3.56274 4.37534C3.56274 3.6515 4.1515 3.06274 4.87534 3.06274C5.11719 3.06274 5.31288 2.86727 5.31288 2.62548C5.31288 2.38369 5.11599 2.18767 4.87534 2.18767Z"></path>
        </svg>
    },
    caution: {
      outerStyle: "border-amber-500/20 bg-amber-50/50 dark:border-amber-500/30 dark:bg-amber-500/10",
      innerStyle: "text-amber-900 dark:text-amber-200",
      calloutType: "warning",
      icon: <svg className="flex-none w-5 h-5 text-amber-400 dark:text-amber-300/80" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-label="Warning">
          <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
        </svg>
    },
    danger: {
      outerStyle: "border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10",
      innerStyle: "text-red-900 dark:text-red-200",
      calloutType: "danger",
      icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" className="text-red-600 dark:text-red-400/80 w-4 h-4" aria-label="Danger">
          <path d="M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zm41.6-48c-4.3 7.4-4.3 16.6 0 24l88.3 152.9c4.3 7.4 12.2 12 20.8 12l176.6 0c8.6 0 16.5-4.6 20.8-12L453.4 268c4.3-7.4 4.3-16.6 0-24L365.1 91.1c-4.3-7.4-12.2-12-20.8-12l-176.6 0c-8.6 0-16.5 4.6-20.8 12L58.6 244zM256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"></path>
        </svg>
    }
  };
  let variant = type;
  let gotInvalidVariant = false;
  if (!asideVariants.includes(type)) {
    gotInvalidVariant = true;
    variant = "danger";
  }
  const iconVariants = ["regular", "solid", "light", "thin", "sharp-solid", "duotone", "brands"];
  if (!iconVariants.includes(iconType)) {
    iconType = "regular";
  }
  return <>
      <div className={`callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border ${asideComponents[variant].outerStyle}`} data-callout-type={asideComponents[variant].calloutType}>
        <div className="mt-0.5 w-4" data-component-part="callout-icon">
          {}
          {icon === "" ? asideComponents[variant].icon : <Icon icon={icon} iconType={iconType} size={14} />}
        </div>
        <div className={`text-sm prose min-w-0 w-full ${asideComponents[variant].innerStyle}`} data-component-part="callout-content">
          {gotInvalidVariant ? <p>
              <span className="font-bold">
                Invalid <code>type</code> passed!
              </span>
              <br />
              <span className="font-bold">Received: </span>
              {type}
              <br />
              <span className="font-bold">Expected one of: </span>
              {asideVariants.join(", ")}
            </p> : <>
              {title && <p className="font-bold">{title}</p>}
              {children}
            </>}
        </div>
      </div>
    </>;
};

Use this server-side helper to generate a canonical TON message payload and a tracking reference.

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import { createTonPayTransfer } from "@ton-pay/api";

const { message, bodyBase64Hash, reference } = await createTonPayTransfer(
  {
    amount: 10.5,
    asset: "TON",
    recipientAddr: "<RECIPIENT_ADDRESS>",
    senderAddr: "<SENDER_ADDRESS>",
    commentToSender: "Payment for Order #1234",
    commentToRecipient: "Order #1234",
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

<Aside type="tip">
  The TON Pay API key is optional, but providing it enables transaction visibility in the TON Pay Merchant Dashboard, webhook notifications, and wallet management features.
</Aside>

## Function signature

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
createTonPayTransfer(
  params: CreateTonPayTransferParams,
  options?: APIOptions
): Promise<CreateTonPayTransferResponse>
```

### Transfer parameters

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
type CreateTonPayTransferParams = {
  amount: number;
  asset: string;
  recipientAddr?: string;
  senderAddr: string;
  queryId?: number;
  commentToSender?: string;
  commentToRecipient?: string;
};
```

### API options

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
type APIOptions = {
  chain?: "mainnet" | "testnet";
  apiKey?: string; // optional
};
```

<ParamField body="chain" type="string" default="mainnet">
  Target blockchain network. Use `"mainnet"` for production or `"testnet"` for development and testing.
</ParamField>

<ParamField body="apiKey" type="string">
  The optional TON Pay API key from the Merchant Dashboard. When provided, it enables:

  * Transaction visibility in the TON Pay Merchant Dashboard.
  * Webhook notifications for completed transactions.
  * Receiving wallet management from the Merchant Dashboard.
</ParamField>

## Parameter details

<ParamField body="amount" type="number" required>
  Human-readable payment amount. Decimals are allowed, for example, 10.5 TON.
</ParamField>

<ParamField body="asset" type="string" required>
  Asset to transfer. Use "TON" for Toncoin or a jetton master address or constant, for example, USDT.
</ParamField>

<Aside type="danger" title="Mainnet address risk">
  Token constants such as `USDT` always reference mainnet jetton master addresses and are not affected by the `chain` option. Using them on testnet may send transactions to mainnet contracts.

  For testnet, explicitly pass the correct testnet jetton master address instead of using token constants.
</Aside>

<ParamField body="recipientAddr" type="string">
  Payee wallet address. Optional if an API key is provided. Defaults to the merchant’s default wallet address configured in the Merchant Dashboard.
</ParamField>

<ParamField body="senderAddr" type="string" required>
  Payer wallet address. In UI flows, obtain it from TON Connect.
</ParamField>

<ParamField body="queryId" type="number">
  Jetton only. Numeric identifier embedded into the jetton payload for idempotency and tracking. Ignored for Toncoin payments.
</ParamField>

<ParamField body="commentToSender" type="string">
  Short note visible to the user in the wallet while signing.

  <Aside>
    Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. Keep comments under 120 characters to avoid increased gas fees.
  </Aside>
</ParamField>

<ParamField body="commentToRecipient" type="string">
  Note visible to the recipient after the transfer is received.

  <Aside>
    Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. Keep comments under 120 characters to avoid increased gas fees.
  </Aside>
</ParamField>

## Predefined asset constants

Built-in constants can be used instead of raw addresses.

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import { createTonPayTransfer, TON, USDT } from "@ton-pay/api";

// Toncoin transfer
await createTonPayTransfer(
  {
    amount: 1,
    asset: TON, // same as "TON"
    recipientAddr: "<RECIPIENT_ADDRESS>", // shortened example; replace with a full wallet address
    senderAddr: "<SENDER_ADDRESS>",       // shortened example; replace with a full wallet address
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);

// USDT jetton transfer
await createTonPayTransfer(
  {
    amount: 25,
    asset: USDT, // mainnet USDT jetton master address
    recipientAddr: "<RECIPIENT_ADDRESS>", // shortened example; replace with a full wallet address
    senderAddr: "<SENDER_ADDRESS>",       // shortened example; replace with a full wallet address
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

<Aside type="danger" title="Mainnet address risk">
  In `asset`, token constants such as `USDT` always reference mainnet jetton master addresses and are not affected by the `chain` option. Using them on testnet may send transactions to mainnet contracts.

  For testnet, explicitly pass the correct testnet jetton master address instead of using token constants.
</Aside>

Response:

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
type CreateTonPayTransferResponse = {
  message: {
    address: string;
    amount: string;
    payload: string;
  };
  bodyBase64Hash: string;
  reference: string;
};
```

## Response fields

<ResponseField name="message" type="object" required>
  Prebuilt TON Connect transaction message. Intended to be passed to `sendTransaction` as `messages: [message]`.
</ResponseField>

<ResponseField name="bodyBase64Hash" type="string" required>
  Base64 hash of the signed message body content (payload). Used with `getTonPayTransferByBodyHash`.
</ResponseField>

<ResponseField name="reference" type="string" required>
  Tracking reference string. Used with `getTonPayTransferByReference`.
</ResponseField>

The SDK call returns a ready-to-send message along with identifiers for subsequent status lookups. Always persist tracking identifiers server-side before sending the transaction to the user.

## Optional API key configuration

The TON Pay API key is optional but enables merchant features, including transaction visibility in the dashboard, webhook notifications, and centralized wallet management in the TON Pay Merchant Dashboard.

### Obtain an optional API key

<Steps>
  <Step title="Open the Merchant Dashboard">
    Open the TON Pay Merchant Dashboard and sign in to the merchant account.
  </Step>

  <Step title="Open Developer settings">
    Navigate to <kbd>Developer</kbd> → <kbd>API Keys</kbd> sections to set up the optional API key.
  </Step>

  <Step title="Generate or copy an optional API key">
    Generate a new API key (optional) or copy an existing one and store it securely.
  </Step>
</Steps>

### Usage in code

```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
import { createTonPayTransfer } from "@ton-pay/api";

const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: 10.5,
    asset: "TON",
    // recipientAddr is optional when API key is provided
    // Will use merchant's default wallet from the Merchant Dashboard
    senderAddr: userWalletAddress,
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

### Optional API key capabilities

| Capability                            | With optional API key                                                                                             | Without API key                                                                       |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Transaction visibility and monitoring | Transfers appear in the TON Pay Merchant Dashboard with status tracking and full transaction history.             | Transfers are processed on-chain but are not visible in the Merchant dashboard.       |
| Webhook notifications                 | Real-time HTTP POST notifications are sent for completed and failed payments.                                     | No webhook notifications; payment status must be obtained through the manual polling. |
| Receiving wallet management           | Receiving wallets are managed from the TON Pay Merchant Dashboard; addresses can be updated without code changes. | Receiving wallet addresses must be hard-coded in the application.                     |
| `recipientAddr` handling              | Optional; when omitted, the merchant’s default wallet from the Merchant Dashboard is used automatically.          | Required for every transfer.                                                          |

With optional API key: `recipientAddr` is optional.

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
const result = await createTonPayTransfer(
  {
    amount: 10,
    asset: "TON",
    senderAddr: "<SENDER_ADDRESS>",
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

Without API key (the API key remains optional): `recipientAddr` is required.

```ts theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
// Works without API key (it is optional) - transaction processes normally
const result = await createTonPayTransfer(
{
  amount: 10,
  asset: "TON",
  recipientAddr: "<RECIPIENT_ADDRESS>",
  senderAddr: "<SENDER_ADDRESS>",
},
{ chain: "testnet" } // No optional apiKey - transaction works but no dashboard features
);
```

## Testnet configuration

<Aside type="danger" title="Funds at risk">
  Running tests on mainnet can result in irreversible loss of real TON. Always use `chain: "testnet"` and testnet wallet addresses during development. Verify the network before switching to mainnet.
</Aside>

### Set up testnet

<Steps>
  <Step title="Set chain to testnet">
    Configure the `chain` option to `"testnet"` in the API calls:

    ```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
    const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
    {
      amount: 5.0,
      asset: "TON",
      recipientAddr: "<RECIPIENT_ADDRESS>",
      senderAddr: "<SENDER_ADDRESS>",
    },
    {
      chain: "testnet", // Use testnet
      apiKey: "<TONPAY_API_KEY>", // optional
    }
    );
    ```
  </Step>

  <Step title="Use testnet wallet addresses">
    Ensure all wallet addresses are valid [testnet addresses](/foundations/addresses/formats).
  </Step>

  <Step title="Obtain testnet TON">
    * Use testnet wallet account: [Tonkeeper](/ecosystem/wallet-apps/tonkeeper) or other TON wallets.
    * [Get testnet TON](/ecosystem/wallet-apps/get-coins) from a faucet to test transactions.
  </Step>

  <Step title="Configure testnet jettons">
    If testing with jettons, use the correct testnet jetton master addresses; not mainnet addresses.

    ```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
    // Example: Testnet USDT (use actual testnet address)
    await createTonPayTransfer(
    {
      amount: 10,
      asset: "<TESTNET_USDT_MASTER_ADDRESS>", // Testnet jetton address
      recipientAddr: "<RECIPIENT_ADDRESS>",
      senderAddr: "<SENDER_ADDRESS>",
    },
    { chain: "testnet" }
    );
    ```
  </Step>
</Steps>

### Configure environment

Use environment variables to switch between mainnet and testnet:

```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
// .env.development
TON_CHAIN=testnet
MERCHANT_WALLET_ADDRESS=EQC...TESTNET_ADDRESS

// .env.production
TON_CHAIN=mainnet
MERCHANT_WALLET_ADDRESS=EQC...MAINNET_ADDRESS
```

```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
// In the application code
const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: orderAmount,
    asset: "TON",
    recipientAddr: "<WALLET_ADDRESS>",
    senderAddr: "<CUSTOMER_WALLET_ADDRESS>",
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

### Verify testnet transactions

Verify testnet transactions using testnet block [explorers](/ecosystem/explorers/overview):

* [Testnet Tonviewer](https://testnet.tonviewer.com/)
* [Testnet Tonscan](https://testnet.tonscan.org/)

```typescript theme={"theme":{"light":"github-light-default","dark":"dark-plus"},"languages":{"custom":["/resources/grammars/tolk.tmLanguage.json","/resources/grammars/tlb.tmLanguage.json","/resources/grammars/fift.tmLanguage.json","/resources/grammars/tasm.tmLanguage.json","/resources/grammars/func.tmLanguage.json"]}}
// Replace txHash with the actual transaction hash.
// After transaction completes:
console.log(`View on explorer: https://testnet.tonscan.org/tx/${txHash}`);
```

### Apply testing best practices

* Test all payment outcomes on testnet, including success, failures, user rejections, and edge cases.
* Verify webhook endpoint correctly processes testnet webhooks; payload structure matches mainnet.
* Validate behavior across different amounts, including small, large, and fractional values.
* Ensure `reference` and `bodyBase64Hash` are persisted and usable for status lookups.
* Exercise error paths such as insufficient balance, invalid addresses, and network issues.
