Skip to main content
React & React NativeUpdated Jun 2

Step 5: Callbacks & rewards

AppLixir delivers ad lifecycle information through two channels. They serve different purposes and a production integration uses both.

ChannelWhere it runsWhat it's for
Local callback (adStatusCallbackFn)In the browser/WebView, inside your pageOptimistic UI: dismiss the player, advance the game, queue a reward animation
Web callbackServer-to-server, AppLixir → your backendSource of truth for granting persistent rewards (currency, items, XP)

Rule of thumb: the local callback drives what the player sees; the web callback drives what the player gets.

Local callback

You wire it into adStatusCallbackFn when you open the player. It receives a status object whose type is one of:

{ type: "allAdsCompleted" | "click" | "complete" | "firstQuartile" | "loaded"
| "midpoint" | "paused" | "started" | "thirdQuartile" | "skipped"
| "manuallyEnded" | "consentDeclined" }

Event types

  • loaded — ad data is available
  • started — the ad started playing
  • firstQuartile / midpoint / thirdQuartile — playhead crossed 25% / 50% / 75%
  • complete — the ad finished playing (the user watched it through)
  • allAdsCompleted — the ads manager is done, or the response returned no valid ads
  • click — the ad was clicked
  • paused — the ad is paused
  • skipped — the user skipped the ad
  • manuallyEnded — the user manually ended the ad
  • consentDeclined — the user declined consent for personalized ads

complete is optimistic, not authoritative

status.type === "complete" is the right hook to dismiss the player and show "reward incoming…" — but it is not the right hook to grant a persistent reward. The browser is untrusted: DevTools can fire callbacks, extensions can replay events, multi-tab sessions can double-fire complete for one impression. Treat the local complete as optimistic UX only.

In the useRewardedAd hook, this is exactly why showAd() resolving true means "show the optimistic reward," and the real credit comes from your server.

React Native bridge

In a WebView there is no shared JS context — the hosted page forwards events to native via window.ReactNativeWebView.postMessage and you handle them in onMessage (see Step 4). The same rule applies: the reward message is optimistic; the server grants.

Web callback (server-to-server)

This is the only channel that should grant persistent rewards. When an ad completes, AppLixir sends an HTTP GET to a URL you provide, signed according to the Mode you choose.

Configuration

  1. In client.applixir.com, open the Server-side reward webhook card.
  2. Set the URL to an HTTPS endpoint on your backend that returns 200 OK on success.
  3. Set the Secret to a long random string. Store the same secret on your backend to verify the request signature.
  4. Choose the Mode — use MD5 and TID for production.

The exact request params, the MD5 signature formula, and PHP/Node verify snippets are documented once in HTML5 Step 4 → Web callback — the server-to-server contract is identical for React.

The flow

  1. Player watches the ad → local complete (or the RN reward message) → your client shows "reward incoming…"
  2. AppLixir sends the GET to your webhook
  3. Your backend verifies the signature, dedupes on tid (MD5 and TID mode), credits the player
  4. Your client refreshes from server state and shows the granted reward

Idempotency

In MD5 and TID mode, treat the webhook as at-least-once delivery. Retries, replays, and edge restarts can deliver the same tid more than once. Dedupe on tid before crediting — a second arrival for a tid you've already processed should be a no-op 200 OK, not a duplicate grant.

Next: Updating ads.txt.