Skip to main content

What are Webhooks?

Webhooks are HTTP callbacks that Dialgen sends to your server when specific events occur. Instead of polling for updates, your application receives instant notifications.

Available Webhooks

onBatchComplete

Triggered when all calls in the batch are finished.
{
  "event": "batch.completed",
  "batchId": "batch_123",
  "agentId": "agent_456",
  "userId": "user_789",
  "statistics": {
    "total": 1000,
    "processed": 1000,
    "succeeded": 850,
    "failed": 150,
    "percentComplete": 100,
    "successRate": 85
  },
  "timestamps": {
    "createdAt": "2025-11-15T14:00:00.000Z",
    "startedAt": "2025-11-15T14:05:00.000Z",
    "completedAt": "2025-11-15T16:30:00.000Z"
  }
}

onCallComplete

Triggered after each individual call completes. Includes full call data:
{
  "success": true,
  "callData": {
    "id": "call_123",
    "contactId": "contact_456",
    "agentId": "agent_789",
    "batchId": "batch_abc",
    "status": "COMPLETED",
    "startTime": "2025-11-15T14:30:00.000Z",
    "endTime": "2025-11-15T14:33:00.000Z",
    "duration": 180,
    "phoneNumber": "+1234567890",
    "contactName": "John Doe",
    "transcription": [...],
    "recordingUrl": "https://...",
    "summary": "Call summary text...",
    "metricJson": {
      "intent": "SALES_INQUIRY",
      "sentiment": "positive",
      "confidence": 0.95
    }
  },
  "metricSchema": {...}
}

onBatchFailed

Triggered if the batch fails to process:
{
  "event": "batch.failed",
  "batchId": "batch_123",
  "agentId": "agent_456",
  "error": "Insufficient call minutes",
  "code": "INSUFFICIENT_CREDITS",
  "timestamp": "2025-11-15T14:05:00.000Z"
}

Configuring Webhooks

Add webhook URLs when creating your batch:
{
  "agentId": "agent_123",
  "userId": "user_456",
  "contacts": [...],
  "options": {
    "webhooks": {
      "onBatchComplete": "https://api.yourcompany.com/webhooks/batch-complete",
      "onCallComplete": "https://api.yourcompany.com/webhooks/call-complete",
      "onBatchFailed": "https://api.yourcompany.com/webhooks/batch-failed"
    }
  }
}

Implementing Webhook Endpoints

Node.js Example

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

app.use(express.json());

// Handle call completion
app.post('/webhooks/call-complete', (req, res) => {
  const { callData } = req.body;
  
  console.log(`Call ${callData.id} completed`);
  console.log(`Duration: ${callData.duration}s`);
  console.log(`Summary: ${callData.summary}`);
  
  // Process call data
  // - Update CRM
  // - Send notifications
  // - Trigger workflows
  
  res.status(200).json({ received: true });
});

// Handle batch completion
app.post('/webhooks/batch-complete', (req, res) => {
  const { batchId, statistics } = req.body;
  
  console.log(`Batch ${batchId} completed`);
  console.log(`Success rate: ${statistics.successRate}%`);
  
  // Process batch completion
  // - Generate reports
  // - Send summary email
  // - Archive batch data
  
  res.status(200).json({ received: true });
});

app.listen(3000);

Python Example

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/call-complete', methods=['POST'])
def call_complete():
    data = request.json
    call_data = data['callData']
    
    print(f"Call {call_data['id']} completed")
    print(f"Duration: {call_data['duration']}s")
    print(f"Summary: {call_data['summary']}")
    
    # Process call data
    # - Update CRM
    # - Send notifications
    # - Trigger workflows
    
    return jsonify({'received': True}), 200

@app.route('/webhooks/batch-complete', methods=['POST'])
def batch_complete():
    data = request.json
    batch_id = data['batchId']
    statistics = data['statistics']
    
    print(f"Batch {batch_id} completed")
    print(f"Success rate: {statistics['successRate']}%")
    
    # Process batch completion
    # - Generate reports
    # - Send summary email
    # - Archive batch data
    
    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)

Best Practices

Respond Quickly

  • Respond within 5 seconds to avoid retries
  • Return a 2xx status code immediately
  • Process data asynchronously if needed
app.post('/webhooks/call-complete', async (req, res) => {
  // Respond immediately
  res.status(200).json({ received: true });
  
  // Process asynchronously
  processCallData(req.body).catch(console.error);
});

Handle Idempotency

Webhooks may be delivered multiple times. Use idempotency keys:
const processedWebhooks = new Set();

app.post('/webhooks/call-complete', (req, res) => {
  const callId = req.body.callData.id;
  
  if (processedWebhooks.has(callId)) {
    return res.status(200).json({ received: true, duplicate: true });
  }
  
  processedWebhooks.add(callId);
  
  // Process webhook
  processCallData(req.body);
  
  res.status(200).json({ received: true });
});

Secure Your Endpoints

Verify webhook authenticity:
const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const signature = req.headers['x-dialgen-signature'];
  const payload = JSON.stringify(req.body);
  
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return signature === expectedSignature;
}

app.post('/webhooks/call-complete', (req, res) => {
  if (!verifyWebhook(req, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Process webhook
  res.status(200).json({ received: true });
});

Log Everything

Keep detailed logs for debugging:
app.post('/webhooks/call-complete', (req, res) => {
  console.log('Webhook received:', {
    timestamp: new Date().toISOString(),
    callId: req.body.callData?.id,
    status: req.body.callData?.status,
    headers: req.headers
  });
  
  // Process webhook
  
  res.status(200).json({ received: true });
});

Handle Errors Gracefully

app.post('/webhooks/call-complete', async (req, res) => {
  try {
    await processCallData(req.body);
    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Webhook processing error:', error);
    // Still return 200 to prevent retries for application errors
    res.status(200).json({ received: true, error: error.message });
  }
});

Retry Logic

If your endpoint fails, Dialgen will retry:
  • Retry Attempts: Up to 3 times
  • Retry Delay: Exponential backoff (1s, 5s, 25s)
  • Timeout: 30 seconds per attempt
If all retries fail, the webhook is dropped. Implement proper error handling and logging.

Testing Webhooks

Use ngrok for Local Testing

# Start ngrok
ngrok http 3000

# Use the ngrok URL in your webhook configuration
https://abc123.ngrok.io/webhooks/call-complete

Test with curl

curl -X POST https://your-server.com/webhooks/call-complete \
  -H "Content-Type: application/json" \
  -d '{
    "success": true,
    "callData": {
      "id": "call_test_123",
      "status": "COMPLETED",
      "duration": 180
    }
  }'

Common Use Cases

Update CRM

app.post('/webhooks/call-complete', async (req, res) => {
  const { callData } = req.body;
  
  await crm.updateContact({
    id: callData.contactId,
    lastCallDate: callData.startTime,
    callSummary: callData.summary,
    sentiment: callData.metricJson.sentiment
  });
  
  res.status(200).json({ received: true });
});

Send Notifications

app.post('/webhooks/batch-complete', async (req, res) => {
  const { batchId, statistics } = req.body;
  
  await sendEmail({
    to: 'team@company.com',
    subject: `Batch ${batchId} Completed`,
    body: `
      Total Calls: ${statistics.total}
      Success Rate: ${statistics.successRate}%
      Succeeded: ${statistics.succeeded}
      Failed: ${statistics.failed}
    `
  });
  
  res.status(200).json({ received: true });
});

Trigger Workflows

app.post('/webhooks/call-complete', async (req, res) => {
  const { callData } = req.body;
  
  if (callData.metricJson.intent === 'INTERESTED') {
    await workflow.trigger('send-follow-up-email', {
      contactId: callData.contactId,
      summary: callData.summary
    });
  }
  
  res.status(200).json({ received: true });
});

Next Steps