Webhook Integration
Oncade services emit webhooks for events like purchase fulfillment, account linking, virtual currency cashouts, and campaign lifecycle updates. All webhooks are sent as HTTPS POST requests with a JSON body and an HMAC signature header.
Signature Scheme
- Header:
x-oncade-signature - Algorithm: HMAC-SHA256 over the raw request body
- Secret: your webhook key configured in the dashboard
Always verify against the raw string body and compare using constant time.
Obtain your Webhook Key
- Game-level events (purchases, subscriptions, cashouts): Dashboard → Games → Webhooks. Every game has a single outbound configuration and HMAC secret that is used for all purchase lifecycle and cashout events.
- Campaign events: Dashboard → Campaigns → Webhooks.
Minimal Verifier
Reusable function to verify the x-oncade-signature header:
import crypto from 'crypto';
export function verifyOncadeSignature(rawBody: string, signature: string, secret: string): boolean {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
if (!signature) return false;
// Use constant time compare to prevent timing attacks
return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
}Server Examples
Next.js Route Handler
import crypto from 'crypto';
import { NextRequest, NextResponse } from 'next/server';
function verify(body: string, sig: string | null, secret: string) {
if (!sig) return false;
const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'));
}
export async function POST(request: NextRequest) {
const body = await request.text(); // read raw body for HMAC
const signature = request.headers.get('x-oncade-signature');
const secret = process.env.ONCADE_WEBHOOK_SECRET!; // store securely
if (!verify(body, signature, secret)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const payload = JSON.parse(body);
// handle payload.event and payload.data here
return NextResponse.json({ received: true });
}Express
Use express.raw to access the unparsed request body for signature verification.
import crypto from 'crypto';
import express from 'express';
const app = express();
// Capture raw body for application/json
app.post('/webhooks/oncade', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('x-oncade-signature');
const secret = process.env.ONCADE_WEBHOOK_SECRET!;
const raw = req.body.toString('utf8');
const expected = crypto.createHmac('sha256', secret).update(raw).digest('hex');
const valid = signature && crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
if (!valid) return res.status(401).send('Invalid signature');
const event = JSON.parse(raw);
// handle event.event and event.data
return res.sendStatus(200);
});
app.listen(3000, () => console.log('Listening on :3000'));Payload Shape
Example payload for a purchase fulfillment event:
{
"event": "Purchases.Completed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"metadata": { "platform": "steam" },
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}The paymentProvider field indicates how the purchase was paid:card, token, virtual-currency, free, or unknown.
Purchase Metadata Passthrough
When initiating a purchase via the wallet or virtual currency endpoints, you can include an optional metadata object. This metadata is stored with the purchase and echoed in all purchase webhook payloads (Purchases.Started, Purchases.Completed, etc.), allowing you to correlate webhook events with your own application context.
Constraints
- Type: Must be a plain JSON object
- Allowed values: Strings, numbers, booleans, null, arrays, or nested objects
- Maximum size: 4KB (4096 bytes when serialized)
- Maximum depth: 5 levels of nesting
Functions, Date objects, and other non-JSON-serializable types are not permitted and will result in a 400 error.
Example
Pass metadata when initiating a wallet purchase:
POST /api/v1/wallet/purchase
{
"userId": "link_usr_123",
"itemId": "65fa1234abcdef5678901234",
"metadata": {
"platform": "steam",
"partyId": "party-987",
"sessionData": { "level": 42 }
}
}The same metadata object will appear in the Purchases.Completed webhook payload, enabling you to match the completion event to your original request context.
Event Types
Webhook payloads include an event string identifying the event type. Common events include:
Purchases
Purchases.Started
{
"event": "Purchases.Started",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Failed
{
"event": "Purchases.Failed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Canceled
{
"event": "Purchases.Canceled",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Completed
{
"event": "Purchases.Completed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"metadata": { "platform": "steam" },
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Refunded
Fired when a completed purchase is refunded. Includes refund totals and the original payment reference.
{
"event": "Purchases.Refunded",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"refundSubtotal": 499,
"refundTotal": 499,
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Chargeback.Opened
Fired when a cardholder or bank opens a chargeback dispute. Includes the reason code and respond-by date.
{
"event": "Purchases.Chargeback.Opened",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"chargebackId": "cb_xyz789",
"reasonCode": "10.4",
"reasonDescription": "Other Fraud",
"respondByDate": "2024-06-14",
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Chargeback.Won
Fired when a chargeback dispute is resolved in the merchant's favor (funds retained).
{
"event": "Purchases.Chargeback.Won",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"chargebackId": "cb_xyz789",
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Chargeback.Lost
Fired when a chargeback dispute is resolved against the merchant (funds returned to cardholder).
{
"event": "Purchases.Chargeback.Lost",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"chargebackId": "cb_xyz789",
"metadata": {},
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Subscriptions.Started
{
"event": "Purchases.Subscriptions.Started",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Subscriptions.Failed
{
"event": "Purchases.Subscriptions.Failed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Subscriptions.Canceled
{
"event": "Purchases.Subscriptions.Canceled",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Subscriptions.Completed
{
"event": "Purchases.Subscriptions.Completed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Purchases.Fulfilled
Legacy alias for Purchases.Completed
{
"event": "Purchases.Fulfilled",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 499,
"currency": "USD",
"paymentProvider": "card",
"metadata": { "platform": "steam" },
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscription
Subscription lifecycle events are triggered by the payment processor when subscription state changes occur. These events help you track recurring billing status and respond to subscription changes in your game.
Subscription.Started
Fired when a new subscription is successfully created and activated.
{
"event": "Subscription.Started",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"subscriptionId": "sub_abc123def456",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscriptions.Failed
Fired when a subscription payment fails or the subscription cannot be renewed (e.g., payment declined, card expired).
{
"event": "Subscriptions.Failed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"subscriptionId": "sub_abc123def456",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscriptions.Canceled
Fired when a subscription is canceled by the user or through the dashboard.
{
"event": "Subscriptions.Canceled",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"subscriptionId": "sub_abc123def456",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscription.Completed
Fired when a subscription reaches its natural end (e.g., fixed-duration subscription concludes).
{
"event": "Subscription.Completed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"subscriptionId": "sub_abc123def456",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscription.Expired
Fired when a subscription expires and cannot be renewed (e.g., payment method expired, account closed).
{
"event": "Subscription.Expired",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planId": "plan_monthly",
"planCode": "premium_monthly",
"subscriptionId": "sub_abc123def456",
"interval": "monthly",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscription.Recurring.Started
Fired when a recurring payment for an existing subscription is authorized. This indicates the renewal payment has been initiated.
{
"event": "Subscription.Recurring.Started",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"paymentId": "pay_xyz789",
"planCode": "premium_monthly",
"planName": "Premium Monthly",
"subscriptionId": "sub_abc123def456",
"subscriptionStatus": "active",
"interval": "monthly",
"nextPaymentAt": "2024-06-14T12:34:56.000Z",
"subtotal": 999,
"total": 999,
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscription.Recurring.Completed
Fired when a recurring payment for an existing subscription is successfully settled. This confirms the renewal payment has been processed.
{
"event": "Subscription.Recurring.Completed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"paymentId": "pay_xyz789",
"planCode": "premium_monthly",
"planName": "Premium Monthly",
"subscriptionId": "sub_abc123def456",
"subscriptionStatus": "active",
"interval": "monthly",
"nextPaymentAt": "2024-06-14T12:34:56.000Z",
"subtotal": 999,
"total": 999,
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Subscription.Recurring.Failed
Fired when a recurring payment for an existing subscription fails to process.
{
"event": "Subscription.Recurring.Failed",
"data": {
"purchaseId": "purchase_abc123def456",
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "subscription",
"amount": 999,
"currency": "USD",
"paymentProvider": "card",
"metadata": {},
"planCode": "premium_monthly",
"planName": "Premium Monthly",
"subscriptionId": "sub_abc123def456",
"interval": "monthly",
"user_ref": "9f2e8d29-2e1b-4db3-aa3f-fd878d4ebb3f"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}User Accounts
User.Account.Link.Started
{
"event": "User.Account.Link.Started",
"data": {
"sessionKey": "session_abc123def456",
"user_ref": null,
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000001"
}
},
"timestamp": "2025-11-26T23:46:33.769Z"
}User.Account.Link.Failed
{
"event": "User.Account.Link.Failed",
"data": {
"sessionKey": "session_abc123def456",
"user_ref": null,
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000002"
}
},
"timestamp": "2025-11-26T23:46:33.769Z"
}User.Account.Link.Canceled
{
"event": "User.Account.Link.Canceled",
"data": {
"sessionKey": "session_abc123def456",
"user_ref": null,
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000003"
}
},
"timestamp": "2025-11-26T23:46:33.769Z"
}User.Account.Link.Succeeded
{
"event": "User.Account.Link.Succeeded",
"data": {
"sessionKey": "session_abc123def456",
"user_ref": "user-00000000-0000-0000-0000-000000000001",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000004"
}
},
"timestamp": "2025-11-26T23:46:33.769Z"
}User.Account.Link.Removed
{
"event": "User.Account.Link.Removed",
"data": {
"sessionKey": "session_abc123def456",
"user_ref": "user-00000000-0000-0000-0000-000000000001",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000005"
}
},
"timestamp": "2025-11-26T23:46:33.769Z"
}Listings and UGC
UGC.Listing.Created
Replaces Listing.created
{
"event": "UGC.Listing.Created",
"data": {
"userRef": "user-00000000-0000-0000-0000-000000000001",
"item": {
"_id": "item_abc123def456",
"gameId": "game_123",
"environment": "test",
"type": "purchase",
"creatorType": "ugc",
"status": "draft",
"name": "User Created Armor",
"description": "Custom armor created by user",
"price": 2000,
"creatorAddress": "0x0000000000000000000000000000000000000001",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000010"
},
"perUserLimit": 0,
"isVisible": false,
"isPriceVisible": true,
"forSale": false,
"createdAt": "2025-11-26T23:56:18.217Z",
"updatedAt": "2025-11-26T23:56:18.218Z",
"fulfillmentType": "WEBHOOK",
"paymentMethods": [],
"effectivePrice": 2000,
"discountInCents": 0
},
"status": "draft"
},
"timestamp": "2025-11-26T23:56:18.224Z"
}UGC.Listing.Approved
{
"event": "UGC.Listing.Approved",
"data": {
"userRef": "user-00000000-0000-0000-0000-000000000001",
"item": {
"_id": "item_abc123def456",
"gameId": "game_123",
"environment": "test",
"type": "purchase",
"creatorType": "ugc",
"status": "active",
"name": "User Created Armor",
"description": "Custom armor created by user",
"price": 2000,
"creatorAddress": "0x0000000000000000000000000000000000000001",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000010",
"submitIdempotencyKey": "00000000-0000-0000-0000-000000000011",
"reviewIdempotencyKey": "00000000-0000-0000-0000-000000000012"
},
"perUserLimit": 0,
"isVisible": false,
"isPriceVisible": true,
"forSale": false,
"createdAt": "2025-11-26T23:56:18.217Z",
"updatedAt": "2025-11-26T23:56:18.218Z",
"fulfillmentType": "WEBHOOK",
"paymentMethods": [],
"effectivePrice": 2000,
"discountInCents": 0
}
},
"timestamp": "2025-11-26T23:58:20.883Z"
}UGC.Listing.Declined
{
"event": "UGC.Listing.Declined",
"data": {
"userRef": "user-00000000-0000-0000-0000-000000000002",
"item": {
"_id": "item_xyz789ghi012",
"gameId": "game_123",
"environment": "test",
"type": "purchase",
"creatorType": "ugc",
"status": "declined",
"name": "User Created Armor",
"description": "Custom armor created by user",
"price": 2000,
"creatorAddress": "0x0000000000000000000000000000000000000002",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000020",
"submitIdempotencyKey": "00000000-0000-0000-0000-000000000021",
"reviewIdempotencyKey": "00000000-0000-0000-0000-000000000022"
},
"perUserLimit": 0,
"isVisible": false,
"isPriceVisible": true,
"forSale": false,
"createdAt": "2025-11-26T23:58:58.281Z",
"updatedAt": "2025-11-26T23:58:58.281Z",
"fulfillmentType": "WEBHOOK",
"paymentMethods": [],
"effectivePrice": 2000,
"discountInCents": 0
}
},
"timestamp": "2025-11-26T23:59:09.666Z"
}UGC.Listing.DeclinedFinal
{
"event": "UGC.Listing.DeclinedFinal",
"data": {
"userRef": "user-00000000-0000-0000-0000-000000000002",
"item": {
"_id": "item_xyz789ghi012",
"gameId": "game_123",
"environment": "test",
"type": "purchase",
"creatorType": "ugc",
"status": "declined_final",
"name": "User Created Armor",
"description": "Custom armor created by user",
"price": 2000,
"creatorAddress": "0x0000000000000000000000000000000000000002",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000020",
"submitIdempotencyKey": "00000000-0000-0000-0000-000000000021",
"reviewIdempotencyKey": "00000000-0000-0000-0000-000000000022"
},
"perUserLimit": 0,
"isVisible": false,
"isPriceVisible": true,
"forSale": false,
"createdAt": "2025-11-26T23:58:58.281Z",
"updatedAt": "2025-11-26T23:58:58.281Z",
"fulfillmentType": "WEBHOOK",
"paymentMethods": [],
"effectivePrice": 2000,
"discountInCents": 0
}
},
"timestamp": "2025-11-26T23:59:09.666Z"
}UGC.Listing.InProgress
{
"event": "UGC.Listing.InProgress",
"data": {
"userRef": "user-00000000-0000-0000-0000-000000000001",
"item": {
"_id": "item_abc123def456",
"gameId": "game_123",
"environment": "test",
"type": "purchase",
"creatorType": "ugc",
"status": "pending_review",
"name": "User Created Armor",
"description": "Custom armor created by user",
"price": 2000,
"creatorAddress": "0x0000000000000000000000000000000000000001",
"metadata": {
"idempotencyKey": "00000000-0000-0000-0000-000000000010",
"submitIdempotencyKey": "00000000-0000-0000-0000-000000000011"
},
"perUserLimit": 0,
"isVisible": false,
"isPriceVisible": true,
"forSale": false,
"createdAt": "2025-11-26T23:56:18.217Z",
"updatedAt": "2025-11-26T23:56:18.218Z",
"fulfillmentType": "WEBHOOK",
"paymentMethods": [],
"effectivePrice": 2000,
"discountInCents": 0
}
},
"timestamp": "2025-11-26T23:57:15.280Z"
}Campaigns
Campaign.Withdrawal.Completed
{
"event": "Campaign.Withdrawal.Completed",
"data": {
"campaignId": "campaign_abc123def456",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain": "base-sepolia",
"txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Campaign.Started
{
"event": "Campaign.Started",
"data": {
"campaignId": "campaign_abc123def456",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain": "base-sepolia"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Campaign.Stopped
{
"event": "Campaign.Stopped",
"data": {
"campaignId": "campaign_abc123def456",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain": "base-sepolia"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Campaign.Event.Succeeded
{
"event": "Campaign.Event.Succeeded",
"data": {
"campaignId": "campaign_abc123def456",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain": "base-sepolia",
"txHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"eventCode": "signup_bonus",
"eventName": "Sign Up Bonus",
"userRef": "user-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"payoutAmount": 100
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Campaign.Event.Failed
{
"event": "Campaign.Event.Failed",
"data": {
"campaignId": "campaign_abc123def456",
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain": "base-sepolia",
"eventCode": "signup_bonus",
"eventName": "Sign Up Bonus",
"userRef": "user-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"payoutAmount": 100,
"errorMessage": "Insufficient campaign balance"
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Virtual Currency Cashouts
Cashout webhooks are sent when users request to convert virtual currency to base tokens. The lifecycle includes: request → approve/reject → complete (with transaction hash).
Cashout.Requested
Fired when a user creates a cashout request.
{
"event": "Cashout.Requested",
"data": {
"cashoutRequestId": "67856f2a3c8b4e001234abcd",
"currencyId": "67856f1a3c8b4e001234abc1",
"currencyCode": "GOLD",
"currencyName": "Gold Coins",
"userRef": "user-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"unitsRequested": "1000",
"status": "pendingReview",
"requestedRate": {
"baseUnitsPerVcUnit": "1000000000000000000",
"capturedAt": "2024-12-19T10:30:00.000Z"
},
"createdAt": "2024-12-19T10:30:00.000Z",
"updatedAt": "2024-12-19T10:30:00.000Z"
},
"timestamp": "2024-12-19T10:30:00.000Z"
}Cashout.Approved
Fired when a cashout is approved. The virtual currency is burned and converted to base token units. Status changes to approvedPendingPayment.
{
"event": "Cashout.Approved",
"data": {
"cashoutRequestId": "67856f2a3c8b4e001234abcd",
"currencyId": "67856f1a3c8b4e001234abc1",
"currencyCode": "GOLD",
"currencyName": "Gold Coins",
"userRef": "user-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"unitsRequested": "1000",
"status": "approvedPendingPayment",
"requestedRate": {
"baseUnitsPerVcUnit": "1000000000000000000",
"capturedAt": "2024-12-19T10:30:00.000Z"
},
"approved": {
"usedBaseUnitsPerVcUnit": "1000000000000000000",
"convertedBaseUnits": "1000000000000000000000",
"transactionId": "67856f3a3c8b4e001234abc2"
},
"createdAt": "2024-12-19T10:30:00.000Z",
"updatedAt": "2024-12-19T10:35:00.000Z"
},
"timestamp": "2024-12-19T10:35:00.000Z"
}Cashout.Rejected
Fired when a cashout request is rejected. Includes an optional reason.
{
"event": "Cashout.Rejected",
"data": {
"cashoutRequestId": "67856f2a3c8b4e001234abcd",
"currencyId": "67856f1a3c8b4e001234abc1",
"currencyCode": "GOLD",
"currencyName": "Gold Coins",
"userRef": "user-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"unitsRequested": "1000",
"status": "rejected",
"requestedRate": {
"baseUnitsPerVcUnit": "1000000000000000000",
"capturedAt": "2024-12-19T10:30:00.000Z"
},
"reason": "Insufficient verification",
"createdAt": "2024-12-19T10:30:00.000Z",
"updatedAt": "2024-12-19T10:35:00.000Z"
},
"timestamp": "2024-12-19T10:35:00.000Z"
}Cashout.Completed
Fired when a transaction hash is attached to an approved cashout, indicating the on-chain transfer is complete. Status changes to approved.
{
"event": "Cashout.Completed",
"data": {
"cashoutRequestId": "67856f2a3c8b4e001234abcd",
"currencyId": "67856f1a3c8b4e001234abc1",
"currencyCode": "GOLD",
"currencyName": "Gold Coins",
"userRef": "user-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"unitsRequested": "1000",
"status": "approved",
"requestedRate": {
"baseUnitsPerVcUnit": "1000000000000000000",
"capturedAt": "2024-12-19T10:30:00.000Z"
},
"approved": {
"usedBaseUnitsPerVcUnit": "1000000000000000000",
"convertedBaseUnits": "1000000000000000000000",
"transactionId": "67856f3a3c8b4e001234abc2",
"transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
},
"createdAt": "2024-12-19T10:30:00.000Z",
"updatedAt": "2024-12-19T10:40:00.000Z"
},
"timestamp": "2024-12-19T10:40:00.000Z"
}Testing
Webhook.Test
{
"event": "Webhook.Test",
"data": {
"itemId": "item_abc123def456",
"gameId": "game_123",
"itemType": "purchase",
"amount": 0,
"currency": "USD",
"metadata": {}
},
"timestamp": "2024-05-14T12:34:56.000Z"
}Best Practices
- Return 2xx within 10 seconds on success; 4xx/5xx on errors.
- Store the latest processed idempotency key or timestamp to avoid duplicate processing on retries.
- Log request id, signature validity, and response status for debugging.