Skip to main content

Callbacks & Webhooks Overview

The Communications API uses callbacks (webhooks) to provide real-time updates and notifications. Understanding callback concepts is essential for building responsive integrations.

What Are Callbacks?

Callbacks are HTTP POST requests sent by the Communications API to your specified endpoints when certain events occur. They enable real-time communication between the API and your application without requiring constant polling.

Types of Callbacks

1. Partner Authorization Callbacks

Purpose: Receive customer API keys when customers authorize your partner application
When: Customer completes authorization flow through VenturEd platform
Method: POST request to your partner endpoint URL

Use case: Partner applications managing multiple customers

2. Message Status Callbacks

Purpose: Receive delivery status updates for sent messages
When: Message status changes (queued, sent, delivered, failed, etc.)
Method: POST request to the URL specified in CallbackUrl field

Use case: Real-time delivery tracking and user notifications

Callback Design Principles

Reliability

  • Immediate acknowledgment: Always return 200 OK status
  • Retry mechanism: Failed callbacks may be retried by the system
  • Idempotent handling: Same callback may be received multiple times

Security

  • HTTPS only: All callbacks use secure connections
  • IP verification: Callbacks originate from known VenturEd IP ranges
  • Payload validation: Verify callback structure and content

Performance

  • Fast processing: Keep callback handlers lightweight
  • Async processing: Move heavy work to background queues
  • Error isolation: Don't let callback failures affect main application

Callback URL Patterns

Static URLs

Simple, fixed endpoints for straightforward integrations:

https://your-app.com/api/sms-callback
https://your-app.com/webhooks/partner-auth

Dynamic URLs with Parameters

Include message-specific information in the URL:

https://your-app.com/sms-callback?msgid={sentid}&status={status}
https://your-app.com/customer-auth?customer={customerid}

RESTful Patterns

Use REST conventions for organized callback handling:

https://your-app.com/api/messages/{sentid}/status
https://your-app.com/api/customers/{customerid}/authorization

Common Implementation Patterns

Basic Callback Handler

app.post('/api/callback', (req, res) => {
try {
// Process callback data
const callbackData = req.body;

// Perform your business logic
processCallbackData(callbackData);

// Always acknowledge receipt
res.status(200).send('OK');

} catch (error) {
console.error('Callback processing error:', error);
res.status(200).send('OK'); // Still acknowledge to prevent retries
}
});

Asynchronous Processing

app.post('/api/callback', (req, res) => {
// Acknowledge immediately
res.status(200).send('OK');

// Process asynchronously
setImmediate(() => {
processCallbackAsync(req.body);
});
});

async function processCallbackAsync(callbackData) {
try {
// Heavy processing work
await updateDatabase(callbackData);
await sendNotifications(callbackData);
await updateAnalytics(callbackData);
} catch (error) {
console.error('Async callback processing failed:', error);
// Implement retry or error handling as needed
}
}

Idempotency Handling

const processedCallbacks = new Set();

app.post('/api/callback', (req, res) => {
const callbackData = req.body;
const callbackId = generateCallbackId(callbackData);

if (processedCallbacks.has(callbackId)) {
console.log('Duplicate callback ignored:', callbackId);
return res.status(200).send('OK');
}

processedCallbacks.add(callbackId);
processCallback(callbackData);
res.status(200).send('OK');
});

function generateCallbackId(data) {
// Create unique ID based on callback content
return `${data.MessageId || data.Customerid}-${data.Status || 'auth'}-${data.Timestamp || Date.now()}`;
}

Error Handling Best Practices

Always Acknowledge

app.post('/api/callback', (req, res) => {
// ALWAYS return 200 OK, even if processing fails
res.status(200).send('OK');

try {
processCallback(req.body);
} catch (error) {
// Log error but don't fail the callback
console.error('Callback processing failed:', error);

// Queue for retry or manual intervention
queueFailedCallback(req.body, error);
}
});

Error Recovery

function queueFailedCallback(callbackData, error) {
// Add to retry queue
retryQueue.add('failed-callback', {
callback: callbackData,
error: error.message,
attempt: 1,
maxAttempts: 3
});
}

// Process retry queue
retryQueue.process('failed-callback', async (job) => {
const { callback, attempt, maxAttempts } = job.data;

try {
await processCallback(callback);
} catch (error) {
if (attempt < maxAttempts) {
// Retry with exponential backoff
throw new Error(`Retry attempt ${attempt + 1}`);
} else {
// Send to dead letter queue or alert
console.error('Callback permanently failed:', callback);
}
}
});

Testing and Development

Local Development

Use ngrok or similar tools to expose local endpoints:

# Install ngrok
npm install -g ngrok

# Expose local server
ngrok http 3000

# Use the ngrok URL for callbacks
# https://abc123.ngrok.io/api/callback

Callback Simulation

Create test endpoints to simulate callbacks:

// Test callback simulation
app.post('/test/simulate-callback', (req, res) => {
const { type, data } = req.body;

const callbackUrl = getCallbackUrl(type);

fetch(callbackUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(response => res.json({ success: true, status: response.status }))
.catch(error => res.status(500).json({ error: error.message }));
});

Monitoring and Observability

Callback Metrics

const callbackMetrics = {
total: 0,
successful: 0,
failed: 0,
avgProcessingTime: 0,
byType: {}
};

app.use('/api/callbacks/*', (req, res, next) => {
req.startTime = Date.now();
next();
});

app.use('/api/callbacks/*', (req, res, next) => {
const processingTime = Date.now() - req.startTime;

callbackMetrics.total++;
callbackMetrics.avgProcessingTime =
(callbackMetrics.avgProcessingTime + processingTime) / 2;

if (res.statusCode === 200) {
callbackMetrics.successful++;
} else {
callbackMetrics.failed++;
}
});

// Health check endpoint
app.get('/api/callback-health', (req, res) => {
res.json({
status: 'healthy',
metrics: callbackMetrics,
uptime: process.uptime()
});
});

Next Steps

Choose Your Integration Type

For Partner Integrations:

For Message Tracking:

For Both:


Related Documentation: