How to Build Real-Time Lottery Draw Notifications & Webhooks
Polling an API continuously wastes server resources. With US Lottery API Webhooks, your server receives an instant HTTP POST push notification within seconds of official draw validation. In this guide, you will learn how to verify HMAC-SHA256 signatures, handle idempotent events, and broadcast live draw alerts to Discord, Telegram, and Email subscribers.
EVENT DRIVEN PUSH ARCHITECTURE
Webhook Payload Schema
When a drawing completes, US Lottery API delivers an HTTP POST request to your registered webhook URL with an event payload:
{
"event": "draw.results.published",
"event_id": "evt_powerball_20260801_9911",
"timestamp": "2026-08-01T23:05:12Z",
"data": {
"game_id": "powerball",
"game_name": "Powerball",
"draw_date": "2026-08-01",
"winning_numbers": [12, 34, 45, 56, 67],
"powerball": 10,
"multiplier": "2x",
"estimated_jackpot": "$1,200,000,000",
"next_draw_date": "2026-08-03",
"next_jackpot_estimate": "$1,350,000,000"
}
}HMAC-SHA256 Signature Verification
To prevent spoofing attacks, every webhook contains an X-Lottery-Signature header. Calculate the HMAC-SHA256 digest using your secret signing key and verify equality before processing:
const express = require("express");
const crypto = require("crypto");
const fetch = require("node-fetch");
const app = express();
// Note: Use raw body buffer for signature calculation
app.use(express.json({
verify: (req, res, buf) => {
req.rawBody = buf;
}
}));
const WEBHOOK_SECRET = process.env.LOTTERY_WEBHOOK_SECRET;
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID;
const DISCORD_WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL;
// Helper: Verify HMAC-SHA256 signature
function verifySignature(req) {
const signature = req.headers["x-lottery-signature"];
if (!signature || !WEBHOOK_SECRET) return false;
const expectedSignature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(req.rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Push to Telegram Channel
async function sendTelegramAlert(data) {
if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) return;
const message = `🚨 *NEW ${data.game_name.toUpperCase()} DRAW RESULT* 🚨\n\n` +
`📅 *Draw Date:* ${data.draw_date}\n` +
`🎱 *Numbers:* ${data.winning_numbers.join(", ")}\n` +
`🔴 *Powerball:* ${data.powerball}\n` +
`⚡ *Multiplier:* ${data.multiplier}\n` +
`💰 *Jackpot:* ${data.estimated_jackpot}\n\n` +
`➡️ *Next Jackpot:* ${data.next_jackpot_estimate} (${data.next_draw_date})`;
await fetch(`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: TELEGRAM_CHAT_ID,
text: message,
parse_mode: "Markdown",
}),
});
}
// Push to Discord Channel
async function sendDiscordAlert(data) {
if (!DISCORD_WEBHOOK_URL) return;
const payload = {
embeds: [
{
title: `🎉 ${data.game_name} Winning Numbers Released!`,
color: 65535, // Neon Cyan
fields: [
{ name: "Draw Date", value: data.draw_date, inline: true },
{ name: "Winning Numbers", value: data.winning_numbers.join(" - "), inline: false },
{ name: "Bonus Ball", value: String(data.powerball), inline: true },
{ name: "Multiplier", value: data.multiplier, inline: true },
{ name: "Current Jackpot", value: data.estimated_jackpot, inline: true },
],
footer: { text: "Powered by US Lottery API" },
timestamp: new Date().toISOString(),
},
],
};
await fetch(DISCORD_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
}
// Webhook HTTP Receiver Route
app.post("/api/webhooks/lottery", async (req, res) => {
if (!verifySignature(req)) {
console.warn("Unauthorized webhook attempt - HMAC signature mismatch!");
return res.status(401).json({ status: "error", message: "Invalid signature" });
}
const { event, data } = req.body;
console.log(`Received Webhook Event: ${event} for ${data.game_name}`);
if (event === "draw.results.published") {
// Send alerts concurrently
await Promise.allSettled([
sendTelegramAlert(data),
sendDiscordAlert(data)
]);
}
// Always respond 200 OK within 5 seconds to acknowledge receipt
return res.status(200).json({ status: "success", message: "Event processed" });
});
app.listen(3000, () => console.log("Webhook receiver listening on port 3000"));Idempotency & Delivery Guarantees
To guarantee zero missing events during transient network failures, US Lottery API enforces an exponential retry strategy. Webhook receivers should maintain idempotency using event_id:
- Retry Policy: If your server returns HTTP 5xx or times out (5s limit), the engine retries delivery 5 times (10s, 30s, 2m, 10m, 1h).
- Idempotency Check: Store processed
event_idvalues in Redis or your database for 24 hours to prevent sending duplicate Telegram/Discord push messages.
Activate Webhooks on Business & Enterprise Plans
Upgrade to Business or Enterprise tier to unlock real-time HTTP Webhook subscriptions and custom push alerts.