Skip to main content
T TON Adoption
← Glossary
NODE/03 · Term

TON deep-link

URL scheme `ton://...` for passing operation parameters directly to a TON wallet: recipient address, amount, comment, optionally a payload. The base UX abstraction that unites different wallets.

Aliases: ton deep link, deeplink, deep link

TON deep-link is the URL scheme ton://transfer/<address>?amount=<nano>&text=<message>. When a user clicks such a link, the OS opens the registered TON wallet (Tonkeeper, MyTonWallet, @Wallet, etc.) with the transaction form pre-filled.

Base parameters

  • amount — in nano-TON (1 TON = 10^9 nano).
  • text — comment (UTF-8, ≤ ~120 bytes).
  • bin — base64 payload for a smart-contract call (jetton transfer, NFT action, etc.).
  • expires — Unix timestamp deadline.

When to use it

  • Donation links, QR codes for offline payments, payment requests in chat — the simplest and serverless option.
  • An alternative to TON Connect for one-shot operations that don’t need a long-lived authorisation.

Limits

  • You can’t get a response back from the wallet in the browser (this is fire-and-forget) — for interactive flows you need TON Connect 2.x.
  • On desktop with no TON wallet installed, the link won’t open — the fallback should show a QR code.

Real-world examples

Simple 1 TON transfer with a comment:

ton://transfer/EQAbc...XYZ?amount=1000000000&text=Hello%20TON

Jetton transfer (USDT) — parameters are packed into bin, a base64-url payload containing the serialised transfer#0f8a7ea5 op-code, jetton amount in minor units and the destination address:

import { beginCell, toNano } from '@ton/core';

const payload = beginCell()
  .storeUint(0x0f8a7ea5, 32)              // op transfer
  .storeUint(Date.now(), 64)              // query_id
  .storeCoins(toNano('10'))               // amount
  .storeAddress(destination)              // recipient
  .storeAddress(owner)                    // response destination
  .storeBit(0)                            // no custom payload
  .storeCoins(toNano('0.05'))             // forward_ton_amount
  .storeBit(0)                            // forward_payload null
  .endCell();

const bin = payload.toBoc().toString('base64url');
const link = `ton://transfer/${jettonWallet}?amount=50000000&bin=${bin}`;

The 50_000_000 nano-TON (~$0.10) are fees for executing the jetton transfer — without them the message would bankrupt the jetton-wallet contract.

Telegram-bot integration

Inside a bot, a deep-link button is most ergonomically shipped via InlineKeyboardButton.url:

const url = `ton://transfer/${address}?amount=${nanoAmount}&text=${encodeURIComponent(memo)}`;
bot.sendMessage(chatId, 'Pay for the order:', {
  reply_markup: { inline_keyboard: [[{ text: '💎 Pay', url }]] },
});

Telegram on iOS before 10.5 wouldn’t open arbitrary ton:// schemes — you had to wrap them in https://app.tonkeeper.com/transfer/..., which redirected to the native scheme. Support has been stable since 2024-2025, but a Tonkeeper-bridge URL fallback is still reasonable for legacy clients.

Security and validation

When parsing a deep-link on a site or bot, always:

  • Verify the address — the bounceable vs non-bounceable form must match the contract type (jetton-wallets are always bounceable; wallet-V5 accepts either).
  • Convert amount to human-readable before displaying — otherwise the user sees 1000000000 and doesn’t realise it’s 1 TON.
  • Sanitise text — it can carry unicode spoofing (RTL override, zero-width).
  • Don’t trust expires — the wallet validates it itself, but in the site UI show “until 15:42”, not the raw timestamp.
  • TON Connect — for interactive operations that need a wallet response.
  • Jetton — payload format for token transfer.
  • Mini-app — embedded UI where deep-links give way to the TON Connect SDK.

Related terms