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_CREATED
Prize payout has been created
When a new prize payout is created
PRIZE_PAYOUT_SUCCEEDED
Prize payout has been successfully processed
When a prize payout completes successfully
PRIZE_PAYOUT_FAILED
Prize payout has failed
When a prize payout fails after retries
PLAYER_OPTED_IN
Player has opted into a campaign
When a player opts into a campaign
PLAYER_OPTED_OUT
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}), 200def 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
campaign_id
string
Unique campaign identifier
request_id
string
Unique request identifier (UUID)
player_id
string
Player hash identifier
player_currency
string
Player's currency (e.g., "USD", "EUR")
event_type
string
Type of event that triggered the webhook
status
string
Current status of the prize payout
prize_type
string
Type of prize (cash, free_spins, bonus_amount, etc.)
amount
number
Prize amount (for applicable prize types)
bonus_id
number
Optional bonus identifier
game_id
number
Game ID (for free spins)
percentage_value
number
Percentage value (for deposit bonuses)
item
string
Item name (for catalog items)
value
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
player_id
string
Player hash identifier
campaign_id
string
Unique campaign identifier
event_type
string
Type of event (PLAYER_OPTED_IN or PLAYER_OPTED_OUT)

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-key


Verifying 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.0


Best 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 processingconst requestId = payload.request_id;

// Check if you've already processed this requestif (alreadyProcessed(requestId)) {
return res.status(200).json({ already_processed: true });
}

// Process the webhook and store the request_idprocessWebhook(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 eventsconst 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_id field

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