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.