Skip to main content

Runix for server owners

How voting, postbacks, and paid placements work on Runix.

1. Direct votes on Runix

Players can vote directly on Runix from the toplist or your server page. Voting does not require an account. To keep votes clean, Runix uses Cloudflare Turnstile before the vote is recorded.

2. Vote callback (postback)

Runix can call your game server when someone votes. When a player votes on Runix, we automatically call your server's callback URL with the player's username and IP address so you can reward them in-game.

How to set it up

  1. Go to your dashboard and click Postback on the server you want to configure.
  2. Enter your callback URL — this is the endpoint on your server that handles vote rewards, e.g.:
    https://yourserver.com/api/vote-callback
  3. Link players to your dedicated vote page (simplest). They can enter their in-game name on the page, or you prefill it:
    https://runixrs.com/vote/your-serverhttps://runixrs.com/vote/your-server?u=PLAYER_USERNAME
  4. After the player completes the vote on Runix, we call your callback URL (example):
    https://yourserver.com/api/vote-callback?slug=your-server&secret=YOUR_SECRET&u=PLAYER_USERNAME&ip=VOTER_IP
  5. Your server receives the request and can reward the player automatically.

Technical details

  • Runix sends a GET request to your callback URL.
  • Runix appends query parameters to your callback URL, including voted=1, slug, secret, and when available u / userid and ip / userip. Use secret to verify the request on your side. You can use one fixed endpoint per game site and branch on the slug or path if you list on multiple sites.
  • Your endpoint should respond within 5 seconds.
  • Failed callbacks are logged on our end but do not block the vote.
  • The callback is best-effort — if your server is temporarily down, the vote still counts on Runix.
PHP vote callback — ready-to-use example (GET + query parameters — tap header to collapse)

Runix calls your URL with GET and appends parameters such as slug, voted, secret, and when available u / userid and ip / userip. Replace SECRET with the secret from your dashboard. Optional IP allowlisting can fail if Runix's outbound IP does not match www.runixrs.com — verifying secret is enough for most setups.

<?php

// --- Configuration ---
define("DEBUG", 0); // 0 = off, 1 = log, 2 = log + display
define("LOG_FILE", "_postback.log");
define("SECRET", "your-secret");
// Set true only if you know Runix's callback IP and want an extra check (secret is usually enough).
define("VERIFY_RUNIX_IP", false);
define("RUNIXRS_SOURCE_HOST", "www.runixrs.com");

/**
 * Helper to handle logging based on DEBUG level
 */
function logger(string $message, int $level = 1): void
{
    if (DEBUG >= $level) {
        $timestamp = date("[Y-m-d H:i:s]");
        error_log("$timestamp $message\n", 3, LOG_FILE);
        if (DEBUG === 2) {
            echo htmlspecialchars($message, ENT_QUOTES, "UTF-8") . "<br>";
        }
    }
}

// 1. Verify Request Method (Runix uses GET)
if ($_SERVER["REQUEST_METHOD"] !== "GET") {
    http_response_code(405); // Method Not Allowed
    exit("Invalid Request Method");
}

// 2. Optional: restrict by caller IP (off by default — Runix outbound IP may differ from the public site IP)
$ip_request = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER["REMOTE_ADDR"] ?? "";
if (VERIFY_RUNIX_IP) {
    $authorized_ip = gethostbyname(RUNIXRS_SOURCE_HOST);
    if ($authorized_ip !== RUNIXRS_SOURCE_HOST && $ip_request !== $authorized_ip) {
        logger("Unauthorized IP Attempt: $ip_request");
        http_response_code(403);
        exit;
    }
}

// 3. Validate required query parameters
$required = ["secret", "voted", "slug"];
foreach ($required as $field) {
    if (!isset($_GET[$field]) || $_GET[$field] === "") {
        logger("Missing parameter ($field) from IP: $ip_request");
        http_response_code(400);
        exit;
    }
}

// 4. Verify Secret
if ($_GET["secret"] !== SECRET) {
    logger("Invalid secret received from IP: $ip_request");
    http_response_code(401);
    exit;
}

// 5. Sanitize inputs (u / ip may be absent if no name was sent or IP was unreliable)
$username = preg_replace("/[^A-Za-z0-9\_\-]+/", "", (string)($_GET["u"] ?? ""));
$voter_ip = filter_var($_GET["ip"] ?? "", FILTER_VALIDATE_IP) ?: "0.0.0.0";
$voted = (int) $_GET["voted"];

// 6. Only allow positive vote counts
if ($voted < 1) {
    logger("Invalid vote count ($voted) from IP: $ip_request");
    http_response_code(400);
    exit;
}

logger("Processing vote: slug=" . $_GET["slug"] . ", User: $username, Voter IP: $voter_ip, Voted: $voted", 1);

// 7. Process the vote
try {
    // Your database logic here — e.g. reward when $username is non-empty
    // $db->query("UPDATE users SET points = points + 1 WHERE username = ?", [$username]);

    http_response_code(200);
    logger("Vote successful for " . ($_GET["slug"] ?? "") . " user=$username", 1);
} catch (Exception $e) {
    logger("Database Error: " . $e->getMessage());
    http_response_code(500);
}

exit;

3. Paid placements & billing

Runix offers three featured placements: Hero row ($29.99/mo or $14.99 for 7 days), Prime row ($19.99/mo or $9.99 for 7 days), and Discovery row ($9.99/mo or $4.99 for 7 days). These are fully automated through Stripe.

  1. From the dashboard, click Promote on the server you want to highlight.
  2. Choose a package: a monthly subscription (auto-renews) or a one-time boost (7 days) for Hero, Prime, or Discovery. One-time pricing is higher but requires no recurring charges.
  3. You're redirected to Stripe Checkout to complete payment.
  4. After successful payment, Stripe notifies Runix via webhook and your server is automatically moved into the correct featured row.

When a subscription is canceled or a one-time boost expires, your server automatically returns to the free toplist. No manual action is required.

Community & support

Questions about listings, postbacks, or billing? Join the Runix Discord — we're happy to help.