Webhook Integration Guide
Overview
This guide explains how to integrate with our webhook system to receive real-time notifications about prize payout events. Webhooks allow you to stay informed about prize payouts as they happen, enabling you to update your systems, send notifications, or trigger other actions.
What You'll Receive
When you configure a webhook endpoint, you'll receive HTTP POST requests containing JSON payloads with information about prize payout events. Each webhook can handle multiple types of events, making it efficient to process different stages of the prize payout lifecycle.
Supported Event Types
Event Type | Description | When You'll Receive It |
|---|---|---|
| Prize payout has been created | When a new prize payout is created |
| Prize payout has been successfully processed | When a prize payout completes successfully |
| Prize payout has failed | When a prize payout fails after retries |
| Player has opted into a campaign | When a player opts into a campaign |
| Player has opted out of a campaign | When a player opts out of a campaign |
Setting Up Your Webhook Endpoint
1. Create Your Endpoint
Create a public HTTP endpoint that can receive POST requests. Your endpoint should:
Accept POST requests
Return a 2xx status code (200, 201, 202) to acknowledge receipt
Handle JSON payloads
Be idempotent (safe to receive duplicate requests)
2. Example Endpoint (Node.js/Express)
const express = require("express");
const app = express();
app.use(express.json());
app.post("/webhooks/prizes", (req, res) => {
const payload = req.body;
// Process the webhook payload
console.log("Received webhook:", payload);
// Your business logic here
processWebhookEvent(payload);
// Always return a 2xx status code
res.status(200).json({ received: true });
});
function processWebhookEvent(payload) {
switch (payload.event_type) {
case "PRIZE_PAYOUT_CREATED":
handlePrizePayoutCreated(payload);
break;
case "PRIZE_PAYOUT_SUCCEEDED":
handlePrizePayoutSucceeded(payload);
break;
case "PRIZE_PAYOUT_FAILED":
handlePrizePayoutFailed(payload);
break;
case "PLAYER_OPTED_IN":
handlePlayerOptedIn(payload);
break;
case "PLAYER_OPTED_OUT":
handlePlayerOptedOut(payload);
break;
}
}3. Example Endpoint (Python/Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks/prizes', methods=['POST'])
def webhook_handler():
payload = request.get_json()
# Process the webhook payload
print(f"Received webhook: {payload}")
# Your business logic here
process_webhook_event(payload)
# Always return a 2xx status code
return jsonify({"received": True}), 200
def process_webhook_event(payload):
event_type = payload.get('event_type')
if event_type == 'PRIZE_PAYOUT_CREATED':
handle_prize_payout_created(payload)
elif event_type == 'PRIZE_PAYOUT_SUCCEEDED':
handle_prize_payout_succeeded(payload)
elif event_type == 'PRIZE_PAYOUT_FAILED':
handle_prize_payout_failed(payload)
elif event_type == 'PLAYER_OPTED_IN':
handle_player_opted_in(payload)
elif event_type == 'PLAYER_OPTED_OUT':
handle_player_opted_out(payload)Webhook Payload Format
All webhooks send JSON payloads with this structure:
{
"campaign_id": "campaign-123",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"player_id": "player-hash-abc123",
"player_currency": "USD",
"event_type": "PRIZE_PAYOUT_CREATED",
"status": "TODO",
"prize_type": "cash",
"amount": 100.5,
"bonus_id": 456
}Payload Fields
Field | Type | Description |
|---|---|---|
| string | Unique campaign identifier |
| string | Unique request identifier (UUID) |
| string | Player hash identifier |
| string | Player's currency (e.g., "USD", "EUR") |
| string | Type of event that triggered the webhook |
| string | Current status of the prize payout |
| string | Type of prize (cash, free_spins, bonus_amount, etc.) |
| number | Prize amount (for applicable prize types) |
| number | Optional bonus identifier |
| number | Game ID (for free spins) |
| number | Percentage value (for deposit bonuses) |
| string | Item name (for catalog items) |
| number | Item value (for catalog items) |
Prize Type Examples
Different prize types include different fields in the payload:
Cash Prize
{
"campaign_id": "summer-campaign-2024",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"player_id": "player-abc123",
"player_currency": "USD",
"event_type": "PRIZE_PAYOUT_SUCCEEDED",
"status": "SUCCESS",
"prize_type": "cash",
"amount": 100.5,
"bonus_id": 456
}Free Spins
{
"campaign_id": "slot-tournament-2024",
"request_id": "550e8400-e29b-41d4-a716-446655440001",
"player_id": "player-def456",
"player_currency": "EUR",
"event_type": "PRIZE_PAYOUT_CREATED",
"status": "TODO",
"prize_type": "free_spins",
"amount": 50,
"game_id": 123,
"bonus_id": 789
}Bonus Amount
{
"campaign_id": "welcome-bonus",
"request_id": "550e8400-e29b-41d4-a716-446655440002",
"player_id": "player-ghi789",
"player_currency": "GBP",
"event_type": "PRIZE_PAYOUT_SUCCEEDED",
"status": "SUCCESS",
"prize_type": "bonus_amount",
"amount": 75.25,
"bonus_id": 101
}Deposit Bonus
{
"campaign_id": "deposit-match",
"request_id": "550e8400-e29b-41d4-a716-446655440003",
"player_id": "player-jkl012",
"player_currency": "USD",
"event_type": "PRIZE_PAYOUT_CREATED",
"status": "TODO",
"prize_type": "deposit_bonus",
"percentage_value": 100.0,
"bonus_id": 202
}Catalog Item
{
"campaign_id": "loyalty-rewards",
"request_id": "550e8400-e29b-41d4-a716-446655440004",
"player_id": "player-mno345",
"player_currency": "USD",
"event_type": "PRIZE_PAYOUT_SUCCEEDED",
"status": "SUCCESS",
"prize_type": "catalog_item",
"item": "Gaming Headset",
"value": 25.0,
"bonus_id": 303
}Player Opt-in/Out Events
Player opt-in and opt-out events have a simpler payload structure compared to prize payout events. These events are triggered when players choose to participate in or withdraw from campaigns.
Player Opt-in Event
{
"player_id": "player-abc123",
"campaign_id": "summer-campaign-2024",
"event_type": "PLAYER_OPTED_IN"
}Player Opt-out Event
{
"player_id": "player-def456",
"campaign_id": "winter-tournament-2024",
"event_type": "PLAYER_OPTED_OUT"
}Player Opt-in/Out Payload Fields
Field | Type | Description |
|---|---|---|
| string | Player hash identifier |
| string | Unique campaign identifier |
| string | Type of event ( |
Authentication
If you provide an API key when setting up your webhook, we'll include it in the Authorization header:
Authorization: Bearer your-secret-api-keyVerifying Webhook Authenticity
To verify that webhooks are coming from us, check the Authorization header:
// Node.js example
app.post("/webhooks/prizes", (req, res) => {
const authHeader = req.headers.authorization;
const expectedToken = "Bearer your-secret-api-key";
if (authHeader !== expectedToken) {
return res.status(401).json({ error: "Unauthorized" });
}
// Process webhook...
});# Python example
@app.route('/webhooks/prizes', methods=['POST'])
def webhook_handler():
auth_header = request.headers.get('Authorization')
expected_token = 'Bearer your-secret-api-key'
if auth_header != expected_token:
return jsonify({"error": "Unauthorized"}), 401
// Process webhook...HTTP Headers
All webhook requests include these headers:
Content-Type: application/json
Authorization: Bearer {api_key} # if api_key is provided
User-Agent: Unibo-Webhooks/1.0Best Practices
1. Always Return 2xx Status Codes
Your endpoint should return a 2xx status code (200, 201, 202) to acknowledge receipt. If you return an error status code, we'll log the failure but won't retry.
// Good - returns 200
res.status(200).json({ received: true });
// Good - returns 202 (Accepted)
res.status(202).json({ processing: true });
// Bad - returns 500 (will be logged as failure)
res.status(500).json({ error: "Something went wrong" });2. Handle Duplicate Requests
Webhooks might be sent multiple times. Design your endpoint to handle this gracefully:
// Use request_id to prevent duplicate processing
const requestId = payload.request_id;
// Check if you've already processed this request
if (alreadyProcessed(requestId)) {
return res.status(200).json({ already_processed: true });
}
// Process the webhook and store the request_id
processWebhook(payload);
storeProcessedRequest(requestId);3. Process Webhooks Asynchronously
For better performance, process webhooks asynchronously:
app.post("/webhooks/prizes", (req, res) => {
// Immediately acknowledge receipt
res.status(202).json({ accepted: true });
// Process asynchronously
setImmediate(() => {
processWebhookAsync(req.body);
});
});4. Implement Proper Error Handling
app.post("/webhooks/prizes", (req, res) => {
try {
const payload = req.body;
// Validate payload
if (!payload.event_type) {
console.error("Invalid payload received:", payload);
return res.status(400).json({ error: "Invalid payload" });
}
// Process webhook
processWebhook(payload);
res.status(200).json({ received: true });
} catch (error) {
console.error("Error processing webhook:", error);
res.status(500).json({ error: "Internal server error" });
}
});Common Use Cases
1. Update Player Balance
function handlePrizePayoutSucceeded(payload) {
if (payload.prize_type === "cash") {
// Update player's balance in your system
updatePlayerBalance(payload.player_id, payload.amount);
// Send notification to player
sendNotification(payload.player_id, `You received $${payload.amount}!`);
}
}
function handlePrizePayoutCreated(payload) {
// Log the new prize payout for tracking
console.log(`New prize payout created: ${payload.request_id}`);
// Update internal tracking system
trackingService.createPayoutRecord(payload);
// Send confirmation to player
sendNotification(payload.player_id, "Your prize is being processed!");
}
function handlePrizePayoutFailed(payload) {
// Log the failed payout
console.error(`Prize payout failed: ${payload.request_id}`);
// Update internal tracking system
trackingService.markPayoutFailed(payload);
// Notify support team
supportService.createTicket({
type: "payout_failed",
player_id: payload.player_id,
request_id: payload.request_id,
campaign_id: payload.campaign_id,
});
}2. Track Prize Analytics
function trackPrizePayoutAnalytics(payload) {
const analytics = {
campaign_id: payload.campaign_id,
player_id: payload.player_id,
event_type: payload.event_type,
prize_type: payload.prize_type,
amount: payload.amount,
timestamp: new Date().toISOString(),
};
// Send to analytics service
analyticsService.track("prize_payout", analytics);
}3. Trigger External Actions
function handlePrizePayoutSucceeded(payload) {
// Trigger CRM update
crmService.updatePlayer(payload.player_id, { last_prize: payload.amount });
// Send to marketing automation
marketingService.trigger("prize_won", payload);
// Update loyalty program
loyaltyService.addPoints(payload.player_id, payload.amount);
}4. Track Player Campaign Participation
function handlePlayerOptedIn(payload) {
// Update player's campaign participation status
playerService.updateCampaignStatus(
payload.player_id,
payload.campaign_id,
"opted_in"
);
// Send welcome message
notificationService.send(
payload.player_id,
`Welcome to ${payload.campaign_id}!`
);
// Update analytics
analyticsService.track("player_opted_in", {
player_id: payload.player_id,
campaign_id: payload.campaign_id,
timestamp: new Date().toISOString(),
});
}
function handlePlayerOptedOut(payload) {
// Update player's campaign participation status
playerService.updateCampaignStatus(
payload.player_id,
payload.campaign_id,
"opted_out"
);
// Send opt-out confirmation
notificationService.send(
payload.player_id,
"You've been removed from the campaign."
);
// Update analytics
analyticsService.track("player_opted_out", {
player_id: payload.player_id,
campaign_id: payload.campaign_id,
timestamp: new Date().toISOString(),
});
}Testing Your Webhook
1. Use a Webhook Testing Service
Services like webhook.site or ngrok can help you test webhooks during development.
2. Test Different Event Types
Make sure your endpoint handles all event types you've configured:
// Test payloads for different events
const testPayloads = {
created: {
event_type: "PRIZE_PAYOUT_CREATED",
status: "TODO",
// ... other fields
},
succeeded: {
event_type: "PRIZE_PAYOUT_SUCCEEDED",
status: "SUCCESS",
// ... other fields
},
failed: {
event_type: "PRIZE_PAYOUT_FAILED",
status: "FAILED",
// ... other fields
},
player_opted_in: {
event_type: "PLAYER_OPTED_IN",
player_id: "player-test-123",
campaign_id: "test-campaign-2024",
},
player_opted_out: {
event_type: "PLAYER_OPTED_OUT",
player_id: "player-test-456",
campaign_id: "test-campaign-2024",
},
};3. Test Error Scenarios
Test how your endpoint handles:
Invalid JSON
Missing required fields
Authentication failures
Network timeouts
Troubleshooting
Common Issues
Webhook not received: Check that your endpoint is publicly accessible and returns 2xx status codes
Authentication errors: Verify your API key is correct and properly formatted
Payload parsing errors: Ensure your endpoint can handle JSON payloads
Duplicate processing: Implement idempotency using the
request_idfield
Debugging Tips
Log all incoming requests to see what you're receiving
Check your server logs for any errors
Verify your endpoint URL is correct and accessible
Test with a simple endpoint first to ensure basic connectivity
Getting Help
If you're having issues with webhook integration:
Check your server logs for error messages
Verify your endpoint is accessible from the internet
Test with a simple webhook testing service
Contact our support team with specific error details
Rate Limits
Currently, there are no rate limits on webhook delivery. However, we recommend:
Processing webhooks quickly (within 30 seconds)
Implementing proper error handling
Using asynchronous processing for heavy operations
Security Considerations
Use HTTPS: Always use HTTPS for your webhook endpoint
Validate API Keys: Check the Authorization header for valid API keys
Validate Payloads: Verify that payloads contain expected fields
Implement Rate Limiting: Consider rate limiting on your endpoint
Monitor for Abuse: Watch for unusual webhook activity