Configure a listing callback
Open User Control Panel → Edit Account (administrators: Admin → Manage Members → Edit Member). Under Callback, save a direct public HTTPS Callback URL. A missing token is generated on activation; an empty URL disables callbacks.
Generate token also works without a URL. Generate and Rotate save only the token; save URL and other account changes separately. Use Show / hide and Copy token to configure your receiver. Tokens are generated by the server and cannot be entered manually.
Normal saves and URL changes preserve the token. To replace it, confirm and press Rotate token; update your receiver immediately because the old token stops working. Existing HTTP URLs remain inactive until changed to HTTPS. Redirects and non-public target addresses are rejected.
Send a stable player identifier
Use id in your vote link. postback and incentive remain compatible aliases. The first non-empty value in the order id, postback, incentive is forwarded as userid. URL-encode player IDs and custom values.
https://private-server.ws/index.php?a=in&u=TOPLIST_USERNAME&id=VOTER_IDOptional custom value: add &callback=campaign-spring. It is forwarded unchanged as custom.
Validate and reward once
Callbacks use HTTPS POST. The listing token is sent as secret exclusively in the POST body; non-secret fields are also included in the query string for compatibility. Validate the POST secret with hash_equals and reward only voted=1.
secretPrivate server-generated listing token; POST only.voted1 for a new vote, 0 for a duplicate.resetUnix timestamp of the next daily vote reset, using the site timezone and offset.useridOriginal player ID from the vote link, up to 255 bytes.useripPlayer IP; not the callback sender IP.customOptional unchanged custom value, up to 255 bytes.Deduplicate rewards atomically by listing, player and reset timestamp. Return HTTP 200 for successful requests, including duplicates. Delivery failures do not stop the vote and are not automatically retried. Do not log tokens or request bodies; no outgoing IP allowlist is provided.
PHP receiver example
The complete example below uses an InnoDB transaction and a unique reward key. Configure secrets and database credentials in your receiver's environment. The rewards and player balance must use the same database transaction. Adapt table and player names to your application; never use this production handler for TEST callbacks.
CREATE TABLE callback_rewards (
listing VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
player VARBINARY(255) NOT NULL,
reset BIGINT NOT NULL,
PRIMARY KEY (listing, player, reset)
) ENGINE=InnoDB;
-- Existing players table must also use InnoDB and have a unique player ID.
<?php
$expected = getenv('TOPLIST_CALLBACK_SECRET');
$listing = getenv('TOPLIST_LISTING'); // server-controlled identity of this integration
$secret = $_POST['secret'] ?? null;
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !is_string($expected) || $expected === ''
|| $expected === 'TEST' || !is_string($listing) || !preg_match('/^[a-zA-Z0-9_-]{1,64}$/D', $listing)
|| !is_string($secret) || $secret === 'TEST' || !hash_equals($expected, $secret)) {
http_response_code(403); exit;
}
$id = $_POST['userid'] ?? null;
$voted = $_POST['voted'] ?? null;
$resetText = $_POST['reset'] ?? null;
$reset = is_string($resetText) ? filter_var($resetText, FILTER_VALIDATE_INT) : false;
if (!is_string($id) || $id === '' || strlen($id) > 255
|| !in_array($voted, ['0','1'], true) || $reset === false || $reset <= 0) {
http_response_code(400); exit;
}
if ($voted === '0') { http_response_code(200); exit('ok'); }
$pdo = new PDO(getenv('DB_DSN'), getenv('DB_USER'), getenv('DB_PASSWORD'), [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]);
try {
$pdo->beginTransaction();
try {
$insert = $pdo->prepare('INSERT INTO callback_rewards (listing,player,reset) VALUES (?,?,?)');
$insert->execute([$listing,$id,$reset]);
} catch (PDOException $e) {
// MySQL 1062 only: an existing reward is a successful idempotent response.
if (($e->errorInfo[1] ?? null) !== 1062) { throw $e; }
$pdo->rollBack(); http_response_code(200); exit('ok');
}
$reward = $pdo->prepare('UPDATE players SET points=points+1 WHERE player_id=?');
$reward->execute([$id]);
if ($reward->rowCount() !== 1) { throw new RuntimeException('Unknown player'); }
$pdo->commit(); http_response_code(200); echo 'ok';
} catch (Throwable $e) {
if ($pdo->inTransaction()) { $pdo->rollBack(); }
// Log a generic internal error without tokens or request contents.
http_response_code(500);
}Send a public test callback
This form always sends secret=TEST; it never reads a listing token. Use an isolated test receiver and never grant production rewards. The test verifies connectivity and payload delivery; verify your listing token separately with controlled votes. At most one test per IP per 30 seconds is allowed. It returns only Callback accepted or Callback failed; remote response contents are never displayed.