Offerwall for Telegram Mini Apps: A Second Revenue Stream on Top of Rewarded Video
An offerwall is a screen inside your app with a list of tasks from advertisers: subscribe to a channel, open another mini app, sign up for a service. The user completes a task and gets an in-app reward; the advertiser pays for the completed action, and you earn from it. It is the second revenue stream most Telegram Mini Apps leave on the table after they add rewarded video. GigaPub is an ad network and ad-mediation platform for Telegram Mini Apps, and its OfferWall SDK adds this stream with one loader script and one event handler.
Key takeaways
- An offerwall pays per completed action, not per view — its revenue does not depend on video fill rate or on eCPM.
- It does not compete with rewarded video: different moment, different user intent, no ad fatigue.
- The integration is three steps: load the SDK, handle the
rewardClaimevent, open the wall from a button. Show the button only whenhasOffers()is true. - Never lose a reward: call
pending()on launch and credit each reward id exactly once.
What is an offerwall and how are you paid?
An offerwall is a catalogue of advertiser tasks shown inside your app, each with a reward in your own currency. The user picks a task, completes it, and the reward is credited by your app. You are paid for the completed action — a subscription, an install, a registration — not for showing the list and not for a view.
That changes the economics compared to video ads. A rewarded video earns a small, fixed amount per view and depends on there being an ad to show; an offerwall task earns when the user does something an advertiser values, regardless of how the video demand for your countries looks that day.
Why doesn't an offerwall compete with rewarded video?
Because it serves a different moment. Rewarded video is the "I need one more life right now" button: a 15–30 second commitment for a small reward. An offerwall is the "I want a lot of coins" screen: the user chooses to spend a few minutes on a task in exchange for a reward worth many video views.
Three consequences follow:
- No ad fatigue. Nothing is shown unless the user opens the wall. Frequency caps and retention worries that apply to interstitials do not apply here — the format is opt-in twice over, first for the wall, then for the task.
- Different users. Some users never watch ads but will happily subscribe to a channel for a large bonus. The offerwall is often the only revenue you get from them.
- Independent of fill. If video demand for a country is thin, the wall still has tasks. The two streams fail independently, which is what you want from a second stream.
Where rewarded video and interstitial fit in the same app is covered in rewarded video vs interstitial ads.
Rewarded video vs offerwall
| Rewarded video | Offerwall | |
|---|---|---|
| What is paid for | an ad impression | a completed action |
| Who initiates | the user taps "watch an ad" | the user opens the task list |
| Typical moment | needs a small bonus right now | wants a lot of currency at once |
| Reward size | small, fixed per view | larger, varies per task |
| Depends on video fill rate | yes | no |
| Developer work | one showGiga() call | loader, rewardClaim handler, open() |
Where should the offerwall button go?
Next to the balance, in the shop, or wherever the user looks when they run out of currency: a "Free coins" or "Earn" button. Two rules make it work:
- Show the button only when there is something behind it.
sdk.hasOffers()tells you whether the current user has tasks available; an empty wall is worse than no button. - Do not bury it. A wall reachable in one tap from the place where the user feels the shortage earns; a wall five taps deep in settings does not.
How do you integrate the GigaPub OfferWall SDK?
Three steps. Register an offerwall project in the publisher dashboard to get its project id, then:
1. Add the loader. It is served by the backend and picks the optimal CDN for the user's region.
<script src="https://wall.giga.pub/api/v1/loader.js?projectId=YOUR_PROJECT_ID"></script>
2. Initialise the SDK and handle reward claims. The callback queue makes the code safe even when the script loads asynchronously.
(window.loadGigaSDKCallbacks || (window.loadGigaSDKCallbacks = [])).push(() => {
window.loadOfferWallSDK({ projectId: 'YOUR_PROJECT_ID' })
.then((sdk) => {
window.gigaOfferWallSDK = sdk
sdk.on('rewardClaim', async (data) => {
// data: { rewardId, userId, projectId, hash, amount, description }
await creditUserReward(data.userId, data.amount) // your system
sdk.confirmReward(data.rewardId, data.hash)
})
})
.catch((error) => console.error('OfferWall SDK failed to load', error))
})
3. Open the wall from a button.
document.getElementById('rewards-button').addEventListener('click', () => {
if (window.gigaOfferWallSDK?.hasOffers())
window.gigaOfferWallSDK.open()
})
That is the whole integration. If you would rather render tasks in your own UI,
sdk.getOffers() returns the available and active tasks with title, icon and reward amount,
and sdk.openOffer(offerId) opens a specific one. The full reference, including React and
TypeScript variants, is in the
OfferWall quick integration guide.
How do you make sure no reward is lost — or faked?
A task is often completed outside your app — in another mini app, in a channel — so the user may come back after the session that opened the wall is gone. Three habits keep the rewards flowing correctly:
- Pick up pending rewards on launch.
await sdk.pending()returns the rewards that were claimed but not yet confirmed; credit them and callconfirmRewardfor each. A "you have unclaimed rewards" notice on return is a nice touch. - Credit each reward id exactly once. Store the reward ids you have processed and treat
a repeated
rewardClaimfor the same id as already done. Confirmation calls are safe to repeat; double-crediting is not. - Enable advanced mode if the stakes are high. By default the flow above works as-is. For projects with stricter requirements, server-side hash validation can be enabled: your backend verifies the incoming reward hash and generates a confirmation hash with the project's secret key before the reward is confirmed. See server-side hash validation.
The SDK also emits opened and closed events — refresh the user's balance on closed,
so a reward credited while the wall was open is visible the moment the user returns to the
game.
FAQ
What is an offerwall in a Telegram Mini App? A screen inside your app with a list of tasks from advertisers — subscribe to a channel, open another mini app, sign up for a service. The user completes a task and receives an in-app reward; the advertiser pays for the completed action, and you earn from it. Nobody is paid for views.
Does an offerwall replace rewarded video? No, it complements it. Rewarded video serves the moment when a user needs a small bonus right now; an offerwall serves the moment when a user wants a lot of in-app currency and is willing to spend a few minutes on a task. The two formats do not cannibalise each other.
How does the user receive the reward from an offerwall?
When a task is completed, the SDK emits a rewardClaim event with the user id, the amount
and a reward id. Your app credits the user and calls sdk.confirmReward(rewardId, hash).
Rewards claimed while the app was closed are picked up on the next launch with
sdk.pending().
Can I show offers in my own UI instead of the built-in offerwall?
Yes. sdk.getOffers() returns the available and active tasks, and sdk.openOffer(offerId)
opens a specific task directly. The built-in wall with sdk.open() is the zero-design option.
How do I make sure offerwall rewards cannot be faked? Credit each reward id once on your side, and for stricter projects enable advanced mode: your backend verifies the reward hash and generates a confirmation hash with your project's secret key before the reward is confirmed.
Rewarded video, interstitial and the offerwall all live in the same publisher dashboard, and the video formats go through the same ad mediation layer. Add the second stream at giga.pub.
