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.
Responding to a notification
Section titled “Responding to a notification”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.
Handling duplicate events
Section titled “Handling duplicate events”Each event will have an unique eventId.
Verifying a notification
Section titled “Verifying a notification”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:
- Create a HMAC signature by concatenating the
eventId,createdAt, andeventproperties from the notification and sign it with your webhook secret. - Compare your HMAC to the content of the
x-webhook-signature-256header. - If the signatures match, process the notification. Otherwise, return a
403 Forbiddenresponse 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 verificationapp.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 variablesconst 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;