Login

How to Integrate an Offerwall API (Developer Guide)

Building a custom offerwall using a CPA network's API gives you maximum control over user experience, offer curation, and revenue optimization — and it eliminates the revenue share that third-party offerwall SDKs charge. If you are a developer building a rewards site, GPT platform, or mobile app with offerwall monetization, API integration is the path to the best economics and the best user experience.

This guide is written for developers. It covers the technical architecture of offerwall API integration: fetching offers, displaying them, handling click tracking, building a postback receiver, crediting user accounts, implementing security measures, and testing the entire flow before production deployment. Code examples are provided in pseudocode that translates easily to any language or framework.

Offerwall API Architecture Overview

An offerwall API integration has four main components that work together. The architecture follows standard RESTful API patterns as described by resources like Schema.org for data structuring and the IAB Tech Lab for ad measurement standards:

1. Offer Feed API

An HTTP endpoint provided by the CPA network that returns a list of available offers. You call this API periodically (or on demand) to get offer data: titles, descriptions, payouts, tracking URLs, requirements, and targeting criteria.

2. Click Tracking

When a user clicks an offer on your offerwall, you redirect them to the network's tracking URL. This URL contains your publisher ID, the offer ID, and a sub-ID parameter carrying the user's identifier on your platform. The network records the click and redirects the user to the advertiser's page.

3. Postback Handler

A server endpoint on your infrastructure that receives HTTP requests from the CPA network when a user completes an offer. The postback carries the user ID, offer details, payout amount, and transaction ID. Your handler processes this data and credits the user.

4. User Reward System

Your application logic that translates CPA payouts into in-app rewards (points, coins, balance) and credits the appropriate user account.

Data Flow Diagram

User → Your Offerwall UI → Click → Network Tracking URL → Advertiser Page
                                                                    ↓
                                                          User completes offer
                                                                    ↓
                                                   Advertiser confirms conversion
                                                                    ↓
                                               Network fires postback to your server
                                                                    ↓
                                              Your server validates and credits user

Step 1: Set Up Your Development Environment

Obtain API Credentials

Sign up as a publisher with the CPA network and request API access. You will typically receive:

Database Schema

You need tables (or collections) for at minimum these data entities:

-- Offers table: cached offer data from the API
offers
  id (PK)
  network_offer_id (string, indexed)
  title (string)
  description (text)
  payout (decimal)
  user_reward (decimal)
  tracking_url (string)
  requirements (text)
  category (string)
  allowed_geos (string/json)
  allowed_devices (string/json)
  status (enum: active, paused)
  daily_cap (integer, nullable)
  daily_conversions (integer, default 0)
  last_synced_at (timestamp)
  created_at (timestamp)
  updated_at (timestamp)

-- Conversions table: postback records
conversions
  id (PK)
  transaction_id (string, unique, indexed)
  user_id (FK to users, indexed)
  offer_id (FK to offers)
  network_offer_id (string)
  payout (decimal)
  user_reward (decimal)
  status (enum: pending, approved, reversed)
  ip_address (string)
  raw_postback (text)
  created_at (timestamp)
  updated_at (timestamp)

-- Users table (assuming you have one)
users
  id (PK)
  email (string)
  balance (decimal, default 0)
  lifetime_earnings (decimal, default 0)
  ...

Technology Requirements

Step 2: Fetch and Cache Offers

Calling the Offer Feed API

The offer feed API typically accepts these parameters:

ParameterDescriptionExample
api_keyYour authentication tokenabc123def456
publisher_idYour publisher ID789
countryFilter offers by geo (optional)US
platformFilter by device platform (optional)mobile, desktop, all
categoryFilter by offer category (optional)finance, health
formatResponse formatjson

Example API Request (Pseudocode)

response = http.get("https://api.cpanetwork.com/v1/offers", {
  headers: { "Authorization": "Bearer YOUR_API_KEY" },
  params: {
    publisher_id: "789",
    country: "US",
    platform: "all",
    format: "json"
  }
})

offers = response.json().data  // Array of offer objects

Typical Offer Object Structure

{
  "offer_id": "1042",
  "title": "BudgetPro - Free Budget App",
  "description": "Sign up for a free BudgetPro account and link a bank account.",
  "payout": 5.00,
  "category": "finance",
  "tracking_url": "https://track.network.com/click?oid=1042&pid=789&sub1={sub1}&sub2={sub2}",
  "requirements": "User must sign up and link at least one bank account. US only.",
  "allowed_traffic": ["incent", "web", "social"],
  "allowed_geos": ["US"],
  "allowed_devices": ["mobile", "desktop"],
  "daily_cap": 500,
  "epc": 0.85,
  "conversion_rate": 0.17,
  "status": "active"
}

Caching Strategy

Do not call the offer API on every page load. Implement a caching layer:

Calculating User Rewards

When syncing offers, calculate and store the user reward amount based on your payout share percentage:

user_reward_share = 0.70  // 70% to user, 30% margin for you
for each offer in api_response:
    offer.user_reward = round(offer.payout * user_reward_share, 2)
    // $5.00 payout * 0.70 = $3.50 user reward
    save_to_database(offer)

Step 3: Build the Offerwall Display

Serving Offers to Users

Create an API endpoint or page that returns the list of offers appropriate for the current user. Filter based on:

Sorting and Ranking

How you sort offers impacts revenue significantly. Effective sorting strategies:

// Example weighted scoring
for each offer:
    offer.score = (offer.epc * 0.4) + (offer.conversion_rate * 0.3) + (offer.user_reward * 0.3)
sort(offers, by: score, descending)

Generating Tracked Offer Links

When displaying offers, generate tracked click URLs that include the user's ID:

// Replace {sub1} token with the user's ID
function get_offer_link(offer, user_id):
    url = offer.tracking_url
    url = url.replace("{sub1}", user_id)
    url = url.replace("{sub2}", offer.internal_id)  // optional: your internal offer reference
    return url

The sub1 parameter is critical — it is how you identify which user completed the offer when the postback fires. Without it, you cannot credit the correct user.

Step 4: Build the Postback Handler

The postback handler is the most important piece of your integration. It must be reliable, secure, and fast. Read our full postback tracking setup guide for additional context.

Postback URL Configuration

In your CPA network's dashboard, configure your postback URL:

https://yourdomain.com/api/postback?user_id={sub1}&offer_id={offer_id}&payout={payout}&tx_id={transaction_id}&status={status}&secret=YOUR_SECRET

Handler Implementation (Pseudocode)

function handle_postback(request):
    // 1. Parse parameters
    user_id = request.param("user_id")
    offer_id = request.param("offer_id")
    payout = float(request.param("payout"))
    tx_id = request.param("tx_id")
    status = request.param("status")  // "approved", "pending", "reversed"
    secret = request.param("secret")
    source_ip = request.ip

    // 2. Validate secret
    if secret != ENV["POSTBACK_SECRET"]:
        log_warning("Invalid secret from IP: " + source_ip)
        return response(403, "Forbidden")

    // 3. Validate source IP (optional but recommended)
    if source_ip not in ALLOWED_NETWORK_IPS:
        log_warning("Unexpected IP: " + source_ip)
        return response(403, "Forbidden")

    // 4. Validate required fields
    if not all([user_id, offer_id, payout, tx_id]):
        log_error("Missing required fields")
        return response(400, "Bad Request")

    // 5. Check for duplicate
    existing = db.find("conversions", where: { transaction_id: tx_id })
    if existing:
        if status == "reversed" and existing.status != "reversed":
            // Handle reversal of previously approved conversion
            handle_reversal(existing, user_id)
            return response(200, "Reversal processed")
        log_info("Duplicate postback: " + tx_id)
        return response(200, "Already processed")

    // 6. Look up the user
    user = db.find("users", where: { id: user_id })
    if not user:
        log_error("Unknown user: " + user_id)
        return response(200, "OK")  // Return 200 to prevent retries

    // 7. Calculate user reward
    offer = db.find("offers", where: { network_offer_id: offer_id })
    if offer:
        user_reward = offer.user_reward
    else:
        user_reward = round(payout * 0.70, 2)  // Fallback: 70% share

    // 8. Record the conversion
    db.insert("conversions", {
        transaction_id: tx_id,
        user_id: user_id,
        offer_id: offer_id,
        network_offer_id: offer_id,
        payout: payout,
        user_reward: user_reward,
        status: status,
        ip_address: source_ip,
        raw_postback: request.full_url,
        created_at: now()
    })

    // 9. Credit the user (if approved)
    if status == "approved":
        db.increment("users", user_id, "balance", user_reward)
        db.increment("users", user_id, "lifetime_earnings", user_reward)
        // Optionally: send notification to user
        notify_user(user_id, "You earned $" + user_reward + " from " + offer.title)

    // 10. Return success
    log_info("Postback processed: " + tx_id + " for user: " + user_id)
    return response(200, "OK")

Handling Reversals

When a CPA network reverses a conversion (advertiser rejected it), you receive a postback with a "reversed" status. You must deduct the previously credited reward:

function handle_reversal(existing_conversion, user_id):
    if existing_conversion.status == "approved":
        db.decrement("users", user_id, "balance", existing_conversion.user_reward)
        db.update("conversions", existing_conversion.id, { status: "reversed" })
        log_info("Reversed conversion: " + existing_conversion.transaction_id)

Step 5: Security Implementation

An unsecured postback endpoint is a critical vulnerability. Implement all of these security measures:

IP Whitelisting

Maintain a list of IP addresses that your CPA network uses to send postbacks. Reject requests from any other IP. Most networks publish their server IPs in their documentation or can provide them on request.

ALLOWED_IPS = ["203.0.113.10", "203.0.113.11", "198.51.100.0/24"]

function validate_ip(request_ip):
    for allowed in ALLOWED_IPS:
        if ip_matches(request_ip, allowed):
            return true
    return false

Secret Token Validation

Include a static secret in your postback URL that your handler validates on every request. Rotate this secret periodically (quarterly is a reasonable cadence).

Transaction Deduplication

The unique index on transaction_id in your conversions table prevents duplicate crediting at the database level. Your handler should also check for duplicates before attempting to insert.

Payout Validation

Cross-reference the payout in the postback against the expected payout for that offer in your database. Flag significant discrepancies for manual review:

function validate_payout(postback_payout, expected_payout):
    if abs(postback_payout - expected_payout) > (expected_payout * 0.20):
        // Payout differs by more than 20% from expected
        flag_for_review("Payout mismatch: expected " + expected_payout + ", got " + postback_payout)
        return false
    return true

Rate Limiting

Apply rate limiting to your postback endpoint. Under normal conditions, postbacks arrive at a steady, predictable rate. A sudden spike could indicate an attack or misconfiguration.

Request Logging

Log every postback request, including the full URL, headers, source IP, and timestamp. This audit trail is invaluable for debugging, fraud investigation, and revenue reconciliation.

Step 6: Testing

Unit Testing

Write tests for each component of your integration:

Integration Testing

Test the full flow end-to-end:

  1. Fetch offers from the API and verify they display correctly
  2. Click an offer link and verify the redirect works (lands on the advertiser's page)
  3. Simulate a postback by calling your endpoint with test parameters
  4. Verify the conversion is recorded in your database
  5. Verify the user's balance is updated correctly
  6. Send a duplicate postback and verify it is rejected
  7. Send a postback with an invalid secret and verify it returns 403
  8. Send a reversal postback and verify the balance is deducted

Live Testing

Before going to production, ask your CPA network to send a test postback or complete a test conversion yourself. Verify the entire flow works with real network data.

Production Considerations

Reliability

Monitoring

Scaling

Working with Multiple Networks

Most production offerwalls aggregate offers from 2-5 CPA networks. To support multiple networks:

Getting Started with RevBoost's API

RevBoost provides an offer feed API and S2S postback support for publishers building custom offerwalls. Here is how to get started:

  1. Apply as a publisherSubmit your application and mention that you need API access for a custom offerwall integration.
  2. Get your credentials — Your account manager will provide API keys, documentation, and postback configuration instructions.
  3. Build and test — Follow the integration steps in this guide to build your offerwall.
  4. Go live — Deploy to production, configure your production postback URL, and start generating revenue.
  5. Optimize — Work with your RevBoost AM to identify the best-performing offers and optimize your offerwall for maximum revenue.

Building a custom offerwall via API integration is more work than embedding a third-party widget, but the payoff is significant: better user experience, higher revenue (no middleman fee), and complete control over your platform. For any serious rewards site or GPT platform, it is the right investment.

Ready to Integrate? Get API Access.

RevBoost provides offer feed APIs, S2S postback tracking, and dedicated technical support for publishers building custom offerwalls. Apply for access and start building.

Apply as a Publisher

Related Resources