Skip to main content
Version: Latest

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​

Stuck on an integration?
Write to the developer team with your request ID and we'll get back to you quickly.