# Server-to-Server (S2S) Callback Configuration

Server-to-Server callbacks allow you to receive reward confirmation events directly from the offerwall via HTTP requests.&#x20;

Go to the Offerwall section, open the Overview tab, select your app from the list, then click the S2S Callbacks card in your app’s dashboard to create or manage S2S callbacks. Click **Configure** to get started.

<figure><img src="/files/p99FJa3EEU3EoxRzf3od" alt=""><figcaption></figcaption></figure>

## 1. Setup Callback URL

When a user completes an offer and earns a reward, an HTTP GET request is sent to your configured callback URL. The request includes a set of query parameters that convey key reward information.

### 1.1. Base URL

You must first define your **Base URL** — the endpoint on your server that will handle the incoming callback.

<figure><img src="/files/n6SksGSsSOZXFWBAIBu5" alt=""><figcaption><p>BaseUrl and Final Callback Url</p></figcaption></figure>

{% hint style="info" %}
For any custom URL requirements or special customizations, please contact your Account Manager.
{% endhint %}

### 1.2. Parameter Configuration

Parameters can be added using a **key-value builder** interface:

* Keys are fully editable.
* Values are selected from predefined **macros** (e.g., `user_id`, `value`, etc.) via a searchable dropdown.
* **Static parameters** can also be added with hardcoded values.
* Some **default system parameters** are pre-filled (e.g., `user_id`, `value`, `token`, `signature`) and appear in the list with locked values.

<figure><img src="/files/hANNyV7JE22wK767UoNF" alt=""><figcaption><p>Callback Macros Key Values</p></figcaption></figure>

{% hint style="info" %}
**Example callback setup:**

**Callback URL set by you:** <http://www.example.com/payments/offers/pubscale>

**Request sent from us:** HTTP GET <http://www.example.com/payments/offers/pubscale?user\\_id={user\\_id}\\&value={value}\\&token={token}\\&signature={signature}>
{% endhint %}

<table><thead><tr><th width="189.59765625">Query Params</th><th width="577.98046875">Description</th></tr></thead><tbody><tr><td>user_id <mark style="color:red;">*</mark></td><td>The 'unique_id' set while initializing the SDK.</td></tr><tr><td>value <mark style="color:red;">*</mark></td><td>The decimal value of the reward earned by the user.</td></tr><tr><td>token <mark style="color:red;">*</mark></td><td>A token that uniquely represents the transaction.</td></tr><tr><td>signature <mark style="color:red;">*</mark></td><td>Hash of the above values to verify the request (more info below).</td></tr><tr><td>click_datetime</td><td>The Unix timestamp of when the user clicked the offer. Eg<code>1718509200</code></td></tr><tr><td>conversion_datetime</td><td>The Unix timestamp of when the user completed the offer. Eg <code>1718509400</code></td></tr><tr><td>country</td><td>The user's country in ISO 3166-1 alpha-2 code. Example: <code>IN</code>, <code>US</code></td></tr><tr><td>gaid</td><td>The user's Google Advertising ID (Android only). Example: <code>a1b2c3d4-e5f6-7890-gh12-ijkl3456mnop</code></td></tr><tr><td>goal_name</td><td>The name assigned to the reward or task. Example: <code>Complete 5th Level</code></td></tr><tr><td>goal_value</td><td>A unique code for the completed task. Example: <code>FB_Level_Complete_5</code></td></tr><tr><td>idfa</td><td>Apple’s Identifier for Advertisers (iOS only). Example: <code>6D92078A-8246-4BA4-AE5B-76104861E7DC</code></td></tr><tr><td>ip</td><td>The IP address of the user when the offer was clicked. Example: <code>192.168.0.101</code></td></tr><tr><td>offer_id</td><td>A unique identifier for the completed offer. Example: <code>45678</code></td></tr><tr><td>offer_name</td><td>The title of the completed offer. Example: <code>Tappy Plane - GameApp</code></td></tr><tr><td>package_name</td><td>The package name of the app associated with the offer. Example: <code>com.tappyplane.gameapp</code></td></tr><tr><td>payout_usd</td><td>The payout amount in USD for the completed offer. Example: <code>0.75</code></td></tr><tr><td>useragent</td><td>The user’s browser and device information string. Example: <code>Mozilla/5.0 (Linux; Android 10; Pixel 3)</code></td></tr></tbody></table>

## <sub>1.3. Server Response</sub>

<mark style="color:purple;">Responding to the callback URL with status codes between and including the values 200 to 299 will mark the callback as successfully delivered.</mark>

In the event of the server not responding with any of the aforementioned status codes, the request will be **retried 6 times** at different intervals. You can utilize the unique token sent in the request to avoid rewarding the user multiple times for the same callback.

### 1.4. Hash validation

The value sent in the signature parameter can be used to validate the identity of the origin of the request. The value is an MD5 hash of the template below. The secret key is an additional key that will be provided along with the APP API KEY.

{% tabs %}
{% tab title="PHP" %}

```php
function calculateHash() {
	
	$user = $_GET['user_id'];
	$token = $_GET['token'];
	$value = $_GET['value'];

	$template = <secret_key>. "." . $user . "." . (int)$value . "." $token;

	return calculateMD5($template);
}

function calculateMD5($data) {
	return hash('md5', $data);
}

$hash = $_GET['signature'];

if ($hash != calculateHash()) {
	return false
}
```

{% endtab %}

{% tab title="NodeJs" %}

```javascript
const md5 = require('md5')
  
const template = `${<secret_key>}.${<user_id>}.${Math.trunc(<value>)}.${<token>}`
const hash = md5(template)

if(signature != hash) {
    return false
}

```

{% endtab %}

{% tab title="Python" %}

```python
import hashlib

template = '{secret_key}.{user_id}.{value}.{token}'.format(secret_key=<secret_key>,user_id=<user_id>,value=int(<value>),token=<token>)
hash = hashlib.md5(template.encode('utf-8')).hexdigest()

if hash != <signature>:
    return false            
```

{% endtab %}

{% tab title="Golang" %}

```go
template := fmt.Sprintf("%s.%s.%d.%s", <secret_key>, <user_id>, <int({value>), <token>)
hash := toMD5(template)

  if hash != {signature} {
        return errors.New("invalid signature")
  }

func toMD5(text string) string {
  algorithm := md5.New()
  algorithm.Write([]byte(text))
  return hex.EncodeToString(algorithm.Sum(nil))
}

```

{% endtab %}
{% endtabs %}

### Example

secret\_key = `fa072672-d432-11c4-885a-EB1CdEc4Bb13`\
user\_id = `30356439-8d15-4f47-B133-010a37C19eBD`\
value = `100.1234`\
token = `525a5B8e-512b-441A-a10B-72d218c370e5`

Final format = `fa072672-d432-11c4-885a-EB1CdEc4Bb13.30356439-8d15-4f47-B133-010a37C19eBD.100.525a5B8e-512b-441A-a10B-72d218c370e5`

### Steps to validate the value of the hash

1. Concatenate the values in the above format.
2. Make sure to convert the value parameter from decimal to integer.
3. Calculate the MD5 hash of the concatenated value.
4. Build Hex encode of the generated value.
5. Verify if the calculated value is equal to the value sent in the signature parameter.

<figure><img src="/files/8V9YazUjiKZE2XTzmvNU" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
As the value of the secret key will only be known to you and PubScale’s server, the hash cannot be generated by a fraudulent third party.
{% endhint %}

### 1.5. IP Whitelisting

You can restrict the callbacks to be accepted only from our sever IP address(es). Please whitelist the following IP(s) and regularly check back to find possible changes&#x20;

```
34.100.236.68
34.100.128.195
34.14.172.7

//Last updated at: Jan 20, 2026
```

## 2. Test Callback Tool&#x20;

A dedicated modal tool is available to test your callback configuration before going live.

* Safely edit and test the callback URL without impacting your saved configuration.
* **Input Fields**:
  * **User ID**: Used to simulate a user session.
  * **Custom Reward Value**: Used to simulate a reward payout.
* This allows for quick verification of your endpoint and debugging of parameters.

{% hint style="info" %}
The User ID you enter here must be the same as the User ID used in your SDK initialization
{% endhint %}

<figure><img src="/files/KU11BCeUKBkMf41OW4ej" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pubscale.gitbook.io/offerwall-sdk/server-to-server-s2s-callback-configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
