Documentation Index Fetch the complete documentation index at: https://docs.nudj.cx/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide walks you through implementing webhook endpoints to receive real-time notifications from the Nudj platform. You’ll learn how to configure webhooks, handle incoming requests, and implement best practices for production systems.
Prerequisites
Admin API Access You need Admin API credentials to configure webhooks
HTTPS Endpoint Webhook URLs must use HTTPS in production
Event Processing A system to handle and process incoming events
Authentication Mechanism to verify webhook authenticity
First, use the Admin API to configure your webhook endpoint:
cURL
JavaScript (Node.js)
Python
curl -X PUT "https://{subdomain}.nudj.cx/api/v2/admin/webhooks/configs" \
-H "Content-Type: application/json" \
-H "x-api-token: YOUR_ADMIN_API_TOKEN" \
-d '[
{
"events": [
"challenge-completion",
"achievement-completion",
"reward-claim"
],
"url": "https://your-app.com/webhooks/nudj",
"httpMethod": "POST",
"isEnabled": true,
"maxRetries": 3,
"headers": {
"Authorization": "Bearer your-webhook-secret",
"X-Webhook-Source": "nudj-platform"
}
}
]'
Step 2: Implement Webhook Endpoint
Create an endpoint to receive and process webhook events:
Node.js (Express)
Python (Flask)
Ruby (Sinatra)
PHP
Go (Gin)
const express = require ( 'express' );
const crypto = require ( 'crypto' );
const app = express ();
// Middleware to parse JSON with raw body for signature verification
app . use ( '/webhooks' , express . json ({
verify : ( req , res , buf , encoding ) => {
req . rawBody = buf ;
}
}));
// Webhook endpoint
app . post ( '/webhooks/nudj' , async ( req , res ) => {
try {
// Verify the webhook is from Nudj
if ( ! verifyWebhookSignature ( req )) {
return res . status ( 401 ). send ( 'Unauthorized' );
}
const event = req . body ;
console . log ( 'Received webhook:' , event . eventSubCategory );
// Process the event
await processWebhookEvent ( event );
// Always respond with 200 for successful processing
res . status ( 200 ). send ( 'OK' );
} catch ( error ) {
console . error ( 'Webhook processing error:' , error );
res . status ( 500 ). send ( 'Internal Server Error' );
}
});
function verifyWebhookSignature ( req ) {
const expectedAuth = `Bearer ${ process . env . WEBHOOK_SECRET } ` ;
const receivedAuth = req . get ( 'Authorization' );
const webhookSource = req . get ( 'X-Webhook-Source' );
return receivedAuth === expectedAuth && webhookSource === 'nudj-platform' ;
}
async function processWebhookEvent ( event ) {
switch ( event . eventSubCategory ) {
case 'challenge-completion' :
await handleChallengeCompletion ( event );
break ;
case 'achievement-completion' :
await handleAchievementCompletion ( event );
break ;
case 'reward-claim' :
await handleRewardClaim ( event );
break ;
default :
console . log ( 'Unhandled event type:' , event . eventSubCategory );
}
}
async function handleChallengeCompletion ( event ) {
const { userId , eventSourceId , payload } = event ;
// Example: Send congratulatory email
await sendEmail ( userId , {
subject: 'Challenge Completed!' ,
template: 'challenge-completion' ,
data: {
challengeName: payload . challengeName ,
pointsEarned: payload . pointsEarned
}
});
// Example: Update external system
await updateExternalProfile ( userId , {
lastChallengeCompleted: eventSourceId ,
totalChallengesCompleted: await getChallengeCount ( userId ) + 1
});
}
app . listen ( 3000 , () => {
console . log ( 'Webhook server listening on port 3000' );
});
Step 3: Implement Event Handlers
Create specific handlers for different event types:
Challenge Completion Handler
Reward Claim Handler
Variable Captured Handler
async function handleChallengeCompletion ( event ) {
const { userId , communityId , eventSourceId , payload } = event ;
const challengeId = eventSourceId ;
try {
// 1. Award bonus points for fast completion
if ( payload . timeToComplete < 3600 ) { // Less than 1 hour
await awardBonusPoints ( userId , 50 , 'Fast challenge completion' );
}
// 2. Update user statistics
await updateUserStats ( userId , {
challengesCompleted: 1 ,
totalPointsEarned: payload . pointsEarned ,
totalXpEarned: payload . xpEarned
});
// 3. Check for achievement eligibility
const challengeCount = await getUserChallengeCount ( userId );
if ( challengeCount === 10 ) {
await triggerAchievement ( userId , 'challenge-master' );
}
// 4. Send personalized notification
await sendNotification ( userId , {
type: 'challenge_completed' ,
title: 'Challenge Complete!' ,
message: `Great job completing " ${ payload . challengeName } "!` ,
data: {
pointsEarned: payload . pointsEarned ,
xpEarned: payload . xpEarned ,
completionTime: formatDuration ( payload . timeToComplete )
}
});
// 5. Update leaderboards
await updateLeaderboard ( communityId , userId , {
challengesCompleted: 1 ,
pointsEarned: payload . pointsEarned
});
} catch ( error ) {
console . error ( `Error handling challenge completion for user ${ userId } :` , error );
throw error ;
}
}
Step 4: Error Handling and Retry Logic
Implement robust error handling to ensure reliable webhook processing:
Error Handling
Idempotency
const MAX_RETRIES = 3 ;
const RETRY_DELAY = 1000 ; // 1 second
async function processWebhookEvent ( event ) {
let attempt = 1 ;
while ( attempt <= MAX_RETRIES ) {
try {
await processEvent ( event );
return ; // Success, exit retry loop
} catch ( error ) {
console . error ( `Attempt ${ attempt } failed:` , error );
if ( attempt === MAX_RETRIES ) {
// Final attempt failed, handle accordingly
await handleFinalFailure ( event , error );
throw error ;
}
// Wait before retrying
await sleep ( RETRY_DELAY * attempt );
attempt ++ ;
}
}
}
async function handleFinalFailure ( event , error ) {
// Log to error monitoring service
console . error ( 'Webhook processing failed after all retries:' , {
eventId: event . id ,
eventType: event . eventSubCategory ,
error: error . message ,
stack: error . stack
});
// Store in dead letter queue for manual review
await storeInDeadLetterQueue ( event , error );
// Alert operations team
await sendAlert ( 'webhook-processing-failed' , {
eventId: event . id ,
eventType: event . eventSubCategory ,
error: error . message
});
}
function sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ));
}
Step 5: Testing Your Implementation
Test your webhook implementation thoroughly:
Testing Script
Testing with ngrok
const axios = require ( 'axios' );
// Test webhook endpoint locally
async function testWebhook () {
const testPayload = {
id: 'test-webhook-' + Date . now (),
eventCategory: 'challenge' ,
eventSubCategory: 'challenge-completion' ,
organisationId: '60f7b8c8e7d4a2b1c9e3f4a1' ,
communityId: '60f7b8c8e7d4a2b1c9e3f4a2' ,
userId: '60f7b8c8e7d4a2b1c9e3f4a3' ,
eventSourceId: '60f7b8c8e7d4a2b1c9e3f4a4' ,
payload: {
challengeId: '60f7b8c8e7d4a2b1c9e3f4a4' ,
challengeName: 'Test Challenge' ,
completedAt: new Date (). toISOString (),
timeToComplete: 1800 ,
pointsEarned: 100 ,
xpEarned: 50
},
createdAt: new Date (). toISOString ()
};
try {
const response = await axios . post ( 'http://localhost:3000/webhooks/nudj' , testPayload , {
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ process . env . WEBHOOK_SECRET } ` ,
'X-Webhook-Source' : 'nudj-platform'
}
});
console . log ( 'Test webhook successful:' , response . status );
} catch ( error ) {
console . error ( 'Test webhook failed:' , error . message );
}
}
testWebhook ();
Step 6: Production Deployment
Security Checklist
Use valid SSL certificates
Enable HSTS headers
Disable HTTP fallback
Use strong cipher suites
Authentication & Authorization
Verify webhook signatures
Use environment variables for secrets
Implement rate limiting
Add IP allowlisting if needed
Implement retry logic
Use dead letter queues
Add comprehensive logging
Set up monitoring and alerts
Monitoring and Logging
Structured Logging
Health Checks
const winston = require ( 'winston' );
const logger = winston . createLogger ({
level: 'info' ,
format: winston . format . combine (
winston . format . timestamp (),
winston . format . errors ({ stack: true }),
winston . format . json ()
),
defaultMeta: { service: 'webhook-handler' },
transports: [
new winston . transports . File ({ filename: 'error.log' , level: 'error' }),
new winston . transports . File ({ filename: 'combined.log' }),
new winston . transports . Console ({
format: winston . format . simple ()
})
]
});
app . post ( '/webhooks/nudj' , async ( req , res ) => {
const startTime = Date . now ();
const event = req . body ;
logger . info ( 'Webhook received' , {
webhookId: event . id ,
eventType: event . eventSubCategory ,
userId: event . userId ,
timestamp: event . createdAt
});
try {
await processWebhookEvent ( event );
logger . info ( 'Webhook processed successfully' , {
webhookId: event . id ,
processingTime: Date . now () - startTime
});
res . status ( 200 ). send ( 'OK' );
} catch ( error ) {
logger . error ( 'Webhook processing failed' , {
webhookId: event . id ,
error: error . message ,
stack: error . stack ,
processingTime: Date . now () - startTime
});
res . status ( 500 ). send ( 'Internal Server Error' );
}
});
Common Integration Patterns
Real-time Notifications
async function sendPushNotification ( userId , event ) {
const userDevices = await getUserDevices ( userId );
const notification = {
title: getNotificationTitle ( event ),
body: getNotificationMessage ( event ),
data: {
eventType: event . eventSubCategory ,
eventId: event . id ,
deepLink: generateDeepLink ( event )
}
};
for ( const device of userDevices ) {
await pushService . send ( device . token , notification );
}
}
function getNotificationTitle ( event ) {
const titles = {
'challenge-completion' : 'Challenge Complete! 🏆' ,
'achievement-completion' : 'Achievement Unlocked! 🎉' ,
'reward-claim' : 'Reward Claimed! 🎁' ,
'streak-extended' : 'Streak Extended! 🔥'
};
return titles [ event . eventSubCategory ] || 'Nudj Notification' ;
}
Email Marketing Integration
async function triggerEmailCampaign ( event ) {
const user = await getUser ( event . userId );
const campaignData = {
email: user . email ,
templateId: getEmailTemplate ( event . eventSubCategory ),
personalizations: [{
email: user . email ,
dynamic_template_data: {
firstName: user . firstName ,
eventData: event . payload ,
communityName: await getCommunityName ( event . communityId )
}
}]
};
await emailService . send ( campaignData );
}
function getEmailTemplate ( eventType ) {
const templates = {
'challenge-completion' : 'challenge_completed_v2' ,
'achievement-completion' : 'achievement_unlocked_v2' ,
'reward-claim' : 'reward_claimed_v1'
};
return templates [ eventType ] || 'generic_notification_v1' ;
}
Analytics Integration
async function trackAnalyticsEvent ( event ) {
const analyticsEvent = {
userId: event . userId ,
eventName: `nudj_ ${ event . eventSubCategory } ` ,
properties: {
organisationId: event . organisationId ,
communityId: event . communityId ,
eventSourceId: event . eventSourceId ,
timestamp: event . createdAt ,
... event . payload
}
};
// Send to multiple analytics platforms
await Promise . all ([
analytics . track ( analyticsEvent ),
mixpanel . track ( analyticsEvent . userId , analyticsEvent . eventName , analyticsEvent . properties ),
amplitude . logEvent ( analyticsEvent )
]);
}
Best Practices
Response Quickly Return HTTP 200 within 10 seconds. Process heavy workloads asynchronously.
Handle Duplicates Use webhook IDs to implement idempotent processing and prevent duplicate handling.
Validate Payloads Always verify webhook authenticity and validate payload structure before processing.
Monitor Performance Track processing times, error rates, and webhook delivery success rates.
Implement Retries Handle transient failures gracefully with exponential backoff retry logic.
Log Everything Maintain detailed logs for debugging and monitoring webhook processing.
Next Steps
Event Catalog See every available webhook event and its specific payload.
Testing Guide Learn how to trigger test events to verify your endpoint.