Receive votes by webhook
Votifier needs an open inbound port on your game server. If your host doesn't allow it, or if your network already runs its own web service, the webhook is simpler: we send each vote as a POST to the address of your choice.
Both methods can be used together. Turn on the webhook from your server's page, in your dashboard.
What you receive
A JSON body, and three headers.
POST /votes HTTP/1.1
Content-Type: application/json
X-CubeVote-Event: vote
X-CubeVote-Timestamp: 1790000000
X-CubeVote-Signature: sha256=3f7a…
{
"server": { "slug": "my-server", "name": "My Server" },
"username": "Steve",
"votes": { "month": 412, "total": 5120 },
"streak": 7,
"suspicious": false,
"at": "2026-09-20T18:04:11.482Z"
}suspicious flags a vote counted in the ranking but judged doubtful by our heuristics. It's up to you whether to hand out the reward, we advise doing so, and keeping an eye on repeat cases instead.
Verify the signature
Without verification, anyone who knows your address could send you fake votes. The signature is the HMAC-SHA256 of timestamp + "." + raw body, with the secret shown in your dashboard.
Compare it in constant time, and reject a timestamp more than five minutes old: that shuts the door on replaying an old message.
Node.js
import crypto from "node:crypto";
app.post("/votes", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.get("X-CubeVote-Timestamp") ?? "";
const received = (req.get("X-CubeVote-Signature") ?? "").replace("sha256=", "");
// Reject a replayed message.
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.status(408).end();
}
const expected = crypto
.createHmac("sha256", process.env.CUBEVOTE_SECRET)
.update(timestamp + "." + req.body.toString("utf8"))
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(received);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).end();
}
const vote = JSON.parse(req.body.toString("utf8"));
giveReward(vote.username);
res.status(200).end();
});PHP
<?php
$body = file_get_contents("php://input");
$timestamp = $_SERVER["HTTP_X_CUBEVOTE_TIMESTAMP"] ?? "";
$received = str_replace("sha256=", "", $_SERVER["HTTP_X_CUBEVOTE_SIGNATURE"] ?? "");
if (abs(time() - (int) $timestamp) > 300) { http_response_code(408); exit; }
$expected = hash_hmac("sha256", $timestamp . "." . $body, getenv("CUBEVOTE_SECRET"));
if (!hash_equals($expected, $received)) { http_response_code(401); exit; }
$vote = json_decode($body, true);
give_reward($vote["username"]);What we expect from you
- Answer fast. A 2xx code in under five seconds. Do the long work after answering.
- Tolerate duplicates. When delivery is in doubt, better to ignore a vote already processed than to hand out its reward twice.
- Don't redirect. Redirects aren't followed: give the final address directly.
A failed delivery isn't retried: the vote stays recorded on our side, and you can fetch it at any time with the vote API. That's the recommended safety net.