← back to blog

Telegram bot deep link explained: start payloads and how they actually work

A Telegram deep link is just a URL that tells a Telegram client to open something specific inside the app instead of a browser tab. The most common form looks like this:

https://t.me/yourbotname?start=abc123

Open that link on a phone with Telegram installed and it opens a chat with @yourbotname, with the “Start” button ready to go. Tap it, and the bot receives a /start command with abc123 attached. That’s the whole trick. No session, no cookie, no redirect chain. Telegram parses the URL, resolves the username, and hands the bot whatever came after ?start=.

It’s a small mechanism, but it’s the backbone of almost every non-trivial Telegram bot flow: onboarding, account linking, content routing, and yes, proxy configuration, which is where this intersects with what we run at telegramvault.org.

How the payload actually reaches the bot

This part trips people up because it looks like a normal query string, but it isn’t handled like one. Telegram doesn’t pass start=abc123 to the bot as an HTTP parameter. Instead, the client turns it into a bot command: /start abc123. That’s the literal text your bot receives in the message.text field via the Bot API. Your bot’s /start handler is responsible for splitting that string and pulling abc123 out of it.

Two things follow from that:

First, the payload has to survive being embedded in a text command, so Telegram restricts what characters are allowed: letters, digits, underscores, and hyphens, up to 64 characters. No spaces, no punctuation, no arbitrary binary. If you need to pass structured data, you encode it yourself, usually as a short opaque token that your bot looks up server-side, or as a compact string like ref-8821 that you parse with a simple split.

Second, /start fires every time someone opens the link, not just the first time. If a user already has a chat open with your bot and clicks a new deep link with a different payload, Telegram sends /start again with the new value. Your handler needs to treat every /start as “here’s a payload to process,” not “this is a brand new user.”

start, startgroup, and startchannel

?start= isn’t the only variant. ?startgroup= does something different: it prompts the user to add the bot to a group they administer, and once added, the bot receives the payload the same way, through a command, so it can route the group into a specific configuration on join. ?startchannel= works the same way for channels. Both are useful when a bot needs to know why it was added somewhere, not just that it was added, without you having to build a separate admin panel just to tell it.

For anyone building a bot that has to handle multiple entry points (a support bot added to different client channels, for instance), this is the difference between a bot that has to ask “which account is this for?” on every install and one that already knows.

What the payload is actually good for

The realistic uses of a start payload come down to giving the bot enough context to skip a step it would otherwise have to ask the user about. A few honest examples:

  • Deep linking into a specific bot state. Instead of a user landing on a generic menu and hunting for the right option, a link can drop them straight into the flow for, say, retrieving a specific proxy config or resuming a setup they started on a website.
  • Tying a Telegram session to a web session. A site can generate a short-lived token, put it in the payload, and when the user opens the bot link and hits start, the bot reports that token back to the site’s backend to confirm “this Telegram account belongs to this browser session.” This is the same pattern Telegram’s own login widget uses under the hood, just implemented manually.
  • Knowing which piece of content sent someone to the bot. If you post a bot link in three different channels, a different payload per channel tells you which one is generating traffic, purely as attribution, the same way a UTM parameter tells a website which ad drove a click.

What none of that requires is a reward or referral mechanism bolted onto it. The payload is a routing and attribution tool. Treat it as plumbing, not as a growth feature, and it stays simple to reason about and to secure.

The security angle: what a payload is not

Here’s the part that matters most from an account safety standpoint: a start payload is not private. Anyone who has the link can see the payload in plain text, because it’s sitting right there in the URL. If you paste a deep link into a public channel, a group chat, or a forwarded message, the payload travels with it, visible to everyone who sees that message.

That means you should never put anything sensitive directly in the payload itself, no raw credentials, no long-lived auth tokens, nothing that would matter if it leaked. The standard pattern is to generate a short random token, store the actual sensitive data server-side keyed to that token, and put only the token in the link. If the link leaks, the token can be expired or single-use, and the underlying data was never exposed in the URL to begin with.

The other side of this is phishing. Because deep links resolve by username, a lookalike bot with a similar name can generate its own ?start= links that look identical in a forwarded message. The link itself doesn’t prove anything about who controls the bot behind it. Before entering anything into a bot reached via a deep link, especially one asking for account details or wallet information, check the actual @username Telegram resolves to, not just the link text. This is standard advice for any Telegram bot, but it’s worth repeating because deep links make it easy to disguise where a click actually leads.

You’ll see deep links in two forms. The https://t.me/... form works everywhere, including on a device with no Telegram app installed, because it falls back to a web page. The tg://resolve?domain=yourbot&start=abc123 form only works if a Telegram client is already installed and registered to handle that URL scheme; there’s no web fallback. Native apps and some bots prefer tg:// links because they skip the web redirect and open the app directly, but for anything you’re sharing broadly, the https://t.me/ form is the safer default because it degrades gracefully instead of just failing silently on a device without Telegram installed.

This is worth pointing out because it’s directly relevant to what we do here: MTProto proxy configuration links use the exact same URL scheme idea, just a different command. A proxy link looks like tg://proxy?server=1.2.3.4&port=443&secret=abcdef or tg://socks?server=1.2.3.4&port=1080&user=name&pass=word. Telegram parses those parameters and drops the proxy straight into the client’s proxy settings, no manual entry, no copying numbers into a settings screen field by field. It’s the same principle as a bot start payload: encode configuration into a URL, let the client parse it, skip the manual step. When we generate proxy configs for hosted setups, this is the mechanism doing the work behind the “tap to connect” link.

Common mistakes worth avoiding

A few things we see repeatedly when people build on top of deep links:

Trying to cram structured data directly into the payload instead of using a lookup token, then running into the 64 character limit or invalid character errors when the data doesn’t fit the allowed set.

Assuming the payload is only sent once, then building logic that breaks the second time a returning user clicks a link with a new payload attached, because the handler wasn’t written to expect /start more than once per user.

Forgetting that the payload is visible to anyone with the link, and putting something in it that shouldn’t be public.

None of these are exotic problems. They’re the kind of thing that shows up the first time a bot gets real traffic instead of test clicks from the developer’s own account, which is exactly when you want the basics to already be right.

If you’re setting up hosted Telegram infrastructure, proxy configuration, or want a second set of eyes on how your bot handles deep links and account safety, take a look at what we run at telegramvault.org.

need infra for this today?