Docs / Overview
API Specification v2.4

Integration Documentation

Technical guide for developers integrating VeloxOffer Offerwalls, REST Offer Feeds, and Server-to-Server (S2S) Postbacks.

S2S Webhooks
HMAC Authentication
190+ Countries
Mobile WebView Ready

1. Overview

Choose the integration method that fits your product:

Method A

Hosted Offerwall

Fastest setup. Embed with a single iFrame or mobile WebView. VeloxOffer handles offer display, tracking, and user flow.

Method B

REST API Feed

Full UI control. Query active offers as JSON and render your own custom offer cards, lists, or reward store.

Core

S2S Postbacks

Instant HTTP webhook sent to your server whenever a user completes an offer to award virtual currency.

Credentials Reference

Copy these from your Publisher Dashboard:

Scroll table horizontally
Key Example Where to Find
pub_id 1042 Your numeric Publisher ID. Shown in dashboard header.
placement_id 85 Numeric ID for your app or website placement. Found under Apps & Placements.
api_key / secret sec_8f912c4b... Cryptographic key for iFrame access and postback signature verification.

Base URL & Limits

BASE URL https://veloxoffer.com

Rate limit: 60 requests/minute per IP on the Feed API. Cache responses locally for 30–60 seconds.

2. Offerwall Integration

Embed our pre-built, responsive offerwall directly into your site or app.

GET https://veloxoffer.com/wall?api_key={api_key}&user_id={user_id}
<!-- Responsive Offerwall Embed -->
<iframe 
    src="https://veloxoffer.com/wall?api_key=YOUR_API_KEY&user_id=USER_12345" 
    style="width: 100%; height: 850px; border: none; border-radius: 8px;" 
    allow="geolocation; camera" 
    loading="lazy">
</iframe>

URL Parameters

Scroll table horizontally
Parameter Type Requirement Description
api_key string Required Your placement API Key from your dashboard.
user_id string Required Unique identifier for your user. Echoed back in postbacks to credit points.
sub_id string Optional Custom tracking sub-tag (e.g. acquisition channel).
sub_id2 string Optional Secondary tracking sub-tag.

Mobile WebView Implementation

Enable DOM Storage and JavaScript so interactive surveys and offers render properly:

// Android WebView Setup
val webView = findViewById<WebView>(R.id.offerwallWebView)
webView.settings.apply {
    javaScriptEnabled = true
    domStorageEnabled = true
    databaseEnabled = true
    setSupportMultipleWindows(true)
}
webView.webViewClient = WebViewClient()

val apiKey = "YOUR_API_KEY"
val userId = currentUser.id
webView.loadUrl("https://veloxoffer.com/wall?api_key=$apiKey&user_id=$userId")
// iOS WKWebView Setup
import WebKit

let config = WKWebViewConfiguration()
config.preferences.javaScriptEnabled = true

let webView = WKWebView(frame: view.bounds, configuration: config)
view.addSubview(webView)

let apiKey = "YOUR_API_KEY"
let userId = "USER_12345"
if let url = URL(string: "https://veloxoffer.com/wall?api_key=\(apiKey)&user_id=\(userId)") {
    webView.load(URLRequest(url: url))
}

3. Offers Feed API (REST JSON)

Query raw offer metadata to build a native in-app rewards list or custom offerwall UI.

GET https://veloxoffer.com/api?pub_id={pub_id}&placement_id={placement_id}

Query Filters

Scroll table horizontally
Parameter Type Status Accepted Values
pub_id integer Required Your numeric Publisher ID (e.g. 1042).
placement_id integer Optional Applies your placement revenue share to calculated payouts.
country_name string Optional 2-letter ISO country code (e.g. US, GB, DE).
device string Optional Android, Iphone, Ipad, Windows, Mac.
browser string Optional Chrome, Safari, Firefox, Edge.
category_id integer Optional Filter by category ID (Games, Surveys, Free Trials).

Sample Request & Response

curl -X GET "https://veloxoffer.com/api?pub_id=1042&placement_id=85&device=Android&country_name=US"      -H "Accept: application/json"
[
  {
    "id": 1084,
    "offer_name": "Hero Wars - Alliance: Reach Chapter 3",
    "link": "https://veloxoffer.com/click?offer_id=1084&pub_id=1042&app_id=85",
    "description": "Install and complete Chapter 3 within 14 days. New users only.",
    "category_name": "Mobile Games",
    "ua_target": "Android, Tablet",
    "browsers": "Chrome, Edge",
    "countries": "US, CA, GB, AU, DE",
    "payout_type": "CPA",
    "payout": 3.85,
    "lead_qty": "Single"
  }
]

4. Server-to-Server (S2S) Postbacks

Postbacks are real-time HTTP calls sent to your server when an offer is completed to award user points safely.

Setup: Go to Placements → Edit Placement → Postback URL in your dashboard. Insert your endpoint URL containing your macros.

Macro Tokens Reference

Use bracketed tokens in your URL. VeloxOffer replaces them dynamically at dispatch:

Scroll table horizontally
Token Example Description
{user_id} usr_98234 User ID passed into the offerwall or tracking link.
{reward} / {amount} 250 Points or coins to credit to the user based on placement exchange rate.
{payout} 2.5000 Your revenue in USD ($).
{txn_id} txn_660e12f9b8c3 Globally unique conversion ID. Use this to prevent duplicate credits.
{status} 1 or 2 1 = Conversion Approved. 2 = Chargeback/Reversal (deduct points).
{currency} Coins Your placement virtual currency name.
{offer_id} 1084 Completed offer numeric ID.
{offer_name} Hero Wars Alliance Campaign name.
{ip} 198.51.100.42 User IP address at conversion time.
{timestamp} 1777287361 UNIX timestamp.
{secret} sec_8f912c4b... Your placement secret key for request validation.

The 3 Golden Postback Rules

Rule 1

Deduplicate by txn_id

Always verify if txn_id was already processed in your DB. If it exists, return 1 immediately without adding points again.

Rule 2

Handle Reversals (status=2)

If status=2 is received, deduct the reward points from the user's balance due to fraud or chargeback.

Rule 3

Reply HTTP 200 "1"

Your server MUST reply with HTTP 200 OK and body 1. Any other status code triggers 5 automatic retries (10s, 30s, 1m, 5m, 15m).

Secret Key Verification

Verify that incoming postback requests originate from VeloxOffer:

<?php
$appSecret = "YOUR_PLACEMENT_SECRET_KEY";
$receivedSecret = $_GET['secret'] ?? '';

if (!hash_equals($appSecret, $receivedSecret)) {
    http_response_code(403);
    exit('Unauthorized secret key');
}
// Secret key verified. Proceed with credit...
// Node.js Secret Verification
const SECRET_KEY = process.env.VELOXOFFER_SECRET;

app.get('/postback', (req, res) => {
    if (req.query.secret !== SECRET_KEY) {
        return res.status(403).send('Unauthorized');
    }
    // Secret verified. Proceed with credit...
    res.status(200).send('1');
});
# Python Secret Verification
import hmac

SECRET_KEY = "YOUR_PLACEMENT_SECRET_KEY"

def verify_request(secret_arg):
    if not hmac.compare_digest(secret_arg, SECRET_KEY):
        abort(403)

5. Ready-to-Use Webhook Code Recipes

Complete, runnable postback listeners with database deduplication and balance management:

<?php
/**
 * VeloxOffer Production Postback Receiver
 * URL: https://yoursite.com/postback.php?user_id={user_id}&reward={reward}&payout={payout}&txn_id={txn_id}&status={status}&offer_id={offer_id}&secret={secret}
 */
header('Content-Type: text/plain');

$SECRET_KEY = 'YOUR_PLACEMENT_SECRET_KEY';

// 1. Read parameters
$userId  = $_GET['user_id'] ?? '';
$reward  = floatval($_GET['reward'] ?? 0);
$payout  = floatval($_GET['payout'] ?? 0);
$txnId   = $_GET['txn_id'] ?? '';
$status  = intval($_GET['status'] ?? 1); // 1 = Approved, 2 = Reversal
$offerId = $_GET['offer_id'] ?? '';
$secret  = $_GET['secret'] ?? '';

// 2. Validate Secret
if (!hash_equals($SECRET_KEY, $secret)) {
    http_response_code(403);
    exit("Invalid secret key");
}

if (empty($userId) || empty($txnId)) {
    http_response_code(400);
    exit("Missing parameters");
}

// 3. Database connection & Deduplication
try {
    $pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'db_user', 'db_pass', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]);

    // Check if txn_id was already handled
    $stmt = $pdo->prepare("SELECT id FROM transactions WHERE txn_id = ?");
    $stmt->execute([$txnId]);
    if ($stmt->fetch()) {
        exit("1"); // Already processed, acknowledge receipt
    }

    // 4. Update user balance
    if ($status === 1) {
        $pdo->beginTransaction();
        $pdo->prepare("UPDATE users SET points = points + ? WHERE id = ?")->execute([$reward, $userId]);
        $pdo->prepare("INSERT INTO transactions (user_id, txn_id, offer_id, reward, payout, status, created_at) VALUES (?, ?, ?, ?, ?, 'approved', NOW())")
            ->execute([$userId, $txnId, $offerId, $reward, $payout]);
        $pdo->commit();
    } elseif ($status === 2) {
        $pdo->beginTransaction();
        $pdo->prepare("UPDATE users SET points = GREATEST(0, points - ?) WHERE id = ?")->execute([$reward, $userId]);
        $pdo->prepare("INSERT INTO transactions (user_id, txn_id, offer_id, reward, payout, status, created_at) VALUES (?, ?, ?, ?, ?, 'reversed', NOW())")
            ->execute([$userId, $txnId, $offerId, -$reward, -$payout]);
        $pdo->commit();
    }

    // 5. Must return 1
    echo "1";

} catch (Exception $e) {
    if (isset($pdo) && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    http_response_code(500);
    echo "Error: " . $e->getMessage();
}
const express = require('express');
const app = express();

const SECRET_KEY = process.env.VELOXOFFER_SECRET_KEY;

app.get('/postback', async (req, res) => {
    const { user_id, reward, payout, txn_id, status, secret } = req.query;

    if (secret !== SECRET_KEY) {
        return res.status(403).send('Unauthorized');
    }

    try {
        // Prevent duplicate processing
        const alreadyCredited = await db.checkTxn(txn_id);
        if (alreadyCredited) {
            return res.status(200).send('1');
        }

        if (parseInt(status) === 1) {
            await db.addPoints(user_id, parseFloat(reward), txn_id);
        } else if (parseInt(status) === 2) {
            await db.deductPoints(user_id, parseFloat(reward), txn_id);
        }

        return res.status(200).send('1');
    } catch (err) {
        console.error('Postback error:', err);
        return res.status(500).send('Server error');
    }
});

app.listen(3000, () => console.log('Postback listener active on port 3000'));
from flask import Flask, request, abort
import hmac

app = Flask(__name__)
SECRET_KEY = "YOUR_PLACEMENT_SECRET_KEY"

@app.route('/postback', methods=['GET'])
def handle_postback():
    secret = request.args.get('secret', '')
    if not hmac.compare_digest(secret, SECRET_KEY):
        abort(403)

    user_id = request.args.get('user_id')
    reward = float(request.args.get('reward', 0))
    txn_id = request.args.get('txn_id')
    status = int(request.args.get('status', 1))

    if db_has_txn(txn_id):
        return "1", 200

    if status == 1:
        credit_user(user_id, reward, txn_id)
    elif status == 2:
        deduct_user(user_id, reward, txn_id)

    return "1", 200

if __name__ == '__main__':
    app.run(port=5000)

6. Pre-Flight Checklist

Scroll table horizontally
# Verification Item Status
1 Pass a non-empty, unique user_id for every user in the offerwall URL. Mandatory
2 Your postback endpoint responds with HTTP 200 OK and body 1. Mandatory
3 Your database verifies txn_id uniqueness to prevent double crediting. Mandatory
4 Your script deducts points when status=2 (chargeback/reversal). Mandatory
5 Offers API Feed queries are cached for 30–60 seconds to prevent rate limits. Recommended

Developer Support

Integration Desk

Assistance with postback verification and placement setup.

support@veloxoffer.com

Partner Support

Direct Telegram or Slack engineering channels for certified partners.

Contact Partner Support →