Integration docs
Everything you need to connect your app or website to Gamwall: offerwall, postbacks, signatures and APIs.
Getting started
Three steps from sign-up to your first paid conversion. Everything below goes into detail.
- Create a placement — your app or website on Placements. You get a placement ID (15 letters/numbers) and choose your currency name and rate.
- Set your postback URL — on the same placement page. We call it every time one of your users completes an offer, so you can credit them. See Postback setup.
- Show the offerwall — open the offerwall link in your app, or embed it on your site, with your user's ID.
Offerwall link
Open this URL in a browser or WebView. Everything loads from your placement ID (15 letters/numbers, on Placements) — offers, currency, postback. Add your own user's ID.
https://wall.gamwall.com/wall/wgvd372dty32dvb?user_id=USER_123
| Parameter | Description | Example |
|---|---|---|
/wall/{placement} | Your placement ID (in the path) | wgvd372dty32dvb |
user_id required | Your user's unique ID — comes back as {user_id} | USER_123 |
sub1, sub2 optional | Anything you want to track (traffic source, campaign…). Only added when you send them; they come back as {sub1} / {sub2}. |
iFrame embed
Embed the offerwall inside your website.
<iframe src="https://wall.gamwall.com/wall/wgvd372dty32dvb?user_id=USER_123"
width="100%" height="800" frameborder="0"
style="border:none;border-radius:16px"></iframe>Direct offer links
Promote one offer anywhere (button, banner, social post). Make the link on Offers → Promote. A link always needs a placement — that's where the conversion and postback go. Sub IDs are optional.
https://gamwall.com/api/offers/click?offerId=owshgdsw732evew623&userId=USER_123&placementId=wgvd372dty32dvb&sub1=facebook
| Parameter | Required | What it is |
|---|---|---|
offerId | Yes | The offer's ID (18 characters) — copy it from Offers |
placementId | Yes | Your placement's ID (15 characters) — no placement, no click |
userId | Recommended | Your user's ID — comes back in the postback as {user_id} |
sub1, sub2 | Optional | Your own tracking values — added only when you send them |
Every click gets its own click ID starting with clk_ followed by 14 random letters and numbers (e.g. clk_73264dery237eg) — it comes back as {click_id}.
Postback setup
Set a postback URL on your placement. We call it server-to-server on every conversion and every chargeback, replacing the macros below.
click_id + goal_id you already processed.https://yourwebsite.com/postback.php?user_id={user_id}&offer={offer_name}&payout={payout}&reward={currency_amount}&click_id={click_id}&status={status}&placement={placement_id}&sub1={sub1}&goal_id={goal_id}| Macro | Description | Example |
|---|---|---|
{user_id} | Your user's ID from the link (also {subid1}) | USER_123 |
{sub1} | Optional Sub ID 1 you added (also {subid2}) | |
{sub2} | Optional Sub ID 2 you added (also {subid3}) | banner_a |
{placement_id} | The placement the click came from | wgvd372dty32dvb |
{offer_id} | Offer ID | owshgdsw732evew623 |
{offer_name} | Completed offer name | Coin Master |
{payout} | Your payout in USD | 0.75 |
{currency_amount} | Reward in your currency | 75 |
{currency_name} | Your currency name | Coins |
{click_id} | Unique click ID — use it for duplicate checks | clk_73264dery237eg |
{conversion_id} | Unique conversion ID (number) | 93822 |
{status} | approved or chargeback | approved |
{goal_id} | Multi-step goal ID (empty for single-step) | 12 |
{goal_title} | Multi-step goal name | Reach level 10 |
{step_number} | Multi-step sequence number | 2 |
{ip_address} | User IP at click time | 203.0.113.7 |
{country} | User country (2 letters) | US |
{signature} | Optional HMAC-SHA256 signature — see Securing postbacks | a3f1c9… |
{timestamp} | Unix time when Gamwall sent this postback | 1767225600 |
{signature_ts} | Like {signature} but also covers |timestamp — refuse calls older than a few minutes | b81e0d… |
{goal_id}, {goal_title} and {step_number} are only sent for multi-step offers (e.g. Install → Reach level 10 → Reach level 50). For normal single-step offers they are empty.
Securing postbacks optional
Anyone who guesses your postback URL could send a fake postback to credit themselves. To stop this, we can sign every postback with a secret key that only you and Gamwall know. A forged postback won't have a valid signature, so you reject it. If you don't add {signature}, postbacks keep working as before.
1 — Add the signature macro
https://yourwebsite.com/postback.php?click_id={click_id}&user_id={user_id}&sub1={sub1}&offer_id={offer_id}&payout={payout}&reward={currency_amount}&status={status}&goal_id={goal_id}&sig={signature}2 — How the signature is built
HMAC-SHA256 over these values joined by |, in this exact order, using your placement's secret key:
click_id | user_id | sub1 | offer_id | payout | currency_amount | status | goal_id
The order and values must match exactly — including goal_id (an empty string for single-step offers).
3 — Verify it on your server (PHP)
<?php
$secret = 'YOUR_SECRET_KEY';
$base = implode('|', [
$_GET['click_id'] ?? '',
$_GET['user_id'] ?? '',
$_GET['sub1'] ?? '',
$_GET['offer_id'] ?? '',
$_GET['payout'] ?? '',
$_GET['reward'] ?? '',
$_GET['status'] ?? '',
$_GET['goal_id'] ?? '',
]);
$expected = hash_hmac('sha256', $base, $secret);
if (!hash_equals($expected, $_GET['sig'] ?? '')) {
http_response_code(403);
exit('invalid signature');
}
// Signature OK — safe to credit the user.Test it before going live: on your placement page click Test postback. If your URL has {signature}, the test carries a real, correctly signed value.
4 — Stop replays (recommended)
Add &ts={timestamp}&sig={signature_ts} instead of {signature}. {signature_ts} is the same HMAC but over the base string plus |timestamp. Refuse a call when the signature is wrong or the timestamp is more than 10 minutes old — then a copied postback URL can't be used again later.
<?php
$ts = (int) ($_GET['ts'] ?? 0);
if (abs(time() - $ts) > 600) { http_response_code(403); exit('expired'); }
$expected = hash_hmac('sha256', $base . '|' . $ts, $secret); // $base as in step 3
if (!hash_equals($expected, $_GET['sig'] ?? '')) { http_response_code(403); exit('bad signature'); }5 — Secure offerwall links (optional)
On the placement page turn on Secure offerwall links. Then the offerwall, its history and every offer only open when the link carries &hash= — HMAC-SHA256 of the user ID with your postback secret. Nobody can open your wall, or read a user's history, with someone else's user ID. Build the link on your server, never in app code.
<?php
$hash = hash_hmac('sha256', $userId, 'YOUR_SECRET_KEY');
$wall = 'https://gamwall.com/wall/YOUR_PLACEMENT_ID?user_id=' . urlencode($userId) . '&hash=' . $hash;Sample code
Plain PHP
<?php
// postback.php — receives conversion postbacks from Gamwall
$user_id = $_GET['user_id'] ?? '';
$reward = (float)($_GET['reward'] ?? 0);
$conversion_id = ($_GET['click_id'] ?? '') . ':' . ($_GET['goal_id'] ?? '');
$status = strtolower($_GET['status'] ?? '');
if ($user_id === '' || $conversion_id === '') {
http_response_code(400);
exit(json_encode(['success' => false, 'message' => 'Missing parameters']));
}
// 1) Duplicate check — always by conversion_id
$existing = db_find_conversion($conversion_id); // your own lookup
if ($existing && $existing['status'] === $status) {
exit(json_encode(['success' => false, 'message' => 'Duplicate transaction']));
}
// 2) Credit or take back
if ($status === 'approved') credit_user($user_id, $reward);
if ($status === 'chargeback') deduct_user($user_id, $reward);
// 3) Save the conversion
save_conversion($conversion_id, $user_id, $reward, $status);
echo json_encode(['success' => true, 'message' => 'Reward added successfully']);Laravel
// routes/web.php
Route::get('/postback/gamwall', [PostbackController::class, 'gamwall']);
// app/Http/Controllers/PostbackController.php
public function gamwall(Request $request)
{
$userId = $request->query('user_id');
$reward = (float) $request->query('reward', 0);
$convId = $request->query('click_id') . ':' . $request->query('goal_id', '');
$status = strtolower($request->query('status', ''));
if (!$userId || !$convId) {
return response()->json(['success' => false, 'message' => 'Missing parameters'], 400);
}
$existing = Conversion::where('conversion_id', $convId)->first();
if ($existing && $existing->status === $status) {
return response()->json(['success' => false, 'message' => 'Duplicate transaction']);
}
$user = User::findOrFail($userId);
$status === 'chargeback' ? $user->decrement('balance', $reward) : $user->increment('balance', $reward);
Conversion::updateOrCreate(['conversion_id' => $convId], ['user_id' => $userId, 'reward' => $reward, 'status' => $status]);
return response()->json(['success' => true, 'message' => 'Reward added successfully']);
}{conversion_id} (or {click_id} + {goal_id}), never on user + offer — multi-step offers send several postbacks per user. A chargeback reverses an earlier approved conversion, so your endpoint must be able to take coins back.How chargebacks and duplicates work
- One postback per conversion and status. We send
status=approvedonce. If the advertiser later reverses it, we call the same URL again withstatus=chargebackand the same{conversion_id}and{click_id}— find that earlier reward and take it back. - The reward stays positive on a chargeback (e.g.
75, not-75); the status tells you to subtract it. - Retries. If your server does not answer at all (timeout, connection error, 502/503/504), we try again up to 5 times over 48 hours. If your server answers anything else, we do not resend automatically — so a slow or failing script can never pay a user twice. Still, always ignore a
{conversion_id}+{status}you already processed. - Answer quickly with HTTP 200. Credit the user, then reply
1orOK. Any 2xx answer counts as delivered. - Testing. In your placement, "Test postback" sends an approved test; a chargeback test right after uses the same
{conversion_id}, so you can check that your script takes the coins back. You can also type your own reward and transaction ID.
Response examples
Return HTTP 200 for anything you handled. Any 2xx counts as delivered; other codes are retried.
{
"success": true,
"message": "Reward added successfully"
}{
"success": false,
"message": "Duplicate transaction"
}Stats API
Read your own clicks, conversions and revenue from your server — no login, just your API key from Settings. Read-only: it can never change your tracking, postbacks or payouts.
GET https://wall.gamwall.com/api/stats?api_key=YOUR_API_KEY&range=7d
| Parameter | Required | Description |
|---|---|---|
api_key | Yes | Your API key |
range | No | today (default), yesterday, 7d, 30d, month or all |
offer | No | Only this offer (offer ID) |
placement | No | Only this placement (placement ID) |
Response
{
"success": true,
"range": "7d",
"date_from": "2026-09-24",
"date_to": "2026-09-30",
"stats": {
"clicks": 512,
"approved_conversions": 38,
"pending_conversions": 6,
"reversed_conversions": 1,
"revenue": 94.60,
"cvr_percent": 7.42,
"epc": 0.1848
},
"by_offer": [
{ "offer_id": "owshgdsw732evew623", "name": "Audible - IT - CPE", "clicks": 120, "conversions": 14, "revenue": 41.30 }
]
}by_offer lists your top 10 offers by revenue and is left out when you ask for one offer. Days are Bangladesh days (Asia/Dhaka, 12:00 AM → 12:00 AM). Revenue counts approved conversions only. Errors: 401 missing/invalid key · 403 account inactive or API disabled.
Offers Feed API
Pull every active offer for one placement into your own backend instead of using the hosted offerwall. The shape follows the common VortexWall-style feed. Find the Feed API key on the placement page — it's separate from your postback secret.
GET https://wall.gamwall.com/api/feed/wgvd372dty32dvb?api_key=YOUR_FEED_KEY&country=US
| Parameter | Required | Description |
|---|---|---|
/api/feed/{placement} | Yes | Your placement ID (in the path) |
api_key | Yes | That placement's Feed API key |
device | No | android, ios or desktop |
country | No | Comma-separated ISO codes, e.g. US,CA |
Response
{
"success": true,
"placement": "wgvd372dty32dvb",
"count": 1,
"data": [
{
"id": "owshgdsw732evew623",
"name": "Audible - IT - CPE - INCENT",
"description": { "en": "..." },
"payout": 1.50,
"reward": 150,
"currency": "Coins",
"events": [ { "eventId": "1", "action": { "en": "Complete offer" }, "payout": 1.50, "reward": 150 } ],
"multiEvent": false,
"icon": "https://…/icon.png",
"category": "Entertainment",
"country": ["IT"],
"device": [],
"dailyCap": null,
"fastpay": false,
"url": "https://gamwall.com/api/offers/click?offerId=owshgdsw732evew623&userId=[USER_ID]&placementId=wgvd372dty32dvb"
}
]
}Replace [USER_ID] in each url with your user's ID. An empty country or device list means no restriction. Offers you blocked never appear. Errors: 401 invalid placement/key · 403 placement inactive or feed disabled.
FastPay offers
Most offers are paid NET-30: a month's earnings are due 30 days after the month ends. FastPay offers are paid weekly: a week runs Saturday → Friday and is due right when it ends.
FastPay offers live on their own FastPay page. Tracking, postbacks, signatures and caps work exactly the same — only the payment timing changes. On Payments you can filter NET-30 and FastPay payouts; earnings are never counted in both.
Best practices
- Validate postbacks on your server
- Reject duplicate conversion IDs
- Use HTTPS only
- Keep a log of every postback
- Check payout amounts
- Handle chargebacks (take coins back)
- Don't put an iframe inside an iframe
- Use a mobile-friendly layout
