Watch And Earn docs
v3.1.0
Live demoConsole demo Get help
● Monetise · Server-to-server postbacks

Offerwalls and surveys
AdGem, Lootably, BitLabs, CPX.

Users open a provider's web offerwall or survey wall inside the app. The provider pays you for completed offers and surveys. Its server sends a signed postback to your Cloud Function, which then credits points. Every provider is off until you turn it on.

AdGemLootablyBitLabsCPX ResearchOff by default

01How it works

  1. You sign up with the providerYou create an app or placement in its publisher dashboard. The provider reviews it and pays you under its own terms.
  2. You fill in the consoleConsole → Offerwalls and surveys: the provider's public id, the web wall URL with {uid} where the user id goes, and the postback secret.
  3. The app opens the wallA card appears for each enabled provider (under Earn). It opens your URL in the in-app browser with {uid} replaced by the user's Firebase uid (app/lib/ui/screens/offerwall_screen.dart). BitLabs and CPX are shown as surveys.
  4. The provider calls your postbackWhen a user completes an offer or a survey, the provider's server calls owAdgem, owLootably, owBitlabs or owCpx.
  5. The function verifies and credits onceIt checks the signature (a bad one answers 403), ignores duplicates (owConversions), refuses unknown and banned users, then credits the points, or holds them for review. It also writes a revenue event with the payout the provider reported (firebase/functions/src/offerwall/).
The user id must be the Firebase uid

Points go to the user named in the postback. The wall must therefore be opened with the Firebase uid, which is why the URL needs {uid}. The console refuses to enable a provider until the id, an https URL containing {uid} and the secret are all set. The Status page says what is missing.

02What goes where

From OFFERWALL_ID / OFFERWALL_SECRET in admin/src/manifest.ts and firebase/README.md §4. Secrets are write-only, owner-only and stored encrypted. The console writes them to config/private.offerwalls, which only Cloud Functions can read.

ProviderConsole: public idConsole: secretWhere you find the secret
AdGem (offerwall)AdGem app idAdGem postback keyAdGem dashboard → your property → Postback → generate the Postback Key
Lootably (offerwall)Lootably placement idLootably postback secretLootably dashboard → placement → Postback secret
BitLabs (surveys)BitLabs app tokenBitLabs app secretBitLabs dashboard → your app → App Secret
CPX Research (surveys)CPX Research app idCPX Research secure hashCPX Research publisher dashboard → your app → secure hash

For each provider the console also has show in the app (off by default), title in the app and web offerwall URL. Copy the web wall link from the provider's dashboard and put {uid} where the user id goes. The URL must start with https://. The exact URL format is the provider's. These docs do not reproduce it.

AdGem and BitLabs have one more owner-only field, <Provider>: callback URL as pasted in its dashboard (optional). It is explained under Postback URLs.

The Features → Offerwalls and surveys tab switch hides all walls at once.

03Postback URLs to paste

Replace REGION-PROJECT with your region and project id, for example us-central1-my-project. With Backend → Cloud Functions base URL set, the console's Status page shows the exact function URL. Paste each URL as one line in the provider's postback (callback) setting. The verification column is copied from firebase/README.md §11, where each scheme was checked against the provider's public documentation on 2026-10-05.

AdGem

https://REGION-PROJECT.cloudfunctions.net/owAdgem?player_id={player_id}&transaction_id={transaction_id}&amount={amount}&payout={payout}&campaign_id={campaign_id}&goal_id={goal_id}&offer_name={offer_name}

Or the v3 JSON postback to https://REGION-PROJECT.cloudfunctions.net/owAdgem. Confirmed v2: AdGem appends request_id and verifier = HMAC-SHA256(Postback Key, the full URL without &verifier=…). v3: header Signature = HMAC-SHA256(key, raw body). The function answers OK. Docs: postbacks v2, postbacks v3.

AdGem: one open point

AdGem's page does not say whether the scheme and host are part of the signed URL. The function checks the URL as it arrives at Cloud Functions. Send one test postback from the AdGem dashboard and confirm it is credited, not answered with 403.

Lootably

https://REGION-PROJECT.cloudfunctions.net/owLootably?userID={userID}&transactionID={transactionID}&ip={ip}&revenue={revenue}&currencyReward={currencyReward}&offerID={offerID}&offerName={offerName}&status={status}&hash={hash}

Confirmed hash = sha256(userID + ip + revenue + currencyReward + secret). The response body is 1. status 0 = chargeback; it is handled, although Lootably's page says only 1 is sent today. Docs: Lootably postbacks.

BitLabs

https://REGION-PROJECT.cloudfunctions.net/owBitlabs?uid=[%USER:UID%]&tx=[%TX%]&val=[%VALUE:CURRENCY%]&usd=[%VALUE:USD%]&type=[%ACTIVITY:TYPE%]&ref=[%REF%]

Confirmed BitLabs appends &hash= = HMAC-SHA1(App Secret, the URL as received up to &hash=). A RECONCILIATION reverses the conversion named by ref. BitLabs does not document the sign of its value, so the function debits what was credited. Docs: callbacks, securing callbacks through hashing. BitLabs publishes its callback IPs on that page if you want to allow-list them.

CPX Research

https://REGION-PROJECT.cloudfunctions.net/owCpx?status={status}&trans_id={trans_id}&user_id={user_id}&amount_local={amount_local}&amount_usd={amount_usd}&offer_id={offer_ID}&hash={secure_hash}
CPX Research: best effort, not confirmed

CPX shows its postback specification only inside the publisher dashboard, and no public page confirms it. What is implemented: hash = md5(trans_id + "-" + secure hash), status 1 = completed, 2 = reversed. Compare this with your CPX dashboard and send one test postback before you go live. If the parameter names differ, the function answers 403 or 400 and credits nothing. The public page cpx-research.com/main/en/doc.php covers only the wall hash.

AdGem and BitLabs behind a proxy or custom domain

Both sign the full URL. With the plain cloudfunctions.net URL above you need nothing more: the function checks the URL as received. If your postbacks go through a proxy or a custom domain, paste the exact postback URL you entered in the provider's dashboard into console → Offerwalls and surveys → AdGem: callback URL as pasted in its dashboard (optional) or BitLabs surveys: callback URL as pasted in its dashboard (optional). Owner only. On every save the console writes it to config/private.offerwalls.<provider>.callbackUrl, never to the app. It must start with https://; the query string (everything from ?) is dropped, and a value that is not an https URL is ignored and listed on the Status page. The function then also checks the signature against that URL plus the query it received (firebase/functions/src/index.ts).

04Payout and points

From offerwallPoints() in firebase/functions/src/features.ts:

  1. The provider's own amount firstWhen the postback carries a points amount (AdGem amount, Lootably currencyReward, BitLabs val, CPX amount_local), that whole number is credited. Set the provider's currency conversion in its dashboard to match your points.
  2. Otherwise the USD payoutWhen no amount is sent: payout in USD × Points per 1 USD of provider payout (economy.offerwallPointsPerUsd, default 1000, 0 to 100000), rounded down.
  3. Always cappedNever more than Most points one conversion can credit (economy.offerwallMaxPointsPerConversion, default 50000).

Credits use ledger type offerwall (AdGem, Lootably) or survey (BitLabs, CPX). They count as earned points (level, leaderboards, season XP). The VIP or booster multiplier is not applied to them. docs/CONTRACT-3.1.md §10.3 limits it to rewarded-ad, rewarded-interstitial, spin, scratch, quiz, task, check-in and mini-game credits.

05Holds and the review queue

A conversion is held instead of credited when:

  • its points are above Anti-fraud → Hold offerwall conversions above (points) (fraud.offerwallHoldOverPoints, default 5000), or
  • the user is flagged, for example by the device limit or the velocity rule (Anti-fraud).
0 = holding is off

With the hold amount at 0, holding by size is off and every conversion is credited at once. Conversions of flagged users are always held, whatever this setting (firebase/functions/src/offerwall/core.ts). To hold every conversion, set it to 1.

A held conversion appears in console → Review queue with kind offerwall_hold. Approve it and the Functions trigger onReviewDecision credits it exactly once, usually within a minute. Reject it and nothing is credited; you can tick "ban on reject". See Review queue workflow.

06Reversals and chargebacks

ProviderReversal signal
AdGemNone. AdGem documents postbacks only for payable conversions.
Lootablystatus=0 (handled, though Lootably says it sends only 1 today)
BitLabstype=RECONCILIATION with ref = the original transaction
CPX Researchstatus=2 (best effort, see above)

A reversal debits the points the original conversion credited, never below 0. If the user has already spent them, the function takes what is there and opens a fraud flag (offerwall_reversal_shortfall) for the rest. It also writes a negative revenue event. A reversal of a conversion the backend never saw is recorded as reversal_unknown.

07Revenue from offerwalls

Every signed conversion writes revenueEvents/{offerwall|survey}_{provider}_{txid} with the provider's USD payout in cents. Reversals are negative. When the provider sends no payout, the amount is empty with the note "payout not reported" and the console does not count it. These events feed the revenue centre. They record what the provider reported. What the provider finally pays you is settled in its own dashboard.

08Responses and testing

StatusMeaning
403 not configuredThe provider's secret is empty in config/private.
403 bad signatureThe secret or the URL does not match.
400 bad requestThe user id or transaction id is missing or malformed.
200 (OK, or 1 for Lootably)Accepted: credited, held, duplicate, unknown user, banned, zero or reversed. The provider stops retrying.
  • Send a test postback from each provider's dashboard with the uid of your own test account
  • Console → Ledger shows an offerwall or survey row, or the Review queue shows a hold
  • Firebase console → Functions → logs show offerwall conversion with the outcome

09Before you turn one on

  • Points from offerwalls and surveys stay points. They never become cash, gift cards or crypto inside this app.
  • Read the provider's publisher terms. Some limit incentivised installs or require disclosures in your app.
  • Your privacy policy must name the provider, because the wall receives the user id and runs the provider's own pages.