Vote rewards

Vote rewards use the server.vote Webhook event: your handler receives the event, fetches vote data through the API, finds the player in your system, and issues the reward once.

To integrate this method, first configure the project Webhook and verify signature. Vote data is requested through GET /votes/:vote_id.

How the flow works

  1. Receive the Webhook event and verify signature. If is_test is true, return 204 without fetching the vote and without issuing a reward.
  2. Make sure event_type is server.vote.
  3. Take event_id: for server.vote it is the vote ID.
  4. Fetch vote data through GET /votes/:vote_id and find the player in your system.
  5. Apply duplicate-processing protection by event_type + event_id and issue the reward in the same transaction.
  6. If the reward cannot be issued safely, return an error response, fix the cause, and retry delivery from the interface.

Reward example

Suppose player PlayerName voted for the server with ID 1, and your system must add 100 coins.

  1. GAMEMONITORING sends a Webhook with event_type: server.vote and event_id: 9824cabb-2203-437e-9b6c-aba43dde3e4b.
  2. Your handler verifies signature. If the signature is invalid, it returns 401 and stops.
  3. The handler requests GET /votes/9824cabb-2203-437e-9b6c-aba43dde3e4b and receives nickname, server, and user data.
  4. In your database, the handler finds the local account by nickname or your own account link.
  5. In a transaction, the handler applies duplicate-processing protection by server.vote + event_id and adds 100 coins.
  6. Handle repeated delivery of the same event by the same duplicate-processing rules.

The same flow also works for items, roles, VIP time, promo codes, or work queued in an internal system.

Vote event

When a server receives a vote, GAMEMONITORING sends the server.vote event. The event body contains only delivery data: event_type, event_id, is_test, and signature. Full vote data must be requested separately.

Event example
{
  "event_id": "9824cabb-2203-437e-9b6c-aba43dde3e4b",
  "event_type": "server.vote",
  "is_test": false,
  "signature": "ae83b8aba88a3a9ab3b97b1f6d65664da5628a9cb64d56d5132807bca5472e4f"
}

In this event, event_id is the vote ID. Do not use the Webhook body as the source for nickname, server, or user data: those values come from the API.

Fetch vote data

Use event_id as vote_id and request the vote data through GET /votes/:vote_id:

Vote data request
curl -sS "https://api.gamemonitoring.net/votes/9824cabb-2203-437e-9b6c-aba43dde3e4b"

For reward delivery, you usually need response.nickname, response.server, and public response.user data. If the reward depends on a specific server, always check response.server.id.

Example mapping: response.nickname finds the player account in your database, response.server.id selects the reward rule for the server, and response.user.id can be stored in the reward log as the GAMEMONITORING user ID that voted.

If the API is temporarily unavailable or returns an unexpected response, do not issue a reward without verification. Return an error response, fix the cause, and retry delivery from the interface.

Complete example

The example verifies the signature, fetches vote data, prevents duplicate rewards, and applies the reward. Replace the user table name, balance field, and player lookup rule with your system structure.

Before running the example, configure the project Webhook, check GET /votes/:vote_id, and replace the SQL user update queries with your account model.

php
<?php
// Set the webhook token, API URL and local database connection.
$secret = 'paste-webhook-token-here';
$apiUrl = 'https://api.gamemonitoring.net';
$rewardAmount = '1.00';
$pdo = new PDO('mysql:host=127.0.0.1;dbname=game;charset=utf8mb4', 'game', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

// Read the JSON payload sent by GAMEMONITORING.
$data = json_decode(file_get_contents('php://input'), true) ?: [];
$isTest = ($data['is_test'] ?? false) === true;
$signingData = array_replace($data, ['is_test' => $isTest ? 'true' : 'false']);

// Prepare payload keys for signature verification.
$fields = array_values(array_filter(array_keys($data), fn($field) => $field !== 'signature'));
sort($fields, SORT_STRING);

// Build the signing string and expected HMAC.
$signing = implode('&', array_map(fn($field) => $field . '=' . (string) ($signingData[$field] ?? ''), $fields));
$expected = hash_hmac('sha256', $signing, $secret);
$actual = (string) ($data['signature'] ?? '');

// Reject requests with an invalid signature.
if (!hash_equals($expected, $actual)) {
    http_response_code(401);
    exit;
}

// Acknowledge test deliveries without changing balance.
if ($isTest) {
    http_response_code(204);
    exit;
}

// Process server vote events.
if (($data['event_type'] ?? '') === 'server.vote') {
    $eventType = (string) $data['event_type'];
    $eventId = (string) $data['event_id'];

    // Load full vote data by event_id.
    $voteUrl = $apiUrl . '/votes/' . rawurlencode($eventId);
    $voteResponse = json_decode(file_get_contents($voteUrl), true) ?: [];
    $vote = $voteResponse['response'] ?? null;
    $voteServerId = (string) ($vote['server']['id'] ?? '');

    // Use vote nickname to update the local account. Use $voteServerId for per-server rules.
    $nickname = trim((string) ($vote['nickname'] ?? ''));

    if ($nickname !== '') {
        // Keep deduplication and reward update in one transaction.
        $pdo->beginTransaction();

        try {
            // Store the event once; duplicate deliveries affect zero rows.
            $reward = $pdo->prepare('INSERT IGNORE INTO gamemonitoring_webhooks (event_type, event_id) VALUES (?, ?)');
            $reward->execute([$eventType, $eventId]);

            // Reward the local user only for a newly stored event.
            if ($reward->rowCount() === 1) {
                $balance = $pdo->prepare('UPDATE users SET balance = balance + ? WHERE nickname = ?');
                $balance->execute([$rewardAmount, $nickname]);
            }

            // Commit after deduplication and balance update succeed.
            $pdo->commit();
        } catch (Throwable $error) {
            // Roll back if any database step fails.
            $pdo->rollBack();
            throw $error;
        }
    }
}

http_response_code(204);