Skip to main content

Granting Ad Rewards

Reward users for completing AdMob rewarded ads, verified server-side by RevenueCat

AIAsk AIChatGPTClaude

Grant a reward to users when they complete an AdMob rewarded or rewarded interstitial ad. RevenueCat verifies each reward server-side using AdMob Server-Side Verification (SSV) before granting it, so rewards can't be spoofed by a tampered client. You configure what each ad unit grants in the RevenueCat dashboard — no backend of your own required.

Verified rewards can grant virtual currency, a temporary entitlement, or both. An ad unit can have one virtual currency reward and multiple entitlement rewards at the same time — completing the ad grants all of them together.

⚠️Beta Feature

This feature is currently in beta.

How it works

  1. A user completes a rewarded ad.
  2. AdMob sends an SSV callback to RevenueCat, with the user attached by the SDK.
  3. RevenueCat verifies the reward and grants a reward based on your configuration.
  4. The SDK reports the verified result to your app.

Prerequisites

  • The AdMob Adapter SDK is installed and the RevenueCat SDK is configured.
  • SDK minimums: purchases-ios 5.80.3+ (iOS 15+) or purchases-android 10.12.0+. The reward verification APIs are experimental and require explicit opt-in.
  • Your AdMob account is connected to RevenueCat so your rewarded ad units sync to the dashboard.

Step 1: Configure ad units in AdMob

For each rewarded ad unit you want to grant a reward for, enable Server-side verification in the AdMob console and set the SSV callback URL to:

https://api.revenuecat.com/v1/incoming-webhooks/admob-ssv-rewarded

This is a single, shared endpoint — use the same URL for every ad unit. RevenueCat resolves the ad unit and the user from data the SDK attaches at show time. See Google's guide on setting up SSV for where to enter the URL.

The Server-side verification field on an AdMob rewarded ad unit, set to the RevenueCat SSV callback URL

Step 2: Configure the reward in the RevenueCat dashboard

On the Rewards page (under Ads) in your RevenueCat dashboard, add a rule for each reward you want an ad unit to grant. The page has a section for each reward type; an ad unit can appear in both.

Virtual currency rewards

In the Virtual currencies section, select Add currency reward:

  1. Select a synced rewarded ad unit.
  2. Choose the virtual currency to grant.
  3. Enter the amount (a positive whole number).

The Add reward dialog in the RevenueCat dashboard, with fields for the ad unit, virtual currency, and amount

RevenueCat grants this amount every time a reward from that ad unit is verified. An ad unit can have at most one virtual currency reward.

Entitlement rewards

In the Entitlements section, select Add entitlement reward:

  1. Select a synced rewarded ad unit.
  2. Choose the entitlement to grant. Only active (non-archived) entitlements are selectable — create one first if you don't see it in the list.
  3. Enter a duration and pick a unit (minutes, hours, days, weeks, months, or years). The minimum duration is 30 minutes.

The Add reward dialog in the RevenueCat dashboard, with fields for the ad unit, entitlement, and duration

RevenueCat grants the entitlement for the configured duration every time a reward from that ad unit is verified. An ad unit can have multiple entitlement rewards, but only one reward per entitlement — configure another ad unit if you need different durations for the same entitlement.

⚠️Use a real ad unit

Entitlement rewards are verified through AdMob SSV, which is configured per ad unit in your own AdMob account. Google's sample test ad unit IDs aren't part of any account, so SSV can't be enabled on them.

Step 3: Implement it in your app

Enable verification on each ad after it loads, then present it with the verification callbacks.

An ad can grant more than one reward at once. Verification exposes the primary reward and any additional rewards separately, so check both:

  • The primary reward — inspect it directly for its type.
  • Additional rewards — a list of the same reward type, holding everything beyond the primary reward. Empty when only one reward was granted.

Before delivering the result, the SDK already applies the reward locally: it invalidates the virtual currencies cache for any virtual currency reward, and refreshes customer info for any entitlement reward. You don't need to refresh anything yourself — refetch virtual currency balances only when your UI needs the updated total (see reading balances), and check the entitlement status as usual.

Rewarded ads

// Opt in to the experimental reward verification APIs.
@_spi(Experimental) import RevenueCatAdMob

RewardedAd.loadAndTrack(
withAdUnitID: "AD_UNIT_ID",
request: Request(),
placement: "bonus_coins",
fullScreenContentDelegate: self
) { ad, error in
if error != nil { return }
guard let ad else { return }

// Enable RevenueCat reward verification before presenting the ad.
ad.enableRewardVerification()
self.rewardedAd = ad
}

// Later, present the ad with verification callbacks:
rewardedAd?.present(
from: self,
rewardVerificationStarted: {
// RevenueCat is verifying the reward server-side.
// Show a loading state here if you gate the reward on the result.
},
rewardVerificationCompleted: { result in
guard let primaryReward = result.verifiedReward else {
// Verification did not succeed — for example AdMob rejected the reward,
// the callback timed out, or a network error occurred. Do not grant the
// reward client-side as a fallback; let the user retry with another ad.
return
}

// An ad can grant more than one reward; handle the primary reward and any others the same way.
for reward in [primaryReward] + result.moreRewards {
self.handleAdReward(reward)
}
}
)

func handleAdReward(_ reward: AdReward) {
if let virtualCurrency = reward.virtualCurrency {
// RevenueCat already granted the reward server-side and invalidated the local
// virtual currencies cache; the amount and currency come from the reward rule
// you configured in the dashboard.
print("Granted \(virtualCurrency.amount) \(virtualCurrency.code)")

// Refetch balances only if your UI needs the updated total.
Purchases.shared.virtualCurrencies { _, _ in
// Update your UI with the refreshed balances.
}
} else if let entitlement = reward.entitlement {
// RevenueCat already granted the entitlement server-side and refreshed customer
// info before delivering this result — check entitlement status as usual.
print("Granted entitlement \(entitlement.identifier) until \(entitlement.expiresAt)")
}
}

Rewarded interstitial ads

// Opt in to the experimental reward verification APIs.
@_spi(Experimental) import RevenueCatAdMob

RewardedInterstitialAd.loadAndTrack(
withAdUnitID: "AD_UNIT_ID",
request: Request(),
placement: "between_levels",
fullScreenContentDelegate: self
) { ad, error in
if error != nil { return }
guard let ad else { return }

// Enable RevenueCat reward verification before presenting the ad.
ad.enableRewardVerification()
self.rewardedInterstitialAd = ad
}

// Later, present the ad with verification callbacks:
rewardedInterstitialAd?.present(
from: self,
rewardVerificationStarted: {
// RevenueCat is verifying the reward server-side.
// Show a loading state here if you gate the reward on the result.
},
rewardVerificationCompleted: { result in
guard let primaryReward = result.verifiedReward else {
// Verification did not succeed — for example AdMob rejected the reward,
// the callback timed out, or a network error occurred. Do not grant the
// reward client-side as a fallback; let the user retry with another ad.
return
}

// An ad can grant more than one reward; handle the primary reward and any others the same way.
for reward in [primaryReward] + result.moreRewards {
self.handleAdReward(reward)
}
}
)

func handleAdReward(_ reward: AdReward) {
if let virtualCurrency = reward.virtualCurrency {
// RevenueCat already granted the reward server-side and invalidated the local
// virtual currencies cache; the amount and currency come from the reward rule
// you configured in the dashboard.
print("Granted \(virtualCurrency.amount) \(virtualCurrency.code)")

// Refetch balances only if your UI needs the updated total.
Purchases.shared.virtualCurrencies { _, _ in
// Update your UI with the refreshed balances.
}
} else if let entitlement = reward.entitlement {
// RevenueCat already granted the entitlement server-side and refreshed customer
// info before delivering this result — check entitlement status as usual.
print("Granted entitlement \(entitlement.identifier) until \(entitlement.expiresAt)")
}
}

Checking entitlement status

An entitlement granted from an ad reward is no different from one granted by a subscription — it's an active entitlement in CustomerInfo with an expiration date. RevenueCat doesn't notify your app the moment it expires, so apply the same rule you already follow for subscriptions: your app decides when to check status, not RevenueCat.

Check the entitlement's active status at the point where you gate access, not once when the reward is granted and never again. A user who unlocks 30 minutes of premium access can leave the relevant screen open well past that window — if you only checked at grant time, they'd keep access after it should have ended.

See Getting Subscription Status for the full set of options for checking and caching entitlement status.

Granted reward events

Rewards earned in debug builds are treated as sandbox events.

For virtual currency, confirm a verified grant in the Customer History timeline, where it appears as a virtual currency transaction:

Customer History timeline showing a granted virtual currency from an ad reward

Opening the entry shows a VIRTUAL_CURRENCY_TRANSACTION event whose "source" is "ad_reward", which distinguishes ad-reward grants from purchases and other sources. The event also includes an ad_transaction_id matching the reward verification.

For entitlements, the grant is active as soon as verification succeeds and shows up in the customer's active entitlements — there's no separate event in Customer History yet.

Was this page helpful?