> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dialgen.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch Webhooks

> Receive real-time notifications for batch and call completion events.

## 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.

```json theme={null}
{
  "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:

```json theme={null}
{
  "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:

```json theme={null}
{
  "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:

```json theme={null}
{
  "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

```javascript theme={null}
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

```python theme={null}
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

```javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
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

```javascript theme={null}
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

<Warning>
  If all retries fail, the webhook is dropped. Implement proper error handling and logging.
</Warning>

***

## Testing Webhooks

### Use ngrok for Local Testing

```bash theme={null}
# Start ngrok
ngrok http 3000

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

### Test with curl

```bash theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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

<CardGroup cols={2}>
  <Card title="Monitoring" icon="chart-line" href="/guides/batch-calling/monitoring">
    Track batch progress and performance
  </Card>

  <Card title="Call Metrics API" icon="code" href="/api-reference/endpoint/get-call-metrics">
    Retrieve detailed call metrics
  </Card>
</CardGroup>
