Skip to main content

Webhook Examples

This page provides comprehensive examples of how to implement webhook endpoints to receive real-time notifications from the ReviewData Lite API. These examples match the functionality shown in the interactive playground.

Base URL

All API requests are made to: https://data.reviewdata.ai

Webhook Configuration

const axios = require('axios');

const configureWebhook = async (apiKey, webhookUrl) => {
try {
const response = await axios.post('https://data.reviewdata.ai/webhooks/create-webhook/', {
api_key: apiKey,
webhook: webhookUrl
}, {
headers: {
'Content-Type': 'application/json'
}
});

console.log('Webhook configured successfully:');
console.log('Status:', response.data.status);
console.log('Webhook URL:', response.data.webhook);

return response.data;
} catch (error) {
console.error('Error configuring webhook:', error.response?.data || error.message);
throw error;
}
};

// Usage with playground default API key
configureWebhook('YOUR_API_KEY_HERE', 'https://your-domain.com/webhook');

Webhook Endpoint Implementation

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

// Middleware to parse JSON
app.use(express.json());

// Webhook endpoint
app.post('/webhook', async (req, res) => {
try {
const { task_id, publisher_key, foreign_key, profile_key, task_status, reviews_urls } = req.body;

console.log(`Received webhook for task: ${task_id}`);
console.log(`Publisher: ${publisher_key}`);
console.log(`Foreign key: ${foreign_key}`);
console.log(`Task status: ${task_status}`);

// Handle different task statuses
if (task_status === 200) {
await handleTaskCompleted(req.body);
} else if (task_status === 100) {
await handleTaskQueued(req.body);
} else if (task_status === 101) {
await handleTaskInProgress(req.body);
} else if (task_status >= 400) {
await handleTaskFailed(req.body);
} else {
console.warn(`Unknown task status: ${task_status}`);
}

// Return success response
res.status(200).json({ status: 'success', message: 'Webhook processed' });

} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});

// Event handlers
async function handleTaskQueued(data) {
console.log(`Task ${data.task_id} queued for processing`);
console.log(`Publisher: ${data.publisher_key}, Foreign key: ${data.foreign_key}`);

// Update your database with queued status
await updateTaskStatus(data.task_id, 'queued', data);
}

async function handleTaskInProgress(data) {
console.log(`Task ${data.task_id} in progress for ${data.publisher_key}`);

// Update your database with in-progress status
await updateTaskStatus(data.task_id, 'in_progress', data);

// Send notification to users
await sendNotification(`Review collection in progress for ${data.foreign_key}`);
}

async function handleTaskCompleted(data) {
console.log(`Task ${data.task_id} completed successfully`);
console.log(`Reviews URLs: ${data.reviews_urls?.length || 0} files`);

// Store completed task data
const taskData = {
taskId: data.task_id,
foreignKey: data.foreign_key,
publisherKey: data.publisher_key,
profileKey: data.profile_key,
status: 'completed',
taskStatus: data.task_status,
reviewsUrls: data.reviews_urls || [],
completedAt: new Date().toISOString()
};

// Store in database
await storeCompletedTask(taskData);

// Send completion notification
await sendNotification(`Task completed: Reviews available for ${data.foreign_key}`);

// Optionally download and process reviews
if (data.reviews_urls && data.reviews_urls.length > 0) {
await downloadAndProcessReviews(data.reviews_urls, data.task_id);
}
}

async function handleTaskFailed(data) {
console.error(`Task ${data.task_id} failed with status ${data.task_status}`);

// Log error details
await logError({
taskId: data.task_id,
foreignKey: data.foreign_key,
publisherKey: data.publisher_key,
profileKey: data.profile_key,
taskStatus: data.task_status,
timestamp: new Date().toISOString()
});

// Send alert to administrators
await sendAlert(`Task failed: ${data.task_id} (Status: ${data.task_status})`);
}

// Helper functions (implement based on your needs)
async function updateTaskStatus(taskId, status, data) {
console.log(`Updating task ${taskId} status to ${status}`);
// Your database logic here
}

async function storeCompletedTask(taskData) {
console.log('Storing completed task:', taskData);
// Your database logic here
}

async function downloadAndProcessReviews(reviewsUrls, taskId) {
console.log(`Downloading reviews for task ${taskId} from ${reviewsUrls.length} URLs`);
// Your download and processing logic here
}

async function sendNotification(message) {
console.log('Sending notification:', message);
// Your notification logic here
}

async function sendAlert(message) {
console.log('Sending alert:', message);
// Your alerting logic here
}

async function logError(errorData) {
console.error('Logging error:', errorData);
// Your logging logic here
}

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

Webhook Payload Structure

The system sends webhooks with the following payload structure:

{
"task_id": "900e2ff4-2d25-4576-ab18-b9b8e73c0bd6",
"publisher_key": "maps.google.com",
"foreign_key": "TEST_1751004113518_i6yxcl",
"profile_key": "https://www.google.com/maps/place/@40.7094789,-74.0126167,886m/data=!3m2!1e3!5s0x89c2592f4977ef97:0xf78d57398ac93494!4m7!3m6!1s0x89c25a177d4bf5db:0x84e51f23e8c0a75c!8m2!3d40.7094789!4d-74.0100364!10e1!16s%2Fg%2F1thtf190!5m1!1e1?entry=ttu&g_ep=EgoyMDI1MDY",
"task_status": 200,
"reviews_urls": [
"https://prod-data-only-client.s3.amazonaws.com/Review_URLs/9f9d2b5a-085c-4df3-bcca-20928f086925/900e2ff4-2d25-4576-ab18-b9b8e73c0bd6_0.jl"
]
}

Payload Fields

FieldTypeDescription
task_idstringUnique identifier for the task
publisher_keystringReview platform (e.g., "maps.google.com")
foreign_keystringYour unique identifier for this request
profile_keystringURL to the business profile
task_statusintegerCurrent status of the task
reviews_urlsarrayS3 URLs containing review data (present when completed)

Task Status Values

StatusDescription
100Task queued
101Task in progress
200Task completed successfully
400+Task failed

Webhook Examples by Status

Task Queued (Status: 100)

{
"task_id": "900e2ff4-2d25-4576-ab18-b9b8e73c0bd6",
"publisher_key": "maps.google.com",
"foreign_key": "TEST_1751004113518_i6yxcl",
"profile_key": "https://www.google.com/maps/place/@40.7094789,-74.0126167,886m/data=!3m2!1e3!5s0x89c2592f4977ef97:0xf78d57398ac93494!4m7!3m6!1s0x89c25a177d4bf5db:0x84e51f23e8c0a75c!8m2!3d40.7094789!4d-74.0100364!10e1!16s%2Fg%2F1thtf190!5m1!1e1?entry=ttu&g_ep=EgoyMDI1MDY",
"task_status": 100
}

Task In Progress (Status: 101)

{
"task_id": "900e2ff4-2d25-4576-ab18-b9b8e73c0bd6",
"publisher_key": "maps.google.com",
"foreign_key": "TEST_1751004113518_i6yxcl",
"profile_key": "https://www.google.com/maps/place/@40.7094789,-74.0126167,886m/data=!3m2!1e3!5s0x89c2592f4977ef97:0xf78d57398ac93494!4m7!3m6!1s0x89c25a177d4bf5db:0x84e51f23e8c0a75c!8m2!3d40.7094789!4d-74.0100364!10e1!16s%2Fg%2F1thtf190!5m1!1e1?entry=ttu&g_ep=EgoyMDI1MDY",
"task_status": 101
}

Task Completed (Status: 200)

{
"task_id": "900e2ff4-2d25-4576-ab18-b9b8e73c0bd6",
"publisher_key": "maps.google.com",
"foreign_key": "TEST_1751004113518_i6yxcl",
"profile_key": "https://www.google.com/maps/place/@40.7094789,-74.0126167,886m/data=!3m2!1e3!5s0x89c2592f4977ef97:0xf78d57398ac93494!4m7!3m6!1s0x89c25a177d4bf5db:0x84e51f23e8c0a75c!8m2!3d40.7094789!4d-74.0100364!10e1!16s%2Fg%2F1thtf190!5m1!1e1?entry=ttu&g_ep=EgoyMDI1MDY",
"task_status": 200,
"reviews_urls": [
"https://prod-data-only-client.s3.amazonaws.com/Review_URLs/9f9d2b5a-085c-4df3-bcca-20928f086925/900e2ff4-2d25-4576-ab18-b9b8e73c0bd6_0.jl"
]
}

Task Failed (Status: 400+)

{
"task_id": "900e2ff4-2d25-4576-ab18-b9b8e73c0bd6",
"publisher_key": "maps.google.com",
"foreign_key": "TEST_1751004113518_i6yxcl",
"profile_key": "https://www.google.com/maps/place/@40.7094789,-74.0126167,886m/data=!3m2!1e3!5s0x89c2592f4977ef97:0xf78d57398ac93494!4m7!3m6!1s0x89c25a177d4bf5db:0x84e51f23e8c0a75c!8m2!3d40.7094789!4d-74.0100364!10e1!16s%2Fg%2F1thtf190!5m1!1e1?entry=ttu&g_ep=EgoyMDI1MDY",
"task_status": 400
}

Testing Webhooks

Using ngrok for Local Testing

  1. Install ngrok: npm install -g ngrok
  2. Start your local webhook server
  3. Expose your local server: ngrok http 3000
  4. Configure webhook with ngrok URL: https://abc123.ngrok.io/webhook

Webhook Testing Service

// Simple webhook testing server
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
console.log('Received webhook:');
console.log(JSON.stringify(req.body, null, 2));
console.log('Headers:', req.headers);

// Extract webhook data
const { task_id, publisher_key, foreign_key, task_status, reviews_urls } = req.body;

console.log(`Task ID: ${task_id}`);
console.log(`Publisher: ${publisher_key}`);
console.log(`Foreign Key: ${foreign_key}`);
console.log(`Status: ${task_status}`);

if (reviews_urls) {
console.log(`Reviews URLs: ${reviews_urls.length} files`);
}

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

app.listen(3000, () => {
console.log('Webhook test server running on port 3000');
});

Webhook Security Best Practices

1. Use HTTPS

Always use HTTPS for webhook endpoints to ensure data is encrypted in transit.

2. Validate Input Data

function validateWebhookData(data) {
const required = ['task_id', 'publisher_key', 'foreign_key', 'task_status'];

for (const field of required) {
if (!data[field]) {
throw new Error(`Missing required field: ${field}`);
}
}

if (typeof data.task_status !== 'number') {
throw new Error('task_status must be a number');
}

return true;
}

app.post('/webhook', (req, res) => {
try {
validateWebhookData(req.body);
// Process webhook...
} catch (error) {
console.error('Validation error:', error);
return res.status(400).json({ error: error.message });
}
});

3. Handle Idempotency

const processedWebhooks = new Set();

app.post('/webhook', (req, res) => {
const webhookId = `${req.body.task_id}_${req.body.task_status}`;

if (processedWebhooks.has(webhookId)) {
console.log('Webhook already processed:', webhookId);
return res.status(200).json({ status: 'already_processed' });
}

processedWebhooks.add(webhookId);

// Process webhook...

res.status(200).json({ status: 'processed' });
});

4. Implement Retry Logic

app.post('/webhook', async (req, res) => {
try {
await processWebhook(req.body);
res.status(200).json({ status: 'success' });
} catch (error) {
console.error('Webhook processing failed:', error);

// Return 5xx status to trigger retry
res.status(500).json({ error: 'Processing failed' });
}
});

Integration Examples

Database Storage Example

// Using MongoDB
const mongoose = require('mongoose');

const TaskSchema = new mongoose.Schema({
taskId: { type: String, required: true, unique: true },
foreignKey: { type: String, required: true },
publisherKey: { type: String, required: true },
status: { type: String, required: true },
taskStatus: { type: Number, required: true },
reviewsCount: { type: Number, default: 0 },
reviewsUrls: [String],
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});

const Task = mongoose.model('Task', TaskSchema);

async function storeCompletedTask(taskData) {
await Task.findOneAndUpdate(
{ taskId: taskData.taskId },
{
...taskData,
updatedAt: new Date()
},
{ upsert: true }
);
}

Email Notification Example

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransporter({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});

async function sendNotification(message) {
const mailOptions = {
from: process.env.EMAIL_USER,
to: 'admin@yourcompany.com',
subject: 'ReviewData API Notification',
text: message
};

try {
await transporter.sendMail(mailOptions);
console.log('Notification sent:', message);
} catch (error) {
console.error('Failed to send notification:', error);
}
}

Slack Integration Example

const axios = require('axios');

async function sendSlackAlert(message) {
const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL;

if (!slackWebhookUrl) {
console.warn('Slack webhook URL not configured');
return;
}

try {
await axios.post(slackWebhookUrl, {
text: message,
username: 'ReviewData Bot',
icon_emoji: ':robot_face:'
});

console.log('Slack alert sent:', message);
} catch (error) {
console.error('Failed to send Slack alert:', error);
}
}

Error Handling

Common Webhook Errors

  1. Invalid JSON: Malformed webhook payload
  2. Missing Required Fields: Webhook missing expected fields
  3. Database Connection Error: Database unavailable
  4. External Service Error: Email/Slack service unavailable

Error Response Format

app.post('/webhook', (req, res) => {
try {
// Process webhook
res.status(200).json({ status: 'success' });
} catch (error) {
console.error('Webhook error:', error);

// Return appropriate error status
if (error.name === 'ValidationError') {
res.status(400).json({ error: 'Invalid webhook data' });
} else if (error.name === 'DatabaseError') {
res.status(500).json({ error: 'Database unavailable' });
} else {
res.status(500).json({ error: 'Internal server error' });
}
}
});

Monitoring and Logging

Webhook Monitoring

// Track webhook metrics
const webhookMetrics = {
received: 0,
processed: 0,
failed: 0,
processingTime: []
};

app.post('/webhook', async (req, res) => {
const startTime = Date.now();
webhookMetrics.received++;

try {
await processWebhook(req.body);
webhookMetrics.processed++;
res.status(200).json({ status: 'success' });
} catch (error) {
webhookMetrics.failed++;
console.error('Webhook failed:', error);
res.status(500).json({ error: 'Processing failed' });
} finally {
const processingTime = Date.now() - startTime;
webhookMetrics.processingTime.push(processingTime);

// Log metrics periodically
if (webhookMetrics.received % 100 === 0) {
console.log('Webhook metrics:', webhookMetrics);
}
}
});

Best Practices Summary

  1. Use HTTPS: Always use HTTPS for webhook endpoints
  2. Verify Signatures: Implement signature verification for security
  3. Handle Idempotency: Process each webhook only once
  4. Implement Retry Logic: Handle temporary failures gracefully
  5. Monitor Performance: Track webhook processing metrics
  6. Log Everything: Log webhook events for debugging
  7. Handle Errors Gracefully: Return appropriate HTTP status codes
  8. Use Queues: For high-volume webhooks, use message queues
  9. Test Thoroughly: Test webhook handling with various scenarios
  10. Keep Secrets Secure: Store webhook secrets securely

Need Help?

If you encounter issues with webhooks:

  1. Test Configuration: Use the interactive playground to test webhook setup
  2. Check Logs: Review your server logs for webhook processing errors
  3. Verify Endpoint: Ensure your webhook endpoint is accessible via HTTPS
  4. Test Locally: Use ngrok to test webhooks locally during development
  5. Contact Support: Email techsupport@shoutaboutus.com for assistance

Next Steps