Skip to main content
Version: Next

Payload Examples

This page provides complete, real-world examples of webhook payloads for each event type. Use these examples to understand the data structure and build your webhook handlers.

Request Structure

All webhook notifications are sent as HTTP POST requests with the following characteristics:

HTTP Method: POST Content-Type: application/json Authentication: Configured authentication method (Bearer, Basic Auth, API Key, etc.)

Request Headers Example

POST /webhooks/beedeez HTTP/1.1
Host: api.yourcompany.com
Content-Type: application/json
Authorization: Bearer your-secret-bearer-token
X-Webhook-Source: beedeez
Content-Length: 1234

Certificate Completed Event

Triggered when a user successfully completes a certificate.

Event Type

certificate_completed

Complete Payload Example

{
"_id": "507f1f77bcf86cd799439011",
"_userId": "507f191e810c19729de860ea",
"_ownerId": "507f191e810c19729de860eb",
"_badgeId": "507f191e810c19729de860ec",
"creationDate": "2024-11-27T10:30:00.000Z",
"expires": "2025-11-27T10:30:00.000Z",
"disabled": false,
"expiryReminderSent": false,
"_lessonActiveIds": [
"507f191e810c19729de860ed",
"507f191e810c19729de860ee",
"507f191e810c19729de860ef"
],
"ruleData": {
"type": "certification",
"name": "Safety Training Certificate",
"description": "Complete all safety training modules",
"validityDurationDays": 365
},
"createdAt": "2024-11-27T10:30:00.000Z",
"updatedAt": "2024-11-27T10:30:00.000Z",
"__v": 0
}

Field Descriptions

FieldTypeDescription
_idObjectIdUnique identifier for this badge/certificate instance
_userIdObjectIdID of the user who earned the certificate
_ownerIdObjectIdID of the organization/owner
_badgeIdObjectIdID of the badge definition/template
creationDateISO 8601 DateWhen the user completed the certificate requirements
expiresISO 8601 DateWhen the certificate expires (null if no expiration)
disabledBooleanWhether the certificate has been disabled/revoked
expiryReminderSentBooleanWhether an expiry reminder has been sent
_lessonActiveIdsArray\<ObjectId>IDs of lesson activities that validated this certificate
ruleDataObjectAdditional data from the badge rule definition
createdAtISO 8601 DateWhen this certificate record was created in the database
updatedAtISO 8601 DateWhen this certificate record was last updated
__vNumberMongoDB version key

Use Case Example

Scenario: Notify HR system when employee completes safety certification

// Node.js webhook handler
app.post('/webhooks/beedeez/certificates', async (req, res) => {
const certificate = req.body;

if (certificate.ruleData.type === 'certification') {
await hrSystem.updateEmployeeRecord({
employeeId: certificate._userId,
certification: {
name: certificate.ruleData.name,
completedDate: certificate.creationDate,
expiryDate: certificate.expires,
certificateId: certificate._id,
},
});

// Send notification to manager
await notificationService.send({
to: 'manager@company.com',
subject: 'Employee Certification Completed',
body: `Employee completed ${certificate.ruleData.name}`,
});
}

res.status(200).json({ received: true });
});

Certificate Expired Event

Triggered when a certificate reaches its expiration date.

Event Type

certificate_expired

Complete Payload Example

{
"_id": "507f1f77bcf86cd799439011",
"_userId": "507f191e810c19729de860ea",
"_ownerId": "507f191e810c19729de860eb",
"_badgeId": "507f191e810c19729de860ec",
"creationDate": "2023-11-27T10:30:00.000Z",
"expires": "2024-11-27T10:30:00.000Z",
"disabled": false,
"expiryReminderSent": true,
"_lessonActiveIds": ["507f191e810c19729de860ed", "507f191e810c19729de860ee"],
"ruleData": {
"type": "certification",
"name": "First Aid Certification",
"description": "First aid and emergency response training",
"validityDurationDays": 365
},
"createdAt": "2023-11-27T10:30:00.000Z",
"updatedAt": "2024-11-27T10:30:00.000Z",
"__v": 0
}

Field Descriptions

Fields are identical to the certificate_completed event. Key differences:

  • expires date is in the past (certificate has expired)
  • expiryReminderSent is typically true (reminder was sent before expiration)
  • creationDate shows the original completion date (1 year ago in this example)

Use Case Example

Scenario: Trigger re-certification workflow when certificate expires

// Node.js webhook handler
app.post('/webhooks/beedeez/certificates', async (req, res) => {
const certificate = req.body;

// Check if certificate has expired
const now = new Date();
const expiryDate = new Date(certificate.expires);

if (expiryDate < now) {
// Trigger re-certification workflow
await workflowEngine.start({
workflow: 'recertification',
userId: certificate._userId,
certificateId: certificate._badgeId,
certificateName: certificate.ruleData.name,
expiredDate: certificate.expires,
});

// Update compliance tracking
await complianceSystem.markAsExpired({
employeeId: certificate._userId,
certificateId: certificate._id,
requiresRenewal: true,
});

// Send notification to employee and manager
await notificationService.sendExpiredCertificateAlert({
userId: certificate._userId,
certificateName: certificate.ruleData.name,
expiredDate: certificate.expires,
});
}

res.status(200).json({ received: true });
});

Handling Multiple Event Types

If your webhook endpoint handles multiple event types, you can differentiate them using the payload structure or additional metadata.

Example: Multi-Event Handler

app.post('/webhooks/beedeez', async (req, res) => {
const payload = req.body;

// Determine event type based on payload characteristics
const now = new Date();
const expiryDate = payload.expires ? new Date(payload.expires) : null;

let eventType;
if (expiryDate && expiryDate < now) {
eventType = 'certificate_expired';
} else {
eventType = 'certificate_completed';
}

// Route to appropriate handler
switch (eventType) {
case 'certificate_completed':
await handleCertificateCompleted(payload);
break;
case 'certificate_expired':
await handleCertificateExpired(payload);
break;
default:
console.warn('Unknown event type:', eventType);
}

res.status(200).json({ received: true, eventType });
});

async function handleCertificateCompleted(certificate) {
console.log('Certificate completed:', certificate._id);
// Handle completion logic...
}

async function handleCertificateExpired(certificate) {
console.log('Certificate expired:', certificate._id);
// Handle expiration logic...
}

Error Responses

Your webhook endpoint should return appropriate HTTP status codes:

Success Response

HTTP/1.1 200 OK
Content-Type: application/json

{
"received": true,
"message": "Webhook processed successfully",
"eventId": "507f1f77bcf86cd799439011"
}

Error Responses

Authentication Failed:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
"error": "Unauthorized",
"message": "Invalid authentication credentials"
}

Invalid Payload:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
"error": "Bad Request",
"message": "Invalid payload structure"
}

Internal Server Error:

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
"error": "Internal Server Error",
"message": "Failed to process webhook"
}
danger

Only return 2xx status codes if the webhook was successfully received and processed. Returning 2xx prevents Beedeez from retrying the delivery.


Idempotency Handling

Since webhooks may be delivered multiple times, implement idempotency:

Example: Idempotent Handler

const processedWebhooks = new Set(); // In production, use a database

app.post('/webhooks/beedeez', async (req, res) => {
const certificate = req.body;
const webhookId = certificate._id;

// Check if already processed
if (processedWebhooks.has(webhookId)) {
console.log('Duplicate webhook, already processed:', webhookId);
return res.status(200).json({
received: true,
duplicate: true,
});
}

try {
// Process webhook
await processCertificate(certificate);

// Mark as processed
processedWebhooks.add(webhookId);

res.status(200).json({ received: true });
} catch (error) {
console.error('Error processing webhook:', error);
res.status(500).json({ error: 'Processing failed' });
}
});

Database-Based Idempotency

// Using a database to track processed webhooks
app.post('/webhooks/beedeez', async (req, res) => {
const certificate = req.body;
const webhookId = certificate._id;

// Check database
const existing = await db.webhookLogs.findOne({ webhookId });

if (existing) {
return res.status(200).json({
received: true,
duplicate: true,
});
}

try {
// Process webhook
await processCertificate(certificate);

// Store in database
await db.webhookLogs.insert({
webhookId,
receivedAt: new Date(),
payload: certificate,
processed: true,
});

res.status(200).json({ received: true });
} catch (error) {
console.error('Error processing webhook:', error);
res.status(500).json({ error: 'Processing failed' });
}
});

Testing Webhooks

Manual Testing with curl

# Test certificate_completed event
curl -X POST https://your-endpoint.com/webhooks/beedeez \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{
"_id": "507f1f77bcf86cd799439011",
"_userId": "507f191e810c19729de860ea",
"_ownerId": "507f191e810c19729de860eb",
"_badgeId": "507f191e810c19729de860ec",
"creationDate": "2024-11-27T10:30:00.000Z",
"expires": "2025-11-27T10:30:00.000Z",
"disabled": false,
"expiryReminderSent": false,
"_lessonActiveIds": ["507f191e810c19729de860ed"],
"ruleData": {
"type": "certification",
"name": "Test Certificate",
"validityDurationDays": 365
},
"createdAt": "2024-11-27T10:30:00.000Z",
"updatedAt": "2024-11-27T10:30:00.000Z"
}'

Testing with Postman

  1. Create a new POST request
  2. Set URL to your webhook endpoint
  3. Add authentication header (Bearer token, API key, etc.)
  4. Set Content-Type to application/json
  5. Paste example payload in the body
  6. Send request and verify response

Complete Integration Example

Here's a complete, production-ready webhook handler:

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Configuration
const WEBHOOK_TOKEN = process.env.BEEDEEZ_WEBHOOK_TOKEN;
const WEBHOOK_SECRET = process.env.BEEDEEZ_WEBHOOK_SECRET;

// Middleware: Authentication
function authenticateWebhook(req, res, next) {
const authHeader = req.headers.authorization;

if (!authHeader || authHeader !== `Bearer ${WEBHOOK_TOKEN}`) {
return res.status(401).json({ error: 'Unauthorized' });
}

next();
}

// Middleware: Validate payload
function validatePayload(req, res, next) {
const payload = req.body;

if (!payload._id || !payload._userId || !payload._ownerId) {
return res.status(400).json({ error: 'Invalid payload' });
}

next();
}

// Webhook handler
app.post(
'/webhooks/beedeez',
authenticateWebhook,
validatePayload,
async (req, res) => {
const certificate = req.body;

try {
// Determine event type
const now = new Date();
const expiryDate = certificate.expires
? new Date(certificate.expires)
: null;
const isExpired = expiryDate && expiryDate < now;

// Log receipt
console.log('Webhook received:', {
certificateId: certificate._id,
userId: certificate._userId,
eventType: isExpired ? 'expired' : 'completed',
});

// Respond immediately
res.status(200).json({
received: true,
certificateId: certificate._id,
});

// Process asynchronously
if (isExpired) {
await handleExpiredCertificate(certificate);
} else {
await handleCompletedCertificate(certificate);
}
} catch (error) {
console.error('Error processing webhook:', error);
// Still return 200 if we received it successfully
// Log error for investigation
}
},
);

async function handleCompletedCertificate(certificate) {
// Update HR system
await updateHRSystem(certificate);

// Send congratulations email
await sendCongratulationsEmail(certificate);

// Update analytics
await trackCertificateCompletion(certificate);
}

async function handleExpiredCertificate(certificate) {
// Mark as expired in compliance system
await markCertificateExpired(certificate);

// Trigger re-certification workflow
await triggerRecertification(certificate);

// Send renewal reminder
await sendRenewalReminder(certificate);
}

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Webhook server listening on port ${PORT}`);
});

Next Steps