SERVER OWNER GUIDE
Set up vote rewards
Follow these steps once for each server. The callback endpoint belongs on your own website—it receives RuneSource's confirmation and rewards the correct in-game account.
X-RuneSource-Signature header with your callback secret. If it is missing or invalid, return HTTP 403 and never reward the player.Create a receiver
Create a public endpoint on your server website, such as:
https://play.yourserver.com/runesource-callback.phpUse the complete PHP example below. The URL must use HTTPS and must not require a player login.
Open callback settings
Sign in to RuneSource, open Dashboard, find your server, and select Vote callback.
Save the callback URL and secret
Paste the endpoint into Public HTTPS callback URL and save. Copy the generated signing secret into your receiver's $callbackSecret.
X-RuneSource-Signature. Both values must match character for character or the callback must be rejected. Keep it private.Add your vote button
Your website must insert the signed-in player's URL-encoded game name:
$voteUrl = 'https://runesource.org/vote.php?id=YOUR_SERVER_ID&username=' . rawurlencode($playerName);Store and reward the vote
After validating the signature, save vote_id and queue the reward for player_name. Make the vote ID unique so it can never reward twice.
Test it
Start from your website's vote button. Confirm RuneSource shows Voting as PlayerName, complete the vote, and ensure your endpoint returns HTTP 200–299.
- Public HTTPS callback URL
- Matching callback secrets
- Form-encoded POST accepted
source=runesourceaccepted- Required:
X-RuneSource-Signatureverified against the untouched raw body - Missing or invalid signatures return HTTP 403
- Unique
vote_idenforced - HTTP 200 returned on success
OVERVIEW
How it works
- 1Send the player to RuneSource
Include their in-game username in the vote link.
- 2The player completes a verified vote
RuneSource validates and records the vote.
- 3RuneSource calls your endpoint
Your server receives a signed HTTPS POST.
- 4Reward the player
Verify and store the unique vote ID first.
STEP 1
Create the vote link
Replace SERVER_ID and URL-encode the player's in-game username.
https://runesource.org/vote.php?id=SERVER_ID&username=PLAYER_NAMESTEP 2
Receive the postback
Fields are sent as application/x-www-form-urlencoded. In PHP, read them from $_POST, not JSON.
| Field | Example | Description |
|---|---|---|
source | runesource | Fixed source identifier |
provider | RuneSource | Provider display name |
event | vote.created | Event type |
vote_id | 123 | Unique vote ID |
server_id | 3 | RuneSource server ID |
server_name | Catalyst | Listed server name |
username | Osmerek | Name supplied in the vote link |
player_name | Osmerek | Username compatibility alias |
voted_at | 2026-08-26T19:53:04+00:00 | UTC ISO-8601 time |
Headers
Content-Type: application/x-www-form-urlencoded
User-Agent: RuneSource-Vote-Callback/1.3
X-Vote-Source: runesource
X-RuneSource-Provider: RuneSource
X-RuneSource-Event: vote.created
X-RuneSource-Vote-ID: 123
X-RuneSource-Signature: sha256=SIGNATURESECURITY · REQUIRED
Signature verification is mandatory
Every callback includes X-RuneSource-Signature. Sign the untouched raw request body with your callback secret and compare it safely. Never process or reward a vote when this check fails.
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_RUNESOURCE_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $callbackSecret);
if (!hash_equals($expected, $received)) {
http_response_code(403);
exit('Invalid signature');
}COPY-READY
Complete PHP receiver
<?php
declare(strict_types=1);
$callbackSecret = 'PASTE_YOUR_RUNESOURCE_CALLBACK_SECRET';
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_RUNESOURCE_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $callbackSecret);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405); exit('POST required');
}
if (!hash_equals($expected, $signature)) {
http_response_code(403); exit('Invalid signature');
}
if (($_POST['source'] ?? '') !== 'runesource') {
http_response_code(403); exit('Invalid source');
}
$voteId = filter_var($_POST['vote_id'] ?? null, FILTER_VALIDATE_INT);
$player = trim((string)($_POST['player_name'] ?? $_POST['username'] ?? ''));
if (!$voteId || $player === '') {
http_response_code(400); exit('Missing vote_id or player_name');
}
// Check and store vote_id before rewarding $player.
// Grant or queue the player's reward here.
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['ok' => true]);DELIVERY
Response codes
Successfully received.
Missing or invalid fields.
Source or signature rejected.
Your endpoint failed.
RuneSource records a successful delivery only for HTTP 200–299.
HELP
Troubleshooting
Why do I receive “Invalid source”?
Accept lowercase runesource from the source field or X-Vote-Source. A placeholder title such as “TopG” must not be used to reject RuneSource.
Why does it return 403?
Ensure the callback secret matches exactly and calculate the signature from the untouched raw body.
Why are POST fields empty?
The request is form encoded. Use $_POST; use php://input only to verify the signature.
Should it require login or CSRF?
No. It must be a public HTTPS endpoint authenticated by the HMAC signature.
How do I stop duplicate rewards?
Store vote_id with a unique database constraint and never reward the same ID twice.