Rewarded Ads
Overview
Rewarded ads are a user-initiated ad format where users voluntarily opt-in to watch an ad in exchange for a reward. This format is commonly used for:
- Unlocking premium content (articles, videos, features)
- Granting in-game currency or items
- Providing extra lives or power-ups
- Offering access to premium features
- Unlocking WiFi access at airports or public spaces
Rewarded ads provide a positive user experience because users choose when to engage with ads and receive tangible value in return.
Note: H5 Games Rewarded Ads is an opt-in feature offered by Google Ad Manager/Ad Exchange (GAM/AdX). Publishers must be explicitly approved for this feature before it can run.
How it Works
The rewarded ad format uses Google Publisher Tag’s REWARDED out-of-page format. Fuse provides a simple API to:
- Register the ready, granted, video completed, closed, and empty callbacks
- Register and load a rewarded ad
- Receive either the ready callback (fill) or the empty callback (no fill); exactly one fires per ad request
- Make the ad visible to the user (user-initiated)
- Receive the granted callback when the reward is granted (after required viewing duration)
- Receive the video completed callback when the rewarded video finishes playing
- Receive the closed callback when the ad is closed
Important: Always register
onRewardedReady,onRewardedGranted,onRewardedVideoCompleted,onRewardedClosed, andonRewardedEmptybefore callingregisterRewarded. The ready or empty callback can fire as soon as GAM responds. If the callback is not registered yet, you will miss the event.
Important: The reward is only granted if the user watches the ad for the required duration:
- Display ads: Typically 5 seconds of viewable time
- Video ads: Completion or skip threshold (for TrueView ads)
Configuration
Admin Panel Setup
In the Fuse admin panel, create a zone with the rewarded attribute set to true. This tells Fuse to configure the zone as a rewarded ad slot.
{
"attributes": {
"rewarded": true
}
}
You can optionally configure custom reward values in Google Ad Manager:
- Reward Type: The type of reward (e.g., “coins”, “lives”, “articles”)
- Reward Amount: The quantity of the reward (e.g., 20, 5, 3)
API Reference
Register
onRewardedReady,onRewardedGranted,onRewardedVideoCompleted,onRewardedClosed, andonRewardedEmptybefore callingregisterRewarded. The APIs below are listed in the order they should be called.
fusetag.onRewardedReady(callback)
Sets a callback function to be called when the rewarded ad is ready to display. Register this before calling registerRewarded — the ready callback can fire as soon as the ad loads, and a callback registered after that point will not be invoked.
Parameters:
callback(function): A callback function that receives the zone code as a parameter
Example:
fusetag.que.push(function() {
fusetag.onRewardedReady(function(zoneCode) {
console.log('Rewarded ad is ready:', zoneCode);
// Enable your "Watch Ad" button or similar UI
});
});
fusetag.onRewardedGranted(callback)
Sets a callback function to be called when the user earns the reward. This fires after the user has watched the ad for the required duration. Register this before calling registerRewarded.
Parameters:
callback(function): A callback function that receives:zoneCode(string): The zone codepayload(object | null): Reward information with properties:type(string): The reward type (e.g., “coins”, “lives”, “article”)amount(number): The reward amount
Example:
fusetag.que.push(function() {
fusetag.onRewardedGranted(function(zoneCode, payload) {
console.log('Reward granted!', zoneCode, payload);
if (payload) {
const rewardType = payload.type; // e.g., "coins"
const rewardAmount = payload.amount; // e.g., 20
// Grant the reward to the user
grantReward(rewardType, rewardAmount);
}
});
});
fusetag.onRewardedVideoCompleted(callback)
Sets a callback function to be called when the rewarded video creative finishes playing. Register this before calling registerRewarded.
Only one callback can be registered — a second call replaces the previous callback and fires a
console.error([REWARDED]: onRewardedVideoCompleted: replacing existing callback — only the latest registration will fire. Register a single dispatcher if you need to fan out.) so accidental double-registration is loud. The same guard applies toonRewardedReady,onRewardedGranted,onRewardedClosed,onH5InterstitialReady, andonH5InterstitialClosed.
This event is distinct from onRewardedGranted and onRewardedClosed:
onRewardedGrantedfires when the reward is earned per the creative’s own rules (which may be before the video reaches its end).onRewardedClosedfires when the overlay is dismissed (which can happen at any point during or after playback).onRewardedVideoCompletedfires only when the video playback reaches its end.
Use this callback if you need to distinguish “video played to end” from “user closed early”.
Parameters:
callback(function): A callback function that receives the zone code as a parameter
Example:
fusetag.que.push(function() {
fusetag.onRewardedVideoCompleted(function(zoneCode) {
console.log('Rewarded video finished:', zoneCode);
});
});
fusetag.onRewardedClosed(callback)
Sets a callback function to be called when the user closes the rewarded ad. Register this before calling registerRewarded.
Parameters:
callback(function): A callback function that receives the zone code as a parameter
Example:
fusetag.que.push(function() {
fusetag.onRewardedClosed(function(zoneCode) {
console.log('Rewarded ad was closed:', zoneCode);
// Update UI, re-register ad for next time if needed
});
});
fusetag.onRewardedEmpty(callback)
Sets a callback function to be called when GAM returns no ad for the registered slot. onRewardedReady and onRewardedEmpty are mutually exclusive per ad request: exactly one fires. Register this before calling registerRewarded.
Only one callback can be registered — a second call replaces the previous callback and fires a
console.error([REWARDED]: onRewardedEmpty: replacing existing callback — only the latest registration will fire. Register a single dispatcher if you need to fan out.) so accidental double-registration is loud. The same guard applies to everyonRewarded*andonH5Interstitial*setter.
Parameters:
callback(function): A callback function that receives the zone code as a parameter
Example:
fusetag.que.push(function() {
fusetag.onRewardedEmpty(function(zoneCode) {
console.log('No rewarded ad fill for:', zoneCode);
// Hide the "Watch Ad" button or offer an alternative reward path
});
});
fusetag.registerRewarded(zoneCode)
Registers and loads a rewarded ad for the specified zone code. Call this after registering the ready, granted, closed, and empty callbacks.
Parameters:
zoneCode(string): The zone code configured in the Fuse admin panel
Example:
fusetag.que.push(function() {
fusetag.registerRewarded('my_rewarded_zone');
});
fusetag.displayRewarded()
Makes the rewarded ad visible to the user. This should only be called after the ad is ready and in response to a user action (e.g., clicking a “Watch Ad” button).
Parameters:
- None
Example:
fusetag.que.push(function() {
fusetag.displayRewarded();
});
Implementation Example
Here’s a complete example of implementing rewarded ads for a paywall scenario:
<!DOCTYPE html>
<html>
<head>
<title>Premium Content</title>
<script async src="https://cdn.fuseplatform.net/publift/tags/2/YOUR_ACCOUNT/fuse.js"></script>
</head>
<body>
<div id="content-container">
<h1>Premium Articles</h1>
<p>Articles remaining: <span id="articles-remaining">3</span></p>
<button id="read-article" onclick="readArticle()">Read Article</button>
<button id="watch-ad" onclick="watchAd()" style="display: none;">
Watch Ad to Unlock More Articles
</button>
</div>
<script>
const fusetag = window.fusetag || (window.fusetag = { que: [] });
let articlesRemaining = 3;
let adReady = false;
fusetag.que.push(function() {
// Set up callbacks
fusetag.onRewardedReady(function(zoneCode) {
console.log('Rewarded ad is ready:', zoneCode);
adReady = true;
});
fusetag.onRewardedGranted(function(zoneCode, payload) {
console.log('Reward granted!', zoneCode, payload);
// Grant the reward
if (payload) {
articlesRemaining += payload.amount;
updateDisplay();
alert(`You earned ${payload.amount} ${payload.type}(s)!`);
}
});
fusetag.onRewardedVideoCompleted(function(zoneCode) {
console.log('Rewarded video finished:', zoneCode);
});
fusetag.onRewardedClosed(function(zoneCode) {
console.log('Rewarded ad closed:', zoneCode);
// Re-register for next time
adReady = false;
fusetag.que.push(function() {
fusetag.registerRewarded('test_rewarded');
});
});
fusetag.onRewardedEmpty(function(zoneCode) {
console.log('No fill for:', zoneCode);
// Hide the "Watch Ad" button until the next registration attempt
document.getElementById('watch-ad').style.display = 'none';
});
// Register the rewarded ad
fusetag.registerRewarded('test_rewarded');
});
function readArticle() {
if (articlesRemaining <= 0) {
document.getElementById('read-article').disabled = true;
document.getElementById('watch-ad').style.display = 'inline-block';
alert('No articles remaining! Watch an ad to unlock more.');
return;
}
articlesRemaining--;
updateDisplay();
// Show article content...
}
function watchAd() {
if (!adReady) {
alert('Ad is still loading...');
return;
}
fusetag.que.push(function() {
fusetag.displayRewarded();
});
}
function updateDisplay() {
document.getElementById('articles-remaining').textContent = articlesRemaining;
if (articlesRemaining > 0) {
document.getElementById('read-article').disabled = false;
document.getElementById('watch-ad').style.display = 'none';
}
}
</script>
</body>
</html>
Live Example
Best Practices
-
User Choice: Always make rewarded ads opt-in. Users should explicitly choose to watch an ad for a reward.
-
Clear Value Proposition: Clearly communicate what reward the user will receive before they watch the ad.
-
Timing: Offer rewarded ads at logical points where users might want the reward (e.g., when they run out of articles, coins, or lives).
-
Reward Delivery: Grant the reward immediately after the
onRewardedGrantedcallback fires. Don’t delay or require additional actions. -
Re-registration: After a rewarded ad is closed, re-register the ad if you want to allow users to watch another ad later.
-
Error Handling: Always check if the ad is ready before calling
displayRewarded(). Handle cases where ads don’t load. -
User Experience:
- Don’t force users to watch ads
- Provide alternative ways to earn rewards (e.g., purchases)
- Respect user choices if they close the ad before completion
-
Mobile Optimization: Ensure your site has proper viewport settings:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Requirements and Restrictions
- User Opt-in: Users must voluntarily choose to watch the ad
- Mobile Optimized Pages Required: Rewarded ads work on mobile optimized pages
- Sound: Some rewarded ads may play with sound on. Users are prompted to continue or cancel
- Confirmation Prompts: If users try to close the ad early, they’re prompted to confirm forfeiture
- Minimum View Time: Display ads require 5 seconds of viewable time for reward grant
- Completion: Video ads must complete, or reach skip threshold for TrueView ads
- Ad Persistence: Rewarded ads persist after completion and must be closed by the user
Event Flow
- Callback setup: Register
onRewardedReady,onRewardedGranted,onRewardedVideoCompleted,onRewardedClosed, andonRewardedEmpty. This must happen before step 2 — see Important. - Registration: Call
registerRewarded(zoneCode)to load the ad - Ad Response: GAM responds. Exactly one of:
onRewardedReadycallback fires when the ad is available (fill), oronRewardedEmptycallback fires when no ad is returned (no fill)
- User Action: User clicks “Watch Ad” or similar button
- Display: Call
displayRewarded()to show the ad - Viewing: User watches the ad
- Reward Grant:
onRewardedGrantedcallback fires after required view time with reward details - Video Completion: For video creatives,
onRewardedVideoCompletedfires when the video finishes playing - Closure: User closes the ad,
onRewardedClosedcallback fires
Troubleshooting
Ad doesn’t display:
- Verify the zone is configured with
rewarded: truein the admin panel - Check that
registerRewardedwas called before attempting to display - Ensure the zone code matches the configuration
- Verify ad is ready before calling
displayRewarded() - Check browser console for errors
Reward not granted:
- User may have closed the ad before the required viewing time
- Check that
onRewardedGrantedcallback is registered before callingregisterRewarded - Verify the ad had sufficient viewability (must be in view)
- For display ads, ensure at least 5 seconds of viewable time
Callback not firing:
- Ensure all callbacks (
onRewardedReady,onRewardedGranted,onRewardedVideoCompleted,onRewardedClosed,onRewardedEmpty) are registered before callingregisterRewarded. If you register the callback after, the ready or empty event may have already fired and your callback will never be invoked. - Verify all code is wrapped in
fusetag.que.push() - Check for JavaScript errors that might prevent callback execution
Ad never appears and ready callback never fires:
- GAM may have returned no fill for this request. Register
onRewardedEmptyto detect this case rather than waiting ononRewardedReadyindefinitely.
Multiple rewards granted:
- Ensure you’re not calling
displayRewarded()multiple times - Check that you’re properly tracking reward state
- Consider disabling the “Watch Ad” button after clicking
Server-Side Verification
Note: Server-side verification (SSV) is an app-only feature and is not available for web. For web implementations:
- Implement client-side reward validation
- Use fraud detection mechanisms
- Monitor for suspicious patterns
- Consider implementing rate limiting
- Track reward grants server-side for auditing
Reporting and Analytics
You can track rewarded ad performance using Google Ad Manager reports:
- Platform dimension: Shows breakdown by platform
- Interaction type dimension: Identifies rewarded requests vs other types
- Creative technology dimension: Shows video vs display breakdown
Policy Compliance
Publishers using rewarded inventory must comply with Google Ad Manager policies for ad units that offer rewards.