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:
- Read Partner Authorization Callbacks
- Implement customer credential management
- Handle multiple customer authorizations
For Message Tracking:
- Read Message Status Callbacks
- Implement delivery status tracking
- Build user notification systems
For Both:
- Review Authentication Examples
- Implement proper security measures
- Set up monitoring and alerting
Related Documentation:
- Partner Authorization Callbacks - Customer authorization handling
- Message Status Callbacks - Delivery tracking
- Best Practices - Security and performance guidelines