Skip to main content

Overview

With Server-Side Verification (SSV) for rewarded ads, Adrop sends a callback to your server URL when a user completes watching an ad. This lets you verify reward grants on your own server and prevent abuse. The callback payload is encrypted with your API key, so even if the URL is exposed, only a party holding the API key can decrypt its contents.

Setup

1

Prepare an API key

Create an API key first — it will be used to decrypt the SSV callback payload. SSV registration requires at least one active API key in the project.See Integrations & API Keys for instructions.
2

Register the SSV

Under [Management] > [Integrations] > Reward Ad SSV, click [+ Register Reward Ad SSV].
FieldDescription
URLServer URL to call when a reward is completed. Must start with https://.
API keyAPI key used to decrypt the callback payload. Defaults to the most recently created active key.
3

Send userId / customData from the SDK

Set userId and customData via setServerSideVerificationOptions() in the SDK. These values are included in the callback payload.See the SDK guides for each platform:
Only HTTPS URLs are accepted at registration/edit time. Private IPs (localhost, 10.x, 172.16–31.x, 192.168.x, 169.254.x) are blocked.

Request Specification

Adrop delivers the callback as follows.
ItemValue
MethodPOST
Content-Typeapplication/json
RetriesUp to 3 attempts (0ms / 1s / 2s intervals)
Timeout5 seconds per attempt
SuccessHTTP 200 response
The request body is AES-256-GCM encrypted.
{
  "encrypted": "<ivHex>:<tagHex>:<ciphertextHex>"
}
Each segment is a hex string separated by colons (:). After decryption, the plaintext JSON has the following shape:
FieldTypeDescription
projectstringAdrop project ID
appstringAdrop app ID
unitstringAdrop ad unit ID
adNetworkstringAd network identifier
adUnitstringUnit identifier within the ad network
userIdstring?User identifier set via the SDK
customDatastring?Custom data set via the SDK
rewardItemstringReward item name
rewardAmountnumberReward amount
transactionIdstringUnique transaction ID for deduplication
timestampnumberCallback time (Unix ms)
transactionId is unique. If your server receives a transactionId that was already processed, handle it idempotently to avoid granting duplicate rewards.

Decrypting the Payload

The AES-256-GCM key is the raw API key hashed with SHA-256 (32 bytes).

Node.js

import { createDecipheriv, createHash } from 'node:crypto'
import express from 'express'

function decryptSsvPayload(encrypted, apiKey) {
  const [ivHex, tagHex, ciphertextHex] = encrypted.split(':')
  const key = createHash('sha256').update(apiKey).digest() // 32 bytes

  const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(ivHex, 'hex'))
  decipher.setAuthTag(Buffer.from(tagHex, 'hex'))

  const plaintext = Buffer.concat([
    decipher.update(Buffer.from(ciphertextHex, 'hex')),
    decipher.final()
  ]).toString('utf8')

  return JSON.parse(plaintext)
}

const app = express()

app.post('/ssv-callback', express.json(), (req, res) => {
  const payload = decryptSsvPayload(req.body.encrypted, process.env.ADROP_API_KEY)
  // Grant rewards based on payload.userId, payload.customData, payload.rewardAmount, etc.
  res.sendStatus(200)
})

Python

import hashlib
import json
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

def decrypt_ssv_payload(encrypted: str, api_key: str) -> dict:
    iv_hex, tag_hex, ciphertext_hex = encrypted.split(':')
    key = hashlib.sha256(api_key.encode()).digest()  # 32 bytes

    aesgcm = AESGCM(key)
    # AESGCM expects ciphertext concatenated with the authentication tag
    plaintext = aesgcm.decrypt(
        bytes.fromhex(iv_hex),
        bytes.fromhex(ciphertext_hex) + bytes.fromhex(tag_hex),
        None
    )
    return json.loads(plaintext)
SSV callbacks are designed so that only a party holding the Adrop API key can decrypt them, even if the URL is exposed. If the API key connected to the SSV is leaked, revoke it immediately on the server side and replace it with a new API key via the Integrations menu.

Editing and Deleting

Use the row menu in the Reward Ad SSV section to edit or delete an existing SSV.
  • Edit: Change the URL or the connected API key.
  • Delete: Callbacks will no longer be invoked. SDK-side userId / customData values continue to be stored in Adrop’s internal SSV log.

Integrations & API Keys

How to generate, manage, and revoke API keys

Report API

Query campaign performance and backfill revenue data via API