[{"data":1,"prerenderedAt":737},["ShallowReactive",2],{"blog-/en/blog/offerwall-for-telegram-mini-apps":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"author":11,"cover":12,"ogImage":12,"tags":13,"draft":6,"faq":18,"body":34,"_type":731,"_id":732,"_source":733,"_file":734,"_stem":735,"_extension":736},"/en/blog/offerwall-for-telegram-mini-apps","blog",false,"","Offerwall for Telegram Mini Apps: A Second Revenue Stream on Top of Rewarded Video","An offerwall pays per completed task, not per view: a second revenue stream next to rewarded video in a Telegram Mini App. How it works and how to add it.","2026-09-22","GigaPub Team","/og-image.png",[14,15,16,17],"telegram-mini-apps","offerwall","monetization","sdk",[19,22,25,28,31],{"q":20,"a":21},"What is an offerwall in a Telegram Mini App?","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 receives an in-app reward; the advertiser pays for the completed action, and you earn from it. Unlike video ads, nobody is paid for views.",{"q":23,"a":24},"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 large amount of in-app currency and is willing to spend a few minutes on a task. The two formats reach different users at different moments and do not cannibalise each other.",{"q":26,"a":27},"How does the user receive the reward from an offerwall?","When a task is completed, the GigaPub OfferWall 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 that were claimed while the app was closed can be picked up on the next launch with sdk.pending().",{"q":29,"a":30},"Can I show offers in my own UI instead of the built-in offerwall?","Yes. sdk.getOffers() returns the available and active tasks with title, description, icon and reward amount, and sdk.openOffer(offerId) opens a specific task directly. The built-in iframe with sdk.open() is the zero-design option.",{"q":32,"a":33},"How do I make sure offerwall rewards cannot be faked?","Credit each reward id once (idempotently) 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.",{"type":35,"children":36,"toc":721},"root",[37,45,51,60,110,117,122,127,133,138,143,176,190,196,356,362,367,398,404,409,419,431,441,452,460,469,499,505,510,573,601,607,616,625,656,687,696,700],{"type":38,"tag":39,"props":40,"children":42},"element","h1",{"id":41},"offerwall-for-telegram-mini-apps-a-second-revenue-stream-on-top-of-rewarded-video",[43],{"type":44,"value":8},"text",{"type":38,"tag":46,"props":47,"children":48},"p",{},[49],{"type":44,"value":50},"An offerwall is a screen inside your app with a list of tasks from advertisers: subscribe to\na channel, open another mini app, sign up for a service. The user completes a task and gets\nan in-app reward; the advertiser pays for the completed action, and you earn from it. It is\nthe second revenue stream most Telegram Mini Apps leave on the table after they add rewarded\nvideo. GigaPub is an ad network and ad-mediation platform for Telegram Mini Apps, and its\nOfferWall SDK adds this stream with one loader script and one event handler.",{"type":38,"tag":46,"props":52,"children":53},{},[54],{"type":38,"tag":55,"props":56,"children":57},"strong",{},[58],{"type":44,"value":59},"Key takeaways",{"type":38,"tag":61,"props":62,"children":63},"ul",{},[64,70,75,97],{"type":38,"tag":65,"props":66,"children":67},"li",{},[68],{"type":44,"value":69},"An offerwall pays per completed action, not per view — its revenue does not depend on\nvideo fill rate or on eCPM.",{"type":38,"tag":65,"props":71,"children":72},{},[73],{"type":44,"value":74},"It does not compete with rewarded video: different moment, different user intent, no ad\nfatigue.",{"type":38,"tag":65,"props":76,"children":77},{},[78,80,87,89,95],{"type":44,"value":79},"The integration is three steps: load the SDK, handle the ",{"type":38,"tag":81,"props":82,"children":84},"code",{"className":83},[],[85],{"type":44,"value":86},"rewardClaim",{"type":44,"value":88}," event, open the wall\nfrom a button. Show the button only when ",{"type":38,"tag":81,"props":90,"children":92},{"className":91},[],[93],{"type":44,"value":94},"hasOffers()",{"type":44,"value":96}," is true.",{"type":38,"tag":65,"props":98,"children":99},{},[100,102,108],{"type":44,"value":101},"Never lose a reward: call ",{"type":38,"tag":81,"props":103,"children":105},{"className":104},[],[106],{"type":44,"value":107},"pending()",{"type":44,"value":109}," on launch and credit each reward id exactly once.",{"type":38,"tag":111,"props":112,"children":114},"h2",{"id":113},"what-is-an-offerwall-and-how-are-you-paid",[115],{"type":44,"value":116},"What is an offerwall and how are you paid?",{"type":38,"tag":46,"props":118,"children":119},{},[120],{"type":44,"value":121},"An offerwall is a catalogue of advertiser tasks shown inside your app, each with a reward in\nyour own currency. The user picks a task, completes it, and the reward is credited by your\napp. You are paid for the completed action — a subscription, an install, a registration —\nnot for showing the list and not for a view.",{"type":38,"tag":46,"props":123,"children":124},{},[125],{"type":44,"value":126},"That changes the economics compared to video ads. A rewarded video earns a small, fixed\namount per view and depends on there being an ad to show; an offerwall task earns when the user\ndoes something an advertiser values, regardless of how the video demand for your countries\nlooks that day.",{"type":38,"tag":111,"props":128,"children":130},{"id":129},"why-doesnt-an-offerwall-compete-with-rewarded-video",[131],{"type":44,"value":132},"Why doesn't an offerwall compete with rewarded video?",{"type":38,"tag":46,"props":134,"children":135},{},[136],{"type":44,"value":137},"Because it serves a different moment. Rewarded video is the \"I need one more life right now\"\nbutton: a 15–30 second commitment for a small reward. An offerwall is the \"I want a lot of\ncoins\" screen: the user chooses to spend a few minutes on a task in exchange for a reward\nworth many video views.",{"type":38,"tag":46,"props":139,"children":140},{},[141],{"type":44,"value":142},"Three consequences follow:",{"type":38,"tag":61,"props":144,"children":145},{},[146,156,166],{"type":38,"tag":65,"props":147,"children":148},{},[149,154],{"type":38,"tag":55,"props":150,"children":151},{},[152],{"type":44,"value":153},"No ad fatigue.",{"type":44,"value":155}," Nothing is shown unless the user opens the wall. Frequency caps and\nretention worries that apply to interstitials do not apply here — the format is opt-in\ntwice over, first for the wall, then for the task.",{"type":38,"tag":65,"props":157,"children":158},{},[159,164],{"type":38,"tag":55,"props":160,"children":161},{},[162],{"type":44,"value":163},"Different users.",{"type":44,"value":165}," Some users never watch ads but will happily subscribe to a channel\nfor a large bonus. The offerwall is often the only revenue you get from them.",{"type":38,"tag":65,"props":167,"children":168},{},[169,174],{"type":38,"tag":55,"props":170,"children":171},{},[172],{"type":44,"value":173},"Independent of fill.",{"type":44,"value":175}," If video demand for a country is thin, the wall still has tasks.\nThe two streams fail independently, which is what you want from a second stream.",{"type":38,"tag":46,"props":177,"children":178},{},[179,181,188],{"type":44,"value":180},"Where rewarded video and interstitial fit in the same app is covered in\n",{"type":38,"tag":182,"props":183,"children":185},"a",{"href":184},"/blog/rewarded-vs-interstitial-ads",[186],{"type":44,"value":187},"rewarded video vs interstitial ads",{"type":44,"value":189},".",{"type":38,"tag":111,"props":191,"children":193},{"id":192},"rewarded-video-vs-offerwall",[194],{"type":44,"value":195},"Rewarded video vs offerwall",{"type":38,"tag":197,"props":198,"children":199},"table",{},[200,222],{"type":38,"tag":201,"props":202,"children":203},"thead",{},[204],{"type":38,"tag":205,"props":206,"children":207},"tr",{},[208,212,217],{"type":38,"tag":209,"props":210,"children":211},"th",{},[],{"type":38,"tag":209,"props":213,"children":214},{},[215],{"type":44,"value":216},"Rewarded video",{"type":38,"tag":209,"props":218,"children":219},{},[220],{"type":44,"value":221},"Offerwall",{"type":38,"tag":223,"props":224,"children":225},"tbody",{},[226,245,263,281,299,317],{"type":38,"tag":205,"props":227,"children":228},{},[229,235,240],{"type":38,"tag":230,"props":231,"children":232},"td",{},[233],{"type":44,"value":234},"What is paid for",{"type":38,"tag":230,"props":236,"children":237},{},[238],{"type":44,"value":239},"an ad impression",{"type":38,"tag":230,"props":241,"children":242},{},[243],{"type":44,"value":244},"a completed action",{"type":38,"tag":205,"props":246,"children":247},{},[248,253,258],{"type":38,"tag":230,"props":249,"children":250},{},[251],{"type":44,"value":252},"Who initiates",{"type":38,"tag":230,"props":254,"children":255},{},[256],{"type":44,"value":257},"the user taps \"watch an ad\"",{"type":38,"tag":230,"props":259,"children":260},{},[261],{"type":44,"value":262},"the user opens the task list",{"type":38,"tag":205,"props":264,"children":265},{},[266,271,276],{"type":38,"tag":230,"props":267,"children":268},{},[269],{"type":44,"value":270},"Typical moment",{"type":38,"tag":230,"props":272,"children":273},{},[274],{"type":44,"value":275},"needs a small bonus right now",{"type":38,"tag":230,"props":277,"children":278},{},[279],{"type":44,"value":280},"wants a lot of currency at once",{"type":38,"tag":205,"props":282,"children":283},{},[284,289,294],{"type":38,"tag":230,"props":285,"children":286},{},[287],{"type":44,"value":288},"Reward size",{"type":38,"tag":230,"props":290,"children":291},{},[292],{"type":44,"value":293},"small, fixed per view",{"type":38,"tag":230,"props":295,"children":296},{},[297],{"type":44,"value":298},"larger, varies per task",{"type":38,"tag":205,"props":300,"children":301},{},[302,307,312],{"type":38,"tag":230,"props":303,"children":304},{},[305],{"type":44,"value":306},"Depends on video fill rate",{"type":38,"tag":230,"props":308,"children":309},{},[310],{"type":44,"value":311},"yes",{"type":38,"tag":230,"props":313,"children":314},{},[315],{"type":44,"value":316},"no",{"type":38,"tag":205,"props":318,"children":319},{},[320,325,338],{"type":38,"tag":230,"props":321,"children":322},{},[323],{"type":44,"value":324},"Developer work",{"type":38,"tag":230,"props":326,"children":327},{},[328,330,336],{"type":44,"value":329},"one ",{"type":38,"tag":81,"props":331,"children":333},{"className":332},[],[334],{"type":44,"value":335},"showGiga()",{"type":44,"value":337}," call",{"type":38,"tag":230,"props":339,"children":340},{},[341,343,348,350],{"type":44,"value":342},"loader, ",{"type":38,"tag":81,"props":344,"children":346},{"className":345},[],[347],{"type":44,"value":86},{"type":44,"value":349}," handler, ",{"type":38,"tag":81,"props":351,"children":353},{"className":352},[],[354],{"type":44,"value":355},"open()",{"type":38,"tag":111,"props":357,"children":359},{"id":358},"where-should-the-offerwall-button-go",[360],{"type":44,"value":361},"Where should the offerwall button go?",{"type":38,"tag":46,"props":363,"children":364},{},[365],{"type":44,"value":366},"Next to the balance, in the shop, or wherever the user looks when they run out of currency:\na \"Free coins\" or \"Earn\" button. Two rules make it work:",{"type":38,"tag":61,"props":368,"children":369},{},[370,388],{"type":38,"tag":65,"props":371,"children":372},{},[373,378,380,386],{"type":38,"tag":55,"props":374,"children":375},{},[376],{"type":44,"value":377},"Show the button only when there is something behind it.",{"type":44,"value":379}," ",{"type":38,"tag":81,"props":381,"children":383},{"className":382},[],[384],{"type":44,"value":385},"sdk.hasOffers()",{"type":44,"value":387}," tells you\nwhether the current user has tasks available; an empty wall is worse than no button.",{"type":38,"tag":65,"props":389,"children":390},{},[391,396],{"type":38,"tag":55,"props":392,"children":393},{},[394],{"type":44,"value":395},"Do not bury it.",{"type":44,"value":397}," A wall reachable in one tap from the place where the user feels the\nshortage earns; a wall five taps deep in settings does not.",{"type":38,"tag":111,"props":399,"children":401},{"id":400},"how-do-you-integrate-the-gigapub-offerwall-sdk",[402],{"type":44,"value":403},"How do you integrate the GigaPub OfferWall SDK?",{"type":38,"tag":46,"props":405,"children":406},{},[407],{"type":44,"value":408},"Three steps. Register an offerwall project in the publisher dashboard to get its project id,\nthen:",{"type":38,"tag":46,"props":410,"children":411},{},[412,417],{"type":38,"tag":55,"props":413,"children":414},{},[415],{"type":44,"value":416},"1. Add the loader.",{"type":44,"value":418}," It is served by the backend and picks the optimal CDN for the user's\nregion.",{"type":38,"tag":420,"props":421,"children":426},"pre",{"className":422,"code":424,"language":425,"meta":7},[423],"language-html","\u003Cscript src=\"https://wall.giga.pub/api/v1/loader.js?projectId=YOUR_PROJECT_ID\">\u003C/script>\n","html",[427],{"type":38,"tag":81,"props":428,"children":429},{"__ignoreMap":7},[430],{"type":44,"value":424},{"type":38,"tag":46,"props":432,"children":433},{},[434,439],{"type":38,"tag":55,"props":435,"children":436},{},[437],{"type":44,"value":438},"2. Initialise the SDK and handle reward claims.",{"type":44,"value":440}," The callback queue makes the code safe\neven when the script loads asynchronously.",{"type":38,"tag":420,"props":442,"children":447},{"className":443,"code":445,"language":446,"meta":7},[444],"language-js","(window.loadGigaSDKCallbacks || (window.loadGigaSDKCallbacks = [])).push(() => {\n  window.loadOfferWallSDK({ projectId: 'YOUR_PROJECT_ID' })\n    .then((sdk) => {\n      window.gigaOfferWallSDK = sdk\n\n      sdk.on('rewardClaim', async (data) => {\n        // data: { rewardId, userId, projectId, hash, amount, description }\n        await creditUserReward(data.userId, data.amount) // your system\n        sdk.confirmReward(data.rewardId, data.hash)\n      })\n    })\n    .catch((error) => console.error('OfferWall SDK failed to load', error))\n})\n","js",[448],{"type":38,"tag":81,"props":449,"children":450},{"__ignoreMap":7},[451],{"type":44,"value":445},{"type":38,"tag":46,"props":453,"children":454},{},[455],{"type":38,"tag":55,"props":456,"children":457},{},[458],{"type":44,"value":459},"3. Open the wall from a button.",{"type":38,"tag":420,"props":461,"children":464},{"className":462,"code":463,"language":446,"meta":7},[444],"document.getElementById('rewards-button').addEventListener('click', () => {\n  if (window.gigaOfferWallSDK?.hasOffers())\n    window.gigaOfferWallSDK.open()\n})\n",[465],{"type":38,"tag":81,"props":466,"children":467},{"__ignoreMap":7},[468],{"type":44,"value":463},{"type":38,"tag":46,"props":470,"children":471},{},[472,474,480,482,488,490,498],{"type":44,"value":473},"That is the whole integration. If you would rather render tasks in your own UI,\n",{"type":38,"tag":81,"props":475,"children":477},{"className":476},[],[478],{"type":44,"value":479},"sdk.getOffers()",{"type":44,"value":481}," returns the available and active tasks with title, icon and reward amount,\nand ",{"type":38,"tag":81,"props":483,"children":485},{"className":484},[],[486],{"type":44,"value":487},"sdk.openOffer(offerId)",{"type":44,"value":489}," opens a specific one. The full reference, including React and\nTypeScript variants, is in the\n",{"type":38,"tag":182,"props":491,"children":495},{"href":492,"rel":493},"https://docs.giga.pub/integration-offerwall-guide.html",[494],"nofollow",[496],{"type":44,"value":497},"OfferWall quick integration guide",{"type":44,"value":189},{"type":38,"tag":111,"props":500,"children":502},{"id":501},"how-do-you-make-sure-no-reward-is-lost-or-faked",[503],{"type":44,"value":504},"How do you make sure no reward is lost — or faked?",{"type":38,"tag":46,"props":506,"children":507},{},[508],{"type":44,"value":509},"A task is often completed outside your app — in another mini app, in a channel — so the\nuser may come back after the session that opened the wall is gone. Three habits keep the\nrewards flowing correctly:",{"type":38,"tag":61,"props":511,"children":512},{},[513,538,555],{"type":38,"tag":65,"props":514,"children":515},{},[516,521,522,528,530,536],{"type":38,"tag":55,"props":517,"children":518},{},[519],{"type":44,"value":520},"Pick up pending rewards on launch.",{"type":44,"value":379},{"type":38,"tag":81,"props":523,"children":525},{"className":524},[],[526],{"type":44,"value":527},"await sdk.pending()",{"type":44,"value":529}," returns the rewards that\nwere claimed but not yet confirmed; credit them and call ",{"type":38,"tag":81,"props":531,"children":533},{"className":532},[],[534],{"type":44,"value":535},"confirmReward",{"type":44,"value":537}," for each. A\n\"you have unclaimed rewards\" notice on return is a nice touch.",{"type":38,"tag":65,"props":539,"children":540},{},[541,546,548,553],{"type":38,"tag":55,"props":542,"children":543},{},[544],{"type":44,"value":545},"Credit each reward id exactly once.",{"type":44,"value":547}," Store the reward ids you have processed and treat\na repeated ",{"type":38,"tag":81,"props":549,"children":551},{"className":550},[],[552],{"type":44,"value":86},{"type":44,"value":554}," for the same id as already done. Confirmation calls are safe to\nrepeat; double-crediting is not.",{"type":38,"tag":65,"props":556,"children":557},{},[558,563,565,572],{"type":38,"tag":55,"props":559,"children":560},{},[561],{"type":44,"value":562},"Enable advanced mode if the stakes are high.",{"type":44,"value":564}," By default the flow above works as-is.\nFor projects with stricter requirements, server-side hash validation can be enabled: your\nbackend verifies the incoming reward hash and generates a confirmation hash with the\nproject's secret key before the reward is confirmed. See\n",{"type":38,"tag":182,"props":566,"children":569},{"href":567,"rel":568},"https://docs.giga.pub/offerwall-advanced-usage.html",[494],[570],{"type":44,"value":571},"server-side hash validation",{"type":44,"value":189},{"type":38,"tag":46,"props":574,"children":575},{},[576,578,584,586,592,594,599],{"type":44,"value":577},"The SDK also emits ",{"type":38,"tag":81,"props":579,"children":581},{"className":580},[],[582],{"type":44,"value":583},"opened",{"type":44,"value":585}," and ",{"type":38,"tag":81,"props":587,"children":589},{"className":588},[],[590],{"type":44,"value":591},"closed",{"type":44,"value":593}," events — refresh the user's balance on ",{"type":38,"tag":81,"props":595,"children":597},{"className":596},[],[598],{"type":44,"value":591},{"type":44,"value":600},",\nso a reward credited while the wall was open is visible the moment the user returns to the\ngame.",{"type":38,"tag":111,"props":602,"children":604},{"id":603},"faq",[605],{"type":44,"value":606},"FAQ",{"type":38,"tag":46,"props":608,"children":609},{},[610,614],{"type":38,"tag":55,"props":611,"children":612},{},[613],{"type":44,"value":20},{"type":44,"value":615},"\nA screen inside your app with a list of tasks from advertisers — subscribe to a channel,\nopen another mini app, sign up for a service. The user completes a task and receives an\nin-app reward; the advertiser pays for the completed action, and you earn from it. Nobody is\npaid for views.",{"type":38,"tag":46,"props":617,"children":618},{},[619,623],{"type":38,"tag":55,"props":620,"children":621},{},[622],{"type":44,"value":23},{"type":44,"value":624},"\nNo, it complements it. Rewarded video serves the moment when a user needs a small bonus\nright now; an offerwall serves the moment when a user wants a lot of in-app currency and\nis willing to spend a few minutes on a task. The two formats do not cannibalise each other.",{"type":38,"tag":46,"props":626,"children":627},{},[628,632,634,639,641,647,649,655],{"type":38,"tag":55,"props":629,"children":630},{},[631],{"type":44,"value":26},{"type":44,"value":633},"\nWhen a task is completed, the SDK emits a ",{"type":38,"tag":81,"props":635,"children":637},{"className":636},[],[638],{"type":44,"value":86},{"type":44,"value":640}," event with the user id, the amount\nand a reward id. Your app credits the user and calls ",{"type":38,"tag":81,"props":642,"children":644},{"className":643},[],[645],{"type":44,"value":646},"sdk.confirmReward(rewardId, hash)",{"type":44,"value":648},".\nRewards claimed while the app was closed are picked up on the next launch with\n",{"type":38,"tag":81,"props":650,"children":652},{"className":651},[],[653],{"type":44,"value":654},"sdk.pending()",{"type":44,"value":189},{"type":38,"tag":46,"props":657,"children":658},{},[659,663,665,670,672,677,679,685],{"type":38,"tag":55,"props":660,"children":661},{},[662],{"type":44,"value":29},{"type":44,"value":664},"\nYes. ",{"type":38,"tag":81,"props":666,"children":668},{"className":667},[],[669],{"type":44,"value":479},{"type":44,"value":671}," returns the available and active tasks, and ",{"type":38,"tag":81,"props":673,"children":675},{"className":674},[],[676],{"type":44,"value":487},{"type":44,"value":678},"\nopens a specific task directly. The built-in wall with ",{"type":38,"tag":81,"props":680,"children":682},{"className":681},[],[683],{"type":44,"value":684},"sdk.open()",{"type":44,"value":686}," is the zero-design option.",{"type":38,"tag":46,"props":688,"children":689},{},[690,694],{"type":38,"tag":55,"props":691,"children":692},{},[693],{"type":44,"value":32},{"type":44,"value":695},"\nCredit each reward id once on your side, and for stricter projects enable advanced mode:\nyour backend verifies the reward hash and generates a confirmation hash with your project's\nsecret key before the reward is confirmed.",{"type":38,"tag":697,"props":698,"children":699},"hr",{},[],{"type":38,"tag":46,"props":701,"children":702},{},[703,705,711,713,720],{"type":44,"value":704},"Rewarded video, interstitial and the offerwall all live in the same publisher dashboard,\nand the video formats go through the same ",{"type":38,"tag":182,"props":706,"children":708},{"href":707},"/blog/how-ad-mediation-works",[709],{"type":44,"value":710},"ad mediation",{"type":44,"value":712}," layer. Add the second stream at ",{"type":38,"tag":182,"props":714,"children":717},{"href":715,"rel":716},"https://giga.pub",[494],[718],{"type":44,"value":719},"giga.pub",{"type":44,"value":189},{"title":7,"searchDepth":722,"depth":722,"links":723},2,[724,725,726,727,728,729,730],{"id":113,"depth":722,"text":116},{"id":129,"depth":722,"text":132},{"id":192,"depth":722,"text":195},{"id":358,"depth":722,"text":361},{"id":400,"depth":722,"text":403},{"id":501,"depth":722,"text":504},{"id":603,"depth":722,"text":606},"markdown","content:en:blog:offerwall-for-telegram-mini-apps.md","content","en/blog/offerwall-for-telegram-mini-apps.md","en/blog/offerwall-for-telegram-mini-apps","md",1790073279593]