Key takeaways
- A Mini App is your own web app inside Telegram, so your backend signs the offerwall link and credits rewards, as on any website.
- Trust only initData that your server validated with the bot token, never initDataUnsafe sent by the client.
- Read Telegram's Bot Platform Developer Terms yourself: we found no rule that names offerwalls, but several rules apply to how you run one.
How do you add an offerwall to a Telegram Mini App?
To add an offerwall to a Telegram Mini App, send the Mini App’s initData to your backend, validate it there with your bot token, map the Telegram user to an account of your own, and build the signed offerwall link on the server. The Mini App opens that link with Telegram.WebApp.openLink when the user taps Earn, and your backend credits rewards only when the offerwall sends a signed postback. No SDK is needed: a Mini App is a web page, so the signed link is the integration.
For Telegram Mini App monetization this is the same pattern as a rewards website, with Telegram handling sign-in. If you run a channel rather than a Mini App, How to make money with a Telegram channel covers Telegram’s own programs and a companion earn page. If you have not built the Mini App yet, start with How to make a Telegram Mini App, and to weigh rewards against selling items for Stars, read Telegram Stars vs an offerwall.
What a Mini App is, and what that means here
A Telegram Mini App is a web page that Telegram opens inside its apps, connected to your bot. It is hosted on your own server, loads Telegram’s telegram-web-app.js script, and receives launch data about the user from Telegram. Three facts from Telegram’s Mini Apps documentation shape the integration:
initDatais the raw launch data, a query string meant for validation on your server.initDataUnsafeshould not be trusted. Telegram’s docs say to use data frominitDataonly on the bot’s server, after validating it.openLinkopens a link in an external browser, and the Mini App is not closed. It can only be called in response to a user interaction, such as a tap inside the Mini App.
The offerwall itself does not change. Your backend is still the place that knows who the user is, signs the link, and owns the balance.
The flow at a glance
| Step | Where | What happens |
|---|---|---|
| 1 | Mini App | Sends Telegram.WebApp.initData to your backend |
| 2 | Your backend | Validates it with the bot token, finds or creates the user’s account |
| 3 | Your backend | Builds the signed offerwall link and returns it |
| 4 | Mini App | Opens the link with openLink when the user taps Earn |
| 5 | Moonetix to your backend | Signed postback when a result is credited or reversed |
| 6 | Mini App | Reloads the balance from your backend |
On Moonetix, any app works: use its offerwall link, which opens in any browser, with no website restriction.
Step 1: validate initData on your server
Telegram documents the check: build a data-check-string from all received fields except hash, sorted alphabetically as key=value lines joined with a line feed. The secret key is the HMAC-SHA256 of your bot token with the constant string WebAppData as the key. The hex HMAC-SHA256 of the data-check-string with that secret must equal hash. In PHP:
function telegram_user_from_init_data(string $initData, string $botToken): ?array
{
parse_str($initData, $fields);
$hash = $fields['hash'] ?? '';
unset($fields['hash']);
ksort($fields);
$lines = [];
foreach ($fields as $k => $v) {
$lines[] = $k . '=' . $v;
}
$secret = hash_hmac('sha256', $botToken, 'WebAppData', true);
$check = hash_hmac('sha256', implode("\n", $lines), $secret);
if (!is_string($hash) || !hash_equals($check, $hash)) {
return null;
}
if (time() - (int) ($fields['auth_date'] ?? 0) > 86400) {
return null;
}
$user = json_decode($fields['user'] ?? '', true);
return is_array($user) && isset($user['id']) ? $user : null;
}
- The bot token stays on the server. It is a credential, and Telegram’s developer terms prohibit making such credentials public.
- Check
auth_date. Telegram suggests it to reject outdated data. A day is used above; pick what fits your app. - Never accept a user ID the client sends separately. Only the
userobject inside validatedinitDatacounts.
Step 2: map the user and sign the offerwall link
Look up the account that belongs to the Telegram user ID, or create one on first launch, and use your own account ID as the offerwall user ID. It never changes, it carries no personal data, and if the same person also uses your website, both can share it. Then build the link exactly as on any website:
$tgUser = telegram_user_from_init_data($_POST['initData'] ?? '', TELEGRAM_BOT_TOKEN);
if ($tgUser === null) {
http_response_code(403);
exit;
}
$account = find_or_create_account_for_telegram((int) $tgUser['id']);
$userId = (string) $account->id;
$hash = hash_hmac('sha256', $userId, $linkHashSalt);
$url = 'https://wall.moonetix.com/' . $appId . '?user_id=' . rawurlencode($userId) . '&hash=' . $hash;
header('Content-Type: application/json');
echo json_encode(['url' => $url]);
find_or_create_account_for_telegram() stands for your own user storage, and $linkHashSalt and $appId come from your server configuration. The hash is an HMAC-SHA256 of the user ID with your link hash salt, which also stays on the server. The Offerwall link docs have the same code in Node.js and Python.
Step 3: open the offerwall from the Mini App
Fetch the link when the earn screen loads, and open it in the tap handler, because openLink only works in response to a user interaction:
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<button id="earn" disabled>Earn coins</button>
<script>
const tg = window.Telegram.WebApp;
let wallLink = null;
fetch('/api/offerwall-link', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ initData: tg.initData }),
})
.then((r) => r.json())
.then((d) => {
wallLink = d.url;
document.getElementById('earn').disabled = false;
});
document.getElementById('earn').addEventListener('click', () => {
if (wallLink) {
tg.openLink(wallLink);
}
});
</script>
The offerwall then opens in the user’s browser, where offers that lead to app stores and sign-ups behave as they do on any website. The Mini App stays open in Telegram behind it.
What about showing it inside the Mini App?
An iframe of the offerwall only loads on https pages of the website registered for the app and its subdomains, and browsers apply that rule to every parent frame, not just the closest one. Telegram’s web clients run Mini Apps inside a frame of their own page, so an embedded offerwall can fail to load there even when it works in other clients. If you want it embedded, register the Mini App’s domain as the website of your Moonetix app and test every Telegram client you support. openLink avoids the question entirely, which is why we suggest it.
Step 4: credit rewards through your backend
Rewards never pass through the Mini App. When a result is credited or reversed, Moonetix calls your postback URL. Your server checks the hash, an HMAC-SHA256 of transaction ID, user ID, reward, and status joined with colons and signed with your postback secret key, then:
- adds the reward on status 1, once per transaction ID,
- takes it back on status 2, only if that transaction was credited,
- answers with a 2xx status within 6 seconds, with no redirect.
When the user is back in the Mini App, load the balance from your backend each time the earn screen is shown, and add a refresh button. If your bot is allowed to message the user, a short note after a credit is a nice touch; keep it to real events, because Telegram’s developer terms forbid spamming users with unsolicited messages. The handler rules are explained in Postback security: verify every reward call, and the concept in What is a postback URL?
Telegram’s rules to check before launch
We read Telegram’s Bot Platform Developer Terms and Terms of Service for Mini Apps for this guide (checked on November 4, 2026). We did not find a clause that names offerwalls or rewarded offers, either allowing or forbidding them, so do not read this section as approval from Telegram. Read the terms yourself, because Telegram can change them and can remove a Mini App that it decides breaks them. These parts clearly apply; the section numbers are those of the Bot Platform Developer Terms:
- Privacy policy. Every bot and Mini App must have an accessible privacy policy saying what data it stores, how, and why. Telegram’s Standard Privacy Policy applies if you set none, but if it does not describe what you do, you must provide your own in @BotFather. An offerwall means data such as IP address, device, and offer activity reaches the offerwall provider, so say so.
- Required security checks. The terms ask developers to use the documented security checks and to protect their app from malicious clients, naming arbitrary data in WebAppData as an example. Validating
initDataas in Step 1 is that check. - No misleading users. Section 5.2 forbids misrepresenting what a Mini App provides, including misleading users into actions in pursuit of an unachievable outcome, and lists examples such as MLM or ponzi schemes and social growth manipulation. Describe rewards honestly, and if some offers do not fit your audience, hide them on the Offers page of your dashboard.
- Digital goods are paid in Stars. Section 6.2 says digital goods and services sold in a Mini App must be paid with Telegram Stars. Earning through offers is not a sale, but if you also sell coins or boosts, that rule applies.
- Crypto must be TON-based. Section 7 requires Mini Apps with cryptocurrency features to use the TON blockchain only, and section 7.4 forbids directing users to external platforms where cryptoassets not based on TON are promoted or used. If you pay rewards in crypto, or an offer could fall under that rule, read section 7 and Telegram’s blockchain guidelines first.
Moonetix’s own rules apply too: end users must be at least 16 (with parental permission under 18), users on VPNs, proxies, Tor or data center networks are blocked from the wall, the offerwall opens only when the user taps, and incentivized clicking outside the offerwall is not allowed. The traffic quality rules list the rest.
Step 5: test it end to end
- Open the Mini App from a test Telegram account and check your server log: validation should pass, and a request with a changed
initDatashould get a 403. - Compare the link your server returns with Test a link for the same user ID. It is under Check a link in Integration status on the Integration tab.
- Tap Earn on Android, iOS, and desktop, and confirm the offerwall opens in the browser.
- Use Send a test postback in Integration status for your test account’s ID with status 1, and check that the balance goes up once. Opening the same postback URL again from Reports, Postbacks must not change it. A status 2 test gets its own new
test_transaction ID, which you never credited, so your handler must ignore it.
Test from a normal connection: with a VPN on, the wall shows VPN detected instead of offers. The full routine is in How to test your app before it goes live, and more checks are in the Testing and troubleshooting docs.
How to add Moonetix to your Telegram Mini App
If the code above makes sense for your Mini App, these are the steps on the Moonetix side.
- Apply as a publisher at moonetix.com. Every application is reviewed by hand, and once you are approved, apps you add are live right away.
- Add your Mini App as an app in the dashboard and set the currency name, icon, conversion rate, and wall design in the live preview.
- Copy the App ID and link hash salt from the Integration tab into your server configuration, and build the link as in Step 2.
- Save your postback URL in the Postback tab and credit users only from verified postbacks, as in Step 4.
- Run the tests in Step 5, then put the Earn button where users already look for coins. Offerwall placement has ideas.
For the full picture, with the rules that apply and an earnings estimate, see Moonetix for Telegram.
A Moonetix offerwall behind your Mini App’s Earn button
Moonetix is a pay-per-result affiliate network: your users earn your own currency by completing offers, and you earn per completed offer, with signed postbacks for every credit and reversal. See how to get approved as a publisher or compare the ways to show the wall in Offerwall integration: iframe, link or API?
Frequently asked questions
Can I use initDataUnsafe to get the Telegram user ID?
Not for anything that touches rewards. Telegram’s docs say that data should not be trusted. Send initData to your server and validate it with your bot token.
Does Telegram allow offerwalls in Mini Apps?
We found no rule in Telegram’s developer or Mini App terms that names offerwalls either way. Rules on privacy, misleading users, Stars payments, and crypto do apply, so read the current terms before launch.
Why use openLink instead of an iframe?
openLink opens the offerwall in the user’s browser, where store links and uploads behave normally. An iframe only loads on the registered website, and Telegram web clients that frame the Mini App can stop it from loading.
Can the Mini App credit the reward itself?
No. Anything in the client can be changed. Only your backend credits users, from postbacks whose hash it verified.
Which user ID should I send to the offerwall?
Your own account ID for that Telegram user. It stays the same, carries no personal data, and can be shared with your website.
Do I need an SDK for a Telegram Mini App offerwall?
No. A Mini App is a web page, so your server signs the offerwall link and the Mini App opens it. Moonetix’s SDKs are for native Android, iOS, Unity, Flutter, and React Native apps.
Can users on a VPN use the offerwall in my Mini App?
No. Users on VPNs, proxies, Tor, or data center networks see a block page instead of offers, because offers cannot track users through them.


