Skip to content

Webhooks allow you to receive HTTP notifications of events in the Nexus Support-a-Creator system. Nexus sends each event to an endpoint you expose as an HTTP POST request, containing a JSON payload describing the event.

{
"eventId": "1_63OfIMngN5aqHEUixc_",
"createdAt": "2023-02-08 22:56:42.055676",
"eventType": "Bounty",
"eventAction": "BountyReward",
"event": {
"attributionGroupId": "B8b84wld-ohveWtgQPJsS",
"attributionGroupName": "Great Game",
"creatorId": "9jd7RBR_B2BZ0uWO3_lgv",
"bountyId": "tzPlV9PeX9Tsxg158pZ47",
"bountyName": "Refer 5 Players",
"bountyProgressId": "4a3C8DLDjPToZ_BMRrYXx",
"bountyRewards": [
{
"id": "prYjeQugJG1n8qFCjW2rM",
"name": "In Game Title",
"amount": 1,
"externalId": "dalfjkAsdlgJkdal",
"creatorRewardId": "PDKczbm3bmNFZWcrelC5N",
"userId": "55d57cffaa074cc6b8d332f5d77661f2"
}
]
}
}

All notifications will contain eventId, createdAt, eventType, and eventAction. The event field contains the event’s data and will vary based upon the eventAction and eventType combination.

Your response to a webhook request must return a 200 OK status code. If processing of the event will take more than a second or two, consider logging the event and processing it after responding with 200 OK.

Each event will have an unique eventId.

We include a x-webhook-signature-256 header on each notification request which contains a HMAC signature (signed by your webhook secret) of the event payload, prefixed by sha256=.

To verify the notification signature:

  1. Create a HMAC signature by concatenating the eventId, createdAt, and event properties from the notification and sign it with your webhook secret.
  2. Compare your HMAC to the content of the x-webhook-signature-256 header.
  3. If the signatures match, process the notification. Otherwise, return a 403 Forbidden response code.

The following javascript snippet shows a simple implementation of this verification process:

const express = require('express');
const crypto = require('crypto');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 3099;
// Middleware to parse JSON and capture raw body for signature verification
app.use('/webhook', express.raw({ type: 'application/json' }));
app.use(express.json());
// In-memory store for processed event IDs (in production, use a database)
const processedEventIds = new Set();
// Webhook secret from environment variables
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || 'your-webhook-secret-here';
/**
* Create HMAC signature for webhook verification
*/
const getHMac = (eventId, createdAt, event) => {
// Convert the createdAt string to a Date object, then back to ISO string
// This ensures we match the server's exact formatting
const createdAtDate = new Date(createdAt);
const createdAtISO = createdAtDate.toISOString();
const message = eventId + createdAtISO + JSON.stringify(event);
return crypto.createHmac('sha256', WEBHOOK_SECRET)
.update(message)
.digest('hex');
};
/**
* Verify webhook signature
*/
const verifySignature = (req) => {
const { eventId, createdAt, event } = JSON.parse(req.body);
const calculatedHmac = getHMac(eventId, createdAt, event);
const signature = req.header('x-webhook-signature-256');
if (!signature) {
console.log('No signature header found');
return false;
}
const receivedHmac = signature.replace('sha256=', '');
return calculatedHmac === receivedHmac;
};
/**
* Handle GroupManagement NewMember event
*/
const handleNewMember = (eventData) => {
console.log('New member joined');
console.log(`Member ID: ${eventData.memberId}`);
console.log(`Group Code: ${eventData.code}`);
console.log(`Group Name: ${eventData.name}`);
console.log(`Logo Image: ${eventData.logoImage}`);
// Add your business logic here
// For example: update database, send notifications, etc.
};
/**
* Handle GroupManagement RemovedMember event
*/
const handleRemovedMember = (eventData) => {
console.log('Member removed');
console.log(`Member ID: ${eventData.memberId}`);
console.log(`Group Code: ${eventData.code}`);
console.log(`Removal Date: ${eventData.removalDate}`);
console.log(`Removal Reason: ${eventData.removalReason}`);
// Add your business logic here
// For example: update database, send notifications, etc.
};
/**
* Main webhook handler
*/
app.post('/webhook', (req, res) => {
try {
// Parse the JSON body
const payload = JSON.parse(req.body);
console.log(payload)
const { eventId, createdAt, eventType, eventAction, event } = payload;
console.log(`Received webhook: ${eventType}:${eventAction} (ID: ${eventId})`);
// Check for duplicate events
if (processedEventIds.has(eventId)) {
console.log(`Duplicate event ignored: ${eventId}`);
return res.status(200).json({ message: 'Event already processed' });
}
// Verify signature (optional but recommended)
if (WEBHOOK_SECRET !== 'your-webhook-secret-here' && !verifySignature(req)) {
console.log('Signature verification failed');
return res.status(403).json({ error: 'Invalid signature' });
}
// Process GroupManagement events
if (eventType === 'GroupManagement') {
switch (eventAction) {
case 'NewMember':
handleNewMember(event);
break;
case 'RemovedMember':
handleRemovedMember(event);
break;
default:
console.log(`Unhandled GroupManagement action: ${eventAction}`);
}
} else {
console.log(`Unhandled event type: ${eventType}`);
}
// Mark event as processed
processedEventIds.add(eventId);
// Respond with 200 OK
res.status(200).json({
message: 'Webhook processed successfully',
eventId: eventId,
eventType: eventType,
eventAction: eventAction
});
} catch (error) {
console.error('Error processing webhook:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
/**
* Root endpoint with basic info
*/
app.get('/', (req, res) => {
res.json({
message: 'Nexus Support-a-Creator Webhook Server',
endpoints: {
webhook: 'POST /webhook',
},
supportedEvents: {
GroupManagement: ['NewMember', 'RemovedMember']
}
});
});
/**
* Start the server
*/
app.listen(port, () => {
console.log(`Webhook server running on port ${port}`);
console.log(`Webhook endpoint: http://localhost:${port}/webhook`);
if (WEBHOOK_SECRET === 'your-webhook-secret-here') {
console.log('Warning: Using default webhook secret. Set WEBHOOK_SECRET environment variable for production.');
}
});
module.exports = app;