How to Configure Postbacks

How to Configure Postbacks #

Monetag’s server-side postback system allows you to receive HTTP notifications when an ad event — such as an impression or a click — is confirmed and processed by our backend.

This mechanism is essential for apps that offer user rewards, perform custom analytics, or rely on server-side validation of ad interactions.

⚠️ Postbacks are sent for these formats:

  • ✅ Rewarded Interstitial (impression and click)
  • ✅ Rewarded Popup (impression; click can come from a zone linked to the popup zone)
  • ✅ In-App Interstitial (impression only)

Step 1: Prepare Your App #

Before configuring postbacks, make sure that:

  • Your Telegram Mini App (TMA) is added to your Monetag account.
  • The SDK is properly integrated and activated for that app.
  • You have the main SDK zone ID from your Monetag dashboard.
  • Your app passes a ymid that is unique for each ad show, for example show_XXX({ ymid: 'user-123_ad_5', requestVar: 'bonus-button' }). If you preload the ad, pass the same ymid to the preload call and to the call that shows it. See {ymid} in the Macro Reference.

Step 2: Configure Postback URL in SSP #

Postbacks can now be configured directly in the Monetag SSP:

Postback configuration example

  • Go to your main SDK zone settings
  • Find the Postback URL field
  • Paste your endpoint URL with macros (see below)
  • Save the changes

Postback macro example

Each SDK can have its own postback configuration.
If your Mini App uses multiple ad formats (e.g. Interstitial + Popup), the {sub_zone_id} macro will help you distinguish which zone triggered the postback.

Step 3: Prepare Your Postback URL #

Monetag will send a GET request to your endpoint for each confirmed monetized ad event.

You can use macros in your URL — these will be replaced with event-specific values by Monetag.

Example:

https://yourdomain.com/postback?secret=YOUR_SECRET&ymid={ymid}&zone_id={zone_id}&event_type={event_type}&reward_event_type={reward_event_type}&estimated_price={estimated_price}

Replace YOUR_SECRET with a long random string of letters and digits. The URL is stored in Monetag and never reaches the app, so a request without this value did not come from Monetag. Your endpoint rejects such requests, see Step 4.

📖 See the Macro Reference section for a full list of available placeholders.

Step 4: Implement the Endpoint #

Your backend must accept GET requests and parse the query parameters.

The examples grant one reward per ymid. This works only if you pass a unique ymid for each ad show, for example user-123_ad_5, not just a user ID.

Node.js (Express):

app.get('/postback', (req, res) => {
  const { secret, ymid, event_type, reward_event_type, zone_id } = req.query

  // reject requests without the secret from your postback URL: they did not come from Monetag
  if (!secret || secret !== process.env.POSTBACK_SECRET) {
    return res.sendStatus(403)
  }

  console.log(`[Postback] ymid=${ymid}, event_type=${event_type}, reward_event_type=${reward_event_type}, zone_id=${zone_id}`)

  // one reward per ymid: retries and the impression and click of one ad show repeat it
  if (reward_event_type === 'valued') {
    rewardUserOnce(ymid)
  }

  res.sendStatus(200)
})

PHP:

<?php
// reject requests without the secret from your postback URL: they did not come from Monetag
$secret = $_GET['secret'] ?? '';
if ($secret === '' || $secret !== getenv('POSTBACK_SECRET')) {
    http_response_code(403);
    exit;
}

$ymid = $_GET['ymid'] ?? null;
$rewardEventType = $_GET['reward_event_type'] ?? null;

// one reward per ymid: retries and the impression and click of one ad show repeat it
if ($rewardEventType === 'valued') {
    reward_user_once($ymid);
}

http_response_code(200);

Python (Flask):

import os

from flask import Flask, request

app = Flask(__name__)

@app.route("/postback")
def postback():
    # reject requests without the secret from your postback URL: they did not come from Monetag
    secret = request.args.get("secret")
    if not secret or secret != os.environ.get("POSTBACK_SECRET"):
        return "", 403

    ymid = request.args.get("ymid")
    event = request.args.get("event_type")
    reward_event_type = request.args.get("reward_event_type")

    print(f"[Postback] ymid={ymid}, event_type={event}, reward_event_type={reward_event_type}")

    # one reward per ymid: retries and the impression and click of one ad show repeat it
    if reward_event_type == "valued":
        reward_user_once(ymid)

    return "", 200

We recommend:

  • Logging all postbacks
  • Ensuring idempotency via ymid
  • Always returning 200 OK for successful processing

Step 5: Understand Retry Behavior #

A 2xx response counts as delivered. Redirects are not followed, so a 3xx response counts as a failure.

  • If your server answers with a status of 300 or higher or does not answer within 30 seconds, Monetag retries the postback after 1, 5, 15, 30, 60, 90, 120 and 180 minutes, then after 30 hours. Each wait counts from the previous attempt.
  • After a connection error (connection refused, DNS failure), Monetag retries once, after 30 hours.
  • After the last retry the postback is dropped.
  • Monetag does not deduplicate postbacks. The same postback can arrive more than once.
  • Make sure your endpoint is available and efficient

Best Practices #

To ensure correct and secure processing:

  • ✅ Always use a unique ymid per ad show
  • ✅ Validate all parameters to prevent abuse or broken logic
  • ✅ Use HTTPS and avoid redirects
  • ✅ Log all postbacks for monitoring and debugging
  • ✅ Ensure idempotency by using ymid to avoid duplicate rewards
  • ✅ Add a secret parameter to your postback URL, for example &secret=<random string>, and reject requests without it. The URL is stored in Monetag and never reaches the app
  • ✅ Call the SDK with your main zone ID only. The zone that delivered the ad arrives in {sub_zone_id}
  • ✅ Use request_var to see where in your UI the ad was shown

Once your setup is complete, test postbacks by triggering real ad views in your app.