Webhooks Overview
Webhooks enable real-time communication between Dasha BlackBox and your application. Instead of constantly polling for updates, your application receives instant HTTP notifications when specific events occur—call completions, failures, agent tool invocations, and more. This event-driven architecture powers seamless integrations with CRMs, analytics platforms, notification systems, and custom workflows.
What are Webhooks?
Webhooks are HTTP callbacks that Dasha BlackBox sends to your application when events occur. Think of them as “reverse API calls”—instead of your application requesting data from Dasha BlackBox, Dasha BlackBox proactively pushes event data to your endpoints.
Core Characteristics:
- Event-Driven: Triggered automatically when specific events occur
- HTTP POST Requests: Standard RESTful JSON payloads
- Real-Time Delivery: Notifications sent immediately after events
- Configurable: Choose which events to receive and where to send them
- Reliable: Built-in retry logic with exponential backoff
How Webhooks Work
The webhook lifecycle follows a simple pattern:
Step-by-Step Flow:
- Event Occurs: Something happens in Dasha BlackBox (call ends, agent updated, tool called)
- Webhook Triggered: Event system determines which webhooks to notify
- Request Assembled: Dasha BlackBox constructs HTTP POST request with event payload
- Delivery Attempted: Request sent to your configured endpoint URL
- Your Endpoint Responds: Your application processes the event and returns HTTP 200
- Acknowledgment: Dasha BlackBox logs successful delivery
If your endpoint doesn’t respond with success (200-299 status), Dasha BlackBox retries with exponential backoff.
Why Use Webhooks?
Real-Time Integration
Eliminate Polling Overhead
- No need to repeatedly check for updates
- Reduce API call volume by over 95%
- Lower server load and bandwidth costs
- Instant notifications within milliseconds
Immediate Response
- Trigger actions the moment events occur
- Update dashboards in real-time
- Send notifications without delay
- Enable time-sensitive workflows
Seamless System Integration
CRM Synchronization
- Automatically create or update customer records
- Track call history and outcomes
- Update lead scores based on conversation quality
- Sync contact information bidirectionally
Analytics and Reporting
- Feed call data into analytics platforms
- Generate real-time dashboards
- Track conversion metrics
- Monitor agent performance
Workflow Automation
- Trigger follow-up emails after calls
- Create support tickets for failed calls
- Schedule appointments automatically
- Route leads to sales teams
Enhanced Monitoring
System Health
- Track call success rates
- Monitor error patterns
- Detect service degradation early
- Generate uptime reports
Business Intelligence
- Analyze conversation trends
- Identify optimization opportunities
- Measure customer satisfaction
- Track operational KPIs
Webhook Types in Dasha BlackBox
Dasha BlackBox supports two distinct webhook categories with different purposes and behaviors.
Event Webhooks
Event webhooks notify you about call lifecycle events and system activities.
Call Lifecycle Events (WebHookPayloadType):
- StartWebHookPayload - Pre-call webhook for outbound call initiated or inbound call received
- CompletedWebHookPayload - Call finished successfully with full transcript and analysis
- FailedWebHookPayload - Call encountered error or was rejected
- CallDeadLineWebHookPayload - Call cancelled due to timeout or scheduling deadline
- TransferWebHookPayload - Transfer decision requested during call
Configuration Location: Agent’s “Webhooks” tab
Timeout: 30 seconds (fixed)
Response Usage: Acknowledgment only (not used in conversation)
Common Use Cases:
- Updating CRM records with call outcomes
- Sending notifications to support teams
- Triggering analytics pipelines
- Generating audit logs
Tool webhooks execute specific functions called by the agent during conversations.
Purpose:
- Enable agents to interact with external systems
- Query databases, APIs, or services
- Perform actions like booking appointments
- Retrieve real-time information
Configuration Location: Agent’s “Tools” tab
Timeout: Configurable per tool (1-60 seconds)
Response Usage: Result incorporated into conversation by LLM
Common Use Cases:
- Looking up customer information
- Checking appointment availability
- Searching knowledge bases
- Updating support tickets
See Tools & Functions for detailed tool webhook documentation.
Event Types Reference
StartWebHookPayload (Call Started)
Sent when an outbound call is initiated or an inbound call is received. This is a pre-call webhook that allows you to accept/reject the call.
Event Payload
Response Requirements
Use Cases
{
"type": "StartWebHookPayload",
"status": "Queued",
"callId": "call_abc123def456",
"agentId": "agent_xyz789",
"orgId": "org_123abc",
"endpoint": "+14155551234",
"callAdditionalData": {
"customField": "value"
},
"agentAdditionalData": {},
"sip": {
"fromUser": "+14155551234",
"fromDomain": "sip.provider.com",
"toUser": "agent-line",
"toDomain": "blackbox.dasha.ai"
},
"originalAgentConfig": {
"primaryLanguage": "en-US",
"llmConfig": { "vendor": "openai", "model": "gpt-4.1-mini" }
}
}
For Start Webhooks (configured in agent settings), you can control call acceptance:{
"accept": true,
"customPrompt": "Hello! I'm calling from Acme Corp. How can I help you today?"
}
Or reject the call:{
"accept": false,
"reasonMessage": "Outside business hours"
}
For regular event webhooks, simply return HTTP 200. Dynamic Call Routing
- Accept/reject calls based on business logic
- Customize greeting based on customer data
- Route to different agents based on time/availability
Tracking and Analytics
- Record call initiation in analytics system
- Start timing metrics
- Update dashboard with active call count
Integration Triggers
- Notify team members of incoming calls
- Retrieve customer context from CRM
- Prepare resources for call handling
CompletedWebHookPayload (Call Completed)
Sent when a call finishes successfully with full conversation data.
Event Payload
Processing Example
Use Cases
{
"type": "CompletedWebHookPayload",
"status": "Completed",
"callId": "call_abc123def456",
"agentId": "agent_xyz789",
"orgId": "org_123abc",
"endpoint": "+14155551234",
"callType": "OutboundAudio",
"callAdditionalData": {},
"agentAdditionalData": {},
"createdTime": "2025-10-20T15:30:00.000Z",
"completedTime": "2025-10-20T15:35:00.000Z",
"durationSeconds": 312,
"inspectorUrl": "https://blackbox.dasha.ai/calls/call_abc123def456",
"recordingUrl": "https://storage.blackbox.dasha.ai/recordings/call_abc123def456.mp3",
"result": {
"finishReason": "user_hangup",
"status": "success",
"postCallAnalysis": {
"sentiment": "positive",
"satisfactionScore": 8.5,
"callOutcome": "appointment_booked",
"extractedInfo": {
"appointmentDate": "2025-10-25",
"customerName": "John Doe",
"contactEmail": "john@example.com"
}
},
"transferInformation": []
},
"transcription": [
{
"speaker": "assistant",
"text": "Hello! How can I help you today?",
"startTime": "2025-10-20T15:30:05.000Z",
"endTime": "2025-10-20T15:30:08.000Z"
},
{
"speaker": "user",
"text": "I'd like to schedule an appointment.",
"startTime": "2025-10-20T15:30:09.000Z",
"endTime": "2025-10-20T15:30:12.000Z"
}
]
}
app.post('/webhooks/blackbox', async (req, res) => {
const payload = req.body;
if (payload.type === 'CompletedWebHookPayload') {
// Extract key information
const { callId, result, transcription } = payload;
const { postCallAnalysis } = result;
// Update CRM with call outcome
await crm.updateContact({
phone: payload.endpoint,
lastCallDate: payload.completedTime,
sentiment: postCallAnalysis.sentiment,
satisfactionScore: postCallAnalysis.satisfactionScore,
notes: extractSummary(transcription)
});
// Trigger follow-up workflow
if (postCallAnalysis.callOutcome === 'appointment_booked') {
await sendConfirmationEmail(
postCallAnalysis.extractedInfo.contactEmail,
postCallAnalysis.extractedInfo.appointmentDate
);
}
// Log to analytics
await analytics.trackEvent('call_completed', {
agentId: payload.agentId,
duration: payload.durationSeconds,
outcome: postCallAnalysis.callOutcome
});
}
res.status(200).json({ received: true });
});
CRM Integration
- Create or update customer records
- Log conversation transcripts
- Update lead scores and statuses
- Track customer sentiment
Analytics and Reporting
- Calculate call success rates
- Measure average handling time
- Analyze sentiment trends
- Generate performance reports
Workflow Automation
- Send follow-up emails or SMS
- Create support tickets for unresolved issues
- Schedule appointments in calendar systems
- Trigger next steps in sales funnel
FailedWebHookPayload (Call Failed)
Sent when a call encounters an error or cannot be completed.
Event Payload
Error Categories
Use Cases
{
"type": "FailedWebHookPayload",
"status": "Failed",
"callId": "call_abc123def456",
"agentId": "agent_xyz789",
"orgId": "org_123abc",
"endpoint": "+14155551234",
"callType": "OutboundAudio",
"callAdditionalData": {},
"agentAdditionalData": {},
"createdTime": "2025-10-20T15:30:00.000Z",
"completedTime": "2025-10-20T15:31:00.000Z",
"errorMessage": "Call was not answered within timeout period",
"inspectorUrl": "https://blackbox.dasha.ai/calls/call_abc123def456"
}
Recipient Unavailable
BUSY - Line is busy
NO_ANSWER - No response within timeout
DECLINED - Call rejected by recipient
UNREACHABLE - Phone number unreachable
Configuration Errors
INVALID_NUMBER - Malformed phone number
INVALID_AGENT - Agent not found or disabled
MISSING_CONFIGURATION - Required settings missing
System Errors
NETWORK_ERROR - Network connectivity issue
SERVICE_UNAVAILABLE - Telephony service down
TIMEOUT - Call setup exceeded timeout
INTERNAL_ERROR - Unexpected system error
Retry Logic
- Schedule automatic retries for retryable errors
- Implement backoff strategies
- Track retry attempts
Alerting and Monitoring
- Notify support team of system errors
- Alert on high failure rates
- Create incident tickets
Customer Communication
- Send SMS if call fails
- Schedule callback requests
- Update customer on status
CallDeadLineWebHookPayload (Call Deadline)
Sent when a scheduled call is cancelled due to timeout or deadline exceeded.
{
"type": "CallDeadLineWebHookPayload",
"status": "Canceled",
"callId": "call_abc123def456",
"agentId": "agent_xyz789",
"orgId": "org_123abc",
"endpoint": "+14155551234",
"callType": "OutboundAudio",
"callAdditionalData": {
"campaignId": "Q4_outreach"
},
"agentAdditionalData": {},
"createdTime": "2025-10-20T15:00:00.000Z",
"completedTime": "2025-10-20T18:00:00.000Z",
"reasonMessage": "Call deadline expired before execution"
}
Campaign Management
- Mark contacts as unreachable
- Remove from active campaign
- Schedule for different time window
Reporting
- Track deadline hit rates
- Analyze optimal call windows
- Measure campaign effectiveness
Follow-up Actions
- Send fallback communication (email/SMS)
- Add to manual outreach queue
- Flag for review
Sent when an agent invokes a tool during a conversation.
Event Payload
Request Format
Response Requirements
{
"type": "ToolWebHookPayload",
"status": "Running",
"callId": "call_abc123def456",
"agentId": "agent_xyz789",
"orgId": "org_123abc",
"endpoint": "+14155551234",
"callAdditionalData": {},
"agentAdditionalData": {},
"toolName": "check_availability",
"arguments": {
"date": "2025-10-25",
"service": "consultation"
}
}
Tool webhooks receive a specific format designed for function execution:POST https://api.example.com/tools/check-availability
Content-Type: application/json
Authorization: Bearer your-tool-api-key
X-Dasha BlackBox-Call-Id: call_abc123def456
X-Dasha BlackBox-Tool-Name: check_availability
{
"toolName": "check_availability",
"toolArguments": {
"date": "2025-10-25",
"service": "consultation"
},
"callId": "call_abc123def456",
"agentId": "agent_xyz789",
"timestamp": "2025-10-20T15:30:15Z"
}
Tool webhooks must return results that the agent can use in conversation:Success Response:{
"available": true,
"slots": ["10:00 AM", "2:00 PM", "4:00 PM"],
"nextAvailable": "2025-10-25"
}
Error Response:{
"error": "Date is in the past",
"errorCode": "INVALID_DATE",
"validRange": {
"start": "2025-10-20",
"end": "2026-01-20"
}
}
The agent uses this data to continue the conversation naturally.
Common Use Cases
CRM Integration
Automatically sync call data with your CRM system to maintain complete customer interaction history.
Implementation Pattern:
// Express.js webhook handler
app.post('/webhooks/blackbox-events', async (req, res) => {
const payload = req.body;
try {
switch(payload.type) {
case 'CompletedWebHookPayload':
await updateCRMRecord(payload);
break;
case 'FailedWebHookPayload':
await logFailedAttempt(payload);
break;
case 'CallDeadLineWebHookPayload':
await markAsUnreachable(payload);
break;
}
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
async function updateCRMRecord(event) {
const { endpoint, result } = event;
const { postCallAnalysis, transcript } = result;
// Find or create contact
const contact = await crm.findByPhone(endpoint) ||
await crm.createContact({ phone: endpoint });
// Update with call data
await crm.updateContact(contact.id, {
lastContactDate: event.completedTime,
sentiment: postCallAnalysis.sentiment,
satisfactionScore: postCallAnalysis.satisfactionScore,
lastTranscript: formatTranscript(transcript),
tags: extractKeywords(postCallAnalysis)
});
}
Benefits:
- Complete conversation history in CRM
- Automatic lead scoring updates
- Sentiment tracking over time
- No manual data entry required
Real-Time Analytics Dashboard
Update analytics dashboards immediately as calls complete.
Architecture:
Implementation:
// Webhook processor
async function processCallEvent(event) {
// Store in time-series database
await metricsDB.insert({
timestamp: event.timestamp,
agentId: event.agentId,
duration: event.duration,
outcome: event.result?.postCallAnalysis?.callOutcome,
sentiment: event.result?.postCallAnalysis?.sentiment
});
// Publish to real-time dashboard
await pubsub.publish('dashboard-updates', {
type: 'call_completed',
metrics: calculateRealTimeMetrics(event)
});
}
function calculateRealTimeMetrics(event) {
return {
totalCalls: incrementCounter('total_calls'),
avgDuration: updateAverage('call_duration', event.duration),
successRate: calculateSuccessRate(),
sentimentDistribution: updateSentimentCounts(event)
};
}
Dashboard Features:
- Real-time call volume charts
- Live sentiment analysis
- Success rate tracking
- Agent performance metrics
Automated Workflow Triggers
Trigger complex workflows based on call outcomes.
Example: Appointment Booking Flow
app.post('/webhooks/blackbox', async (req, res) => {
const payload = req.body;
if (payload.type === 'CompletedWebHookPayload') {
const outcome = payload.result.postCallAnalysis?.callOutcome;
// Trigger workflow based on outcome
switch(outcome) {
case 'appointment_booked':
await workflows.appointmentBooked(payload);
break;
case 'needs_callback':
await workflows.scheduleCallback(payload);
break;
case 'not_interested':
await workflows.updateLeadStatus(payload, 'disqualified');
break;
case 'information_provided':
await workflows.sendFollowUpEmail(payload);
break;
}
}
res.status(200).json({ received: true });
});
// Workflow definitions
const workflows = {
async appointmentBooked(event) {
const info = event.result.postCallAnalysis.extractedInfo;
// Create calendar event
await calendar.createEvent({
title: `Appointment - ${info.customerName}`,
date: info.appointmentDate,
time: info.appointmentTime,
attendees: [info.contactEmail]
});
// Send confirmation email
await email.send({
to: info.contactEmail,
template: 'appointment-confirmation',
data: info
});
// Update CRM
await crm.updateContact({
email: info.contactEmail,
nextAppointment: info.appointmentDate,
status: 'appointment_scheduled'
});
},
async scheduleCallback(event) {
const preferredTime = event.result.postCallAnalysis.extractedInfo.preferredCallbackTime;
// Schedule new call
await blackbox.scheduleCall({
agentId: event.agentId,
endpoint: event.endpoint,
scheduledTime: preferredTime,
priority: 2 // Higher priority for callbacks
});
// Notify sales team
await slack.sendMessage({
channel: '#callbacks',
text: `Callback requested for ${event.endpoint} at ${preferredTime}`
});
}
};
Multi-System Notifications
Send notifications across multiple channels based on call events.
async function notifyTeams(payload) {
const { type, agentId } = payload;
// Determine notification urgency
const isUrgent = type === 'FailedWebHookPayload' &&
payload.errorMessage?.includes('system');
// Slack notifications
if (isUrgent) {
await slack.sendMessage({
channel: '#alerts',
text: `⚠️ System error detected: ${event.errorMessage}`,
priority: 'high'
});
}
// Email notifications
if (type === 'CompletedWebHookPayload' &&
payload.result.postCallAnalysis.sentiment === 'negative') {
await email.send({
to: 'support@company.com',
subject: 'Negative sentiment call alert',
body: generateCallSummary(payload)
});
}
// SMS notifications for high-value outcomes
if (payload.result?.postCallAnalysis?.extractedInfo?.dealValue > 10000) {
await sms.send({
to: '+14155550123',
message: `High-value lead captured: ${payload.endpoint}`
});
}
}
Webhook Security
Protecting your webhook endpoints is critical for preventing unauthorized access and ensuring data integrity.
HTTPS Requirements
Always use HTTPS for webhook endpoints. Dasha BlackBox will reject HTTP URLs in production environments.
Why HTTPS is Required:
- Prevents man-in-the-middle attacks
- Protects sensitive call data in transit
- Ensures payload integrity
- Required for PCI/HIPAA compliance
Certificate Requirements:
- Valid SSL/TLS certificate from trusted CA
- Certificate must not be expired
- No self-signed certificates in production
- Support TLS 1.2 or higher
Authentication Methods
Include authentication tokens in webhook headers:
{
"url": "https://api.example.com/webhooks/blackbox",
"headers": {
"Authorization": "Bearer sk_live_abc123xyz789",
"X-API-Key": "your-webhook-secret-key"
}
}
Best Practices:
- Rotate API keys regularly (every 90 days)
- Use different keys for different environments
- Store keys in secure vault (not in code)
- Revoke compromised keys immediately
Request Validation
Verify that requests actually come from Dasha BlackBox:
const crypto = require('crypto');
function validateWebhook(req, secret) {
// Verify signature header
const signature = req.headers['x-blackbox-signature'];
if (!signature) {
throw new Error('Missing signature header');
}
// Reconstruct signature
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// Compare signatures
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)) {
throw new Error('Invalid signature');
}
return true;
}
// Use in webhook handler
app.post('/webhooks/blackbox', (req, res) => {
try {
validateWebhook(req, process.env.WEBHOOK_SECRET);
// Process webhook...
} catch (error) {
console.error('Webhook validation failed:', error);
return res.status(401).json({ error: 'Unauthorized' });
}
});
IP Whitelisting
Restrict webhook delivery to known Dasha BlackBox IP addresses:
# Nginx configuration
location /webhooks/blackbox {
# Allow Dasha BlackBox IP ranges
allow 52.6.0.0/16;
allow 54.88.0.0/16;
deny all;
proxy_pass http://app_server;
}
Contact Dasha BlackBox support for the current list of webhook delivery IP addresses.
Data Protection
Sensitive Data Handling:
- Never log full webhook payloads containing PII
- Encrypt webhook data at rest in your database
- Implement data retention policies
- Comply with GDPR, CCPA, and relevant regulations
Example: Secure Logging
function logWebhookEvent(event) {
// Log metadata only, not sensitive data
logger.info('Webhook received', {
eventType: event.eventType,
callId: event.callId,
agentId: event.agentId,
timestamp: event.timestamp,
// Don't log: transcript, phone numbers, extracted info
});
}
Webhook Lifecycle
Understanding the complete webhook lifecycle helps you build reliable integrations.
Configuration
Dashboard Configuration:
- Navigate to agent settings
- Click Webhooks tab
- Click Add Webhook Endpoint
- Configure:
- URL:
https://api.example.com/webhooks/blackbox
- Events: Select events to receive
- Headers: Add authentication
- Timeout: Set response timeout (default: 30s)
- Click Test Endpoint to verify
- Save configuration
API Configuration:
const agent = await fetch('https://blackbox.dasha.ai/api/v1/agents', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Support Agent',
resultWebhook: {
url: 'https://api.example.com/webhooks/call-results',
headers: {
'Authorization': 'Bearer webhook-secret-key'
}
}
})
});
Event Trigger
When an event occurs, Dasha BlackBox:
- Identifies Subscriptions: Finds all webhooks configured for this event type
- Assembles Payload: Constructs JSON payload with event data
- Queues Delivery: Adds webhook delivery task to queue
- Rate Limits: Applies rate limiting per endpoint
Delivery
Dasha BlackBox attempts to deliver the webhook:
Request Format:
POST https://api.example.com/webhooks/blackbox
Content-Type: application/json
User-Agent: Dasha BlackBox-Webhooks/1.0
X-Dasha BlackBox-Event: call.completed
X-Dasha BlackBox-Delivery-ID: delivery_abc123
X-Dasha BlackBox-Signature: sha256=abc123...
Authorization: Bearer your-configured-api-key
{
"eventType": "call.completed",
"timestamp": "2025-10-20T15:35:00Z",
// ... event data
}
Your Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"received": true
}
Return HTTP 200-299 to acknowledge receipt. Any other status code triggers a retry.
Retry Logic
If delivery fails, Dasha BlackBox retries with exponential backoff:
Retry Schedule:
| Attempt | Delay | Cumulative Time |
|---|
| 1 (initial) | 0s | 0s |
| 2 | 5s | 5s |
| 3 | 25s | 30s |
| 4 | 125s | 155s (~2.5 min) |
| 5 | 625s | 780s (~13 min) |
| 6 | 3125s | 3905s (~65 min) |
Retry Conditions:
- HTTP status 5xx (server errors)
- HTTP status 429 (rate limiting)
- Network timeout
- Connection refused
- DNS resolution failure
No Retry Conditions:
- HTTP status 2xx (success)
- HTTP status 4xx except 429 (client errors)
- Invalid URL configuration
- SSL/TLS certificate errors
After maximum retries, the webhook delivery is marked as failed and logged for investigation.
Best Practices
Endpoint Design
Fast Response Times
Respond within 5 seconds to avoid timeouts and delays in the webhook queue.
// ✅ Good: Acknowledge immediately, process async
app.post('/webhooks/blackbox', async (req, res) => {
// Acknowledge receipt immediately
res.status(200).json({ received: true });
// Process async (don't block response)
processWebhookAsync(req.body).catch(err => {
logger.error('Webhook processing error:', err);
});
});
async function processWebhookAsync(event) {
// Long-running tasks here
await updateCRM(event);
await sendNotifications(event);
await generateAnalytics(event);
}
// ❌ Bad: Slow synchronous processing
app.post('/webhooks/blackbox', async (req, res) => {
// Don't wait for slow operations before responding
await updateCRM(req.body); // Might take 3+ seconds
await sendEmails(req.body); // Might take 2+ seconds
res.status(200).json({ received: true }); // Too slow!
});
Idempotent Processing
Design your webhook handler to handle duplicate deliveries gracefully:
async function processWebhook(event) {
const { callId, eventType } = event;
// Check if already processed
const existing = await db.webhookEvents.findOne({
callId,
eventType,
deliveryId: event.deliveryId
});
if (existing) {
console.log('Duplicate webhook delivery, skipping');
return;
}
// Process event
await processEvent(event);
// Record processing
await db.webhookEvents.insert({
callId,
eventType,
deliveryId: event.deliveryId,
processedAt: new Date()
});
}
Error Handling
Implement comprehensive error handling:
app.post('/webhooks/blackbox', async (req, res) => {
try {
// Validate request
if (!req.body.eventType) {
return res.status(400).json({ error: 'Missing eventType' });
}
// Acknowledge immediately
res.status(200).json({ received: true });
// Process with error handling
await processWebhookAsync(req.body);
} catch (error) {
console.error('Webhook handler error:', error);
// Still return 200 if already responded
if (!res.headersSent) {
res.status(500).json({ error: 'Internal error' });
}
// Alert monitoring system
await monitoring.alert({
severity: 'error',
message: 'Webhook processing failed',
error: error.message
});
}
});
Security Practices
Validate All Input
const Joi = require('joi');
const webhookSchema = Joi.object({
eventType: Joi.string().required(),
timestamp: Joi.string().isoDate().required(),
callId: Joi.string().required(),
agentId: Joi.string().required(),
// ... define full schema
});
function validateWebhookPayload(payload) {
const { error, value } = webhookSchema.validate(payload);
if (error) {
throw new Error(`Invalid webhook payload: ${error.message}`);
}
return value;
}
Rate Limiting
Protect your endpoint from abuse:
const rateLimit = require('express-rate-limit');
const webhookLimiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
max: 100, // Max 100 webhooks per minute
message: 'Too many webhook requests'
});
app.post('/webhooks/blackbox', webhookLimiter, handleWebhook);
Monitor and Alert
Track webhook health:
const metrics = {
received: 0,
processed: 0,
failed: 0,
avgProcessingTime: 0
};
async function processWebhookAsync(event) {
const startTime = Date.now();
metrics.received++;
try {
await processEvent(event);
metrics.processed++;
} catch (error) {
metrics.failed++;
// Alert if failure rate exceeds threshold
const failureRate = metrics.failed / metrics.received;
if (failureRate > 0.05) { // Over 5% failure rate
await alerting.notify({
type: 'webhook_failure_rate_high',
rate: failureRate,
metrics
});
}
} finally {
const duration = Date.now() - startTime;
metrics.avgProcessingTime =
(metrics.avgProcessingTime * (metrics.received - 1) + duration) /
metrics.received;
}
}
Getting Started
Step 1: Set Up Your Endpoint
Create a webhook handler in your application:
Node.js (Express)
Python (Flask)
Go
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/blackbox', async (req, res) => {
const event = req.body;
console.log('Webhook received:', event.eventType);
// Acknowledge receipt
res.status(200).json({ received: true });
// Process async
processEvent(event).catch(console.error);
});
async function processEvent(payload) {
switch(payload.type) {
case 'CompletedWebHookPayload':
await handleCallCompleted(payload);
break;
case 'FailedWebHookPayload':
await handleCallFailed(payload);
break;
// ... handle other payload types
}
}
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
from flask import Flask, request, jsonify
import asyncio
app = Flask(__name__)
@app.route('/webhooks/blackbox', methods=['POST'])
def webhook_handler():
event = request.json
print(f"Webhook received: {event['eventType']}")
# Acknowledge receipt
response = jsonify({'received': True})
# Process async
asyncio.create_task(process_event(event))
return response, 200
async def process_event(payload):
payload_type = payload['type']
if payload_type == 'CompletedWebHookPayload':
await handle_call_completed(payload)
elif payload_type == 'FailedWebHookPayload':
await handle_call_failed(payload)
# ... handle other payload types
if __name__ == '__main__':
app.run(port=3000)
package main
import (
"encoding/json"
"log"
"net/http"
)
type WebhookPayload struct {
Type string `json:"type"`
Status string `json:"status"`
CallID string `json:"callId"`
AgentID string `json:"agentId"`
OrgID string `json:"orgId"`
// ... other fields
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
var payload WebhookPayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("Webhook received: %s", payload.Type)
// Acknowledge receipt
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{"received": true})
// Process async
go processEvent(payload)
}
func processEvent(payload WebhookPayload) {
switch payload.Type {
case "CompletedWebHookPayload":
handleCallCompleted(payload)
case "FailedWebHookPayload":
handleCallFailed(payload)
// ... handle other payload types
}
}
func main() {
http.HandleFunc("/webhooks/blackbox", webhookHandler)
log.Fatal(http.ListenAndServe(":3000", nil))
}
Step 2: Test with webhook.site
Before configuring production webhooks, test with webhook.site:
- Visit webhook.site
- Copy the unique URL provided
- Configure in Dasha BlackBox dashboard or API
- Trigger a test event
- Review payload in webhook.site interface
- Navigate to Agents → select agent → Webhooks tab
- Click Add Webhook Endpoint
- Enter your endpoint URL
- Select events to receive
- Add authentication headers
- Click Test Endpoint
- Verify test payload received
- Save configuration
const response = await fetch(
'https://blackbox.dasha.ai/api/v1/agents/agent_xyz789',
{
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
resultWebhook: {
url: 'https://api.example.com/webhooks/blackbox',
headers: {
'Authorization': 'Bearer your-webhook-secret'
}
}
})
}
);
const updated = await response.json();
console.log('Webhook configured:', updated.resultWebhook);
Step 5: Monitor and Debug
After configuration:
- Monitor Deliveries: Check webhook delivery logs in dashboard
- Review Failures: Investigate failed deliveries
- Adjust Timeouts: Increase if needed for slow endpoints
- Add Logging: Log all webhook receipts and processing
Webhook delivery history and statistics are available in the dashboard only. There are no API endpoints for retrieving delivery logs programmatically.
Troubleshooting
Webhooks Not Received
Check Endpoint Accessibility:
# Test from external network
curl -X POST https://api.example.com/webhooks/blackbox \
-H "Content-Type: application/json" \
-d '{"test": true}'
Common Issues:
- Firewall blocking Dasha BlackBox IP addresses
- SSL certificate errors
- Endpoint not publicly accessible
- Wrong URL in configuration
Solutions:
- Verify URL is accessible from internet
- Check SSL certificate validity
- Review firewall rules
- Test with webhook.site first
Authentication Failures
Symptoms:
- Webhooks received but header authentication fails
- 401/403 responses from your endpoint
Debug Steps:
// Log received headers
app.post('/webhooks/blackbox', (req, res) => {
console.log('Headers received:', req.headers);
console.log('Expected auth:', process.env.EXPECTED_AUTH);
// ... validate
});
Solutions:
- Verify header names match exactly (case-sensitive)
- Check for extra whitespace in header values
- Ensure API keys are current and not expired
- Test authentication separately with curl
Delivery Delays
Symptoms:
- Webhooks arrive minutes after events
- Out-of-order delivery
Common Causes:
- Slow endpoint response times
- High retry rate from failures
- Network latency
Solutions:
- Optimize endpoint to respond under 1 second
- Fix validation errors causing retries
- Acknowledge immediately, process async
- Monitor webhook queue depth
Missing Events
Symptoms:
- Some events not received
- Inconsistent delivery
Debug Steps:
- Check event subscription configuration
- Review webhook delivery logs in dashboard
- Verify no client-side filtering dropping events
- Check for high failure rate
Solutions:
- Ensure events are enabled in configuration
- Fix any authentication issues
- Review and fix validation errors
- Check endpoint uptime and availability
Next Steps
Now that you understand webhook fundamentals, explore these related topics:
API Reference