Advanced Batch Calling Guide
Learn how to efficiently schedule and manage large-scale batch calling campaigns with up to 1,000 calls per batch
Overview
The Heppu AI batch calling API enables you to schedule up to 1,000 calls in a single request, perfect for large-scale outreach campaigns, appointment reminders, or automated follow-ups. This guide covers advanced features including priority systems, retry logic, and partial success handling.
Batch Capabilities
Maximum Batch Size
- Limit: 1,000 calls per batch request
- Processing: Asynchronous with webhook notifications
- Status Codes:
201: All calls scheduled successfully207: Partial success (some calls failed validation)400: Complete failure (invalid request)
Priority System
Control call execution order using the priority field (0-10 scale):
| Priority | Value | Use Case |
|---|---|---|
| High | 1-3 | Urgent callbacks, hot leads |
| Normal | 4-6 | Standard outreach |
| Low | 7-10 | Background campaigns |
// Example: Mixed priority batch
{
"agentId": "agent_abc123",
"calls": [
{
"phoneNumber": "+1234567890",
"priority": 1, // High priority - VIP customer
"metadata": { "type": "hot_lead" }
},
{
"phoneNumber": "+0987654321",
"priority": 5, // Normal priority
"metadata": { "type": "standard_outreach" }
}
]
}Retry Configuration
Retry Parameters
Control how the system handles failed calls:
interface RetryConfig {
maxRetries: number; // 0-5 attempts (default: 2)
retryOnNoAnswer: boolean; // Retry if no answer (default: true)
retryOnBusy: boolean; // Retry if busy signal (default: true)
retryOnFailed: boolean; // Retry on technical failures (default: true)
}Retry Strategies
Conservative Approach (fewer retries):
{
"maxRetries": 1,
"retryOnNoAnswer": true,
"retryOnBusy": false,
"retryOnFailed": true
}Aggressive Approach (maximum persistence):
{
"maxRetries": 5,
"retryOnNoAnswer": true,
"retryOnBusy": true,
"retryOnFailed": true
}Business Hours Only (recommended):
{
"maxRetries": 3,
"retryOnNoAnswer": true,
"retryOnBusy": true,
"retryOnFailed": true,
"scheduleAt": "2024-01-15T09:00:00Z",
"timezone": "America/New_York"
}Partial Success Handling
When some calls fail validation, you'll receive a 207 Multi-Status response:
{
"batchId": "batch_xyz789",
"status": "partial_success",
"summary": {
"total": 100,
"scheduled": 95,
"failed": 5
},
"scheduled": [
{
"callId": "call_001",
"phoneNumber": "+1234567890",
"status": "scheduled"
}
],
"failed": [
{
"phoneNumber": "+1invalid",
"error": "Invalid phone number format",
"code": "INVALID_PHONE"
}
]
}Handling Failed Calls
// Example: Retry failed calls from partial success
async function retryFailedCalls(batchResponse) {
const failedCalls = batchResponse.failed.filter(
call => call.code === 'TEMPORARY_ERROR'
);
if (failedCalls.length > 0) {
const retryBatch = {
agentId: "agent_abc123",
calls: failedCalls.map(call => ({
phoneNumber: call.phoneNumber,
priority: 2, // Higher priority for retries
maxRetries: 2
}))
};
await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(retryBatch)
});
}
}Best Practices for Large Campaigns
1. Segment Your Batches
Split large campaigns into manageable chunks:
async function scheduleLargeCampaign(phoneNumbers, agentId) {
const BATCH_SIZE = 1000;
const batches = [];
for (let i = 0; i < phoneNumbers.length; i += BATCH_SIZE) {
const chunk = phoneNumbers.slice(i, i + BATCH_SIZE);
const batch = {
agentId,
calls: chunk.map((number, index) => ({
phoneNumber: number,
priority: 5,
metadata: {
batchIndex: Math.floor(i / BATCH_SIZE),
position: index
}
}))
};
batches.push(batch);
}
return batches;
}2. Implement Rate Limiting
Avoid overwhelming your system:
async function scheduleWithRateLimit(batches, delayMs = 5000) {
const results = [];
for (const batch of batches) {
const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify(batch)
});
results.push(await response.json());
// Wait between batches
await new Promise(resolve => setTimeout(resolve, delayMs));
}
return results;
}3. Monitor Batch Progress
Track batch status using webhooks:
// Webhook endpoint to receive batch updates
app.post('/webhooks/batch-status', (req, res) => {
const { batchId, status, summary } = req.body;
console.log(`Batch ${batchId}: ${status}`);
console.log(`Success: ${summary.scheduled}/${summary.total}`);
if (status === 'completed') {
// Log completion
logBatchCompletion(batchId, summary);
} else if (status === 'partial_success') {
// Handle failed calls
retryFailedCalls(req.body);
}
res.sendStatus(200);
});4. Use Metadata for Tracking
Add custom metadata to track campaign performance:
{
"agentId": "agent_abc123",
"calls": [
{
"phoneNumber": "+1234567890",
"metadata": {
"campaignId": "summer_2024",
"segment": "high_value",
"source": "crm_export",
"customerId": "cust_123"
}
}
]
}Complete Workflow Example
Here's a complete example of scheduling a large campaign with error handling:
JavaScript
const API_BASE = 'https://v2.heppu.ai/api/v1';
const API_KEY = 'your_api_key_here';
async function executeBatchCampaign(agentId, phoneNumbers, options = {}) {
const {
priority = 5,
maxRetries = 3,
batchSize = 1000,
webhookUrl = null
} = options;
// Split into batches
const batches = [];
for (let i = 0; i < phoneNumbers.length; i += batchSize) {
batches.push(phoneNumbers.slice(i, i + batchSize));
}
console.log(`Scheduling ${phoneNumbers.length} calls in ${batches.length} batches`);
const results = {
totalScheduled: 0,
totalFailed: 0,
batchIds: []
};
// Process each batch
for (let i = 0; i < batches.length; i++) {
const batchPayload = {
agentId,
calls: batches[i].map(number => ({
phoneNumber: number,
priority,
maxRetries,
retryOnNoAnswer: true,
retryOnBusy: true,
retryOnFailed: true,
metadata: {
batchNumber: i + 1,
totalBatches: batches.length
}
})),
webhookUrl
};
try {
const response = await fetch(`${API_BASE}/calls/batch`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(batchPayload)
});
const result = await response.json();
if (response.ok) {
results.totalScheduled += result.summary.scheduled;
results.totalFailed += result.summary.failed;
results.batchIds.push(result.batchId);
console.log(`Batch ${i + 1}/${batches.length}: ${result.summary.scheduled} scheduled, ${result.summary.failed} failed`);
} else {
console.error(`Batch ${i + 1} failed:`, result);
results.totalFailed += batches[i].length;
}
// Rate limiting: wait 2 seconds between batches
if (i < batches.length - 1) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
} catch (error) {
console.error(`Error processing batch ${i + 1}:`, error);
results.totalFailed += batches[i].length;
}
}
return results;
}
// Usage
const phoneNumbers = [
'+12345678901',
'+12345678902',
// ... up to 10,000 numbers
];
executeBatchCampaign('agent_abc123', phoneNumbers, {
priority: 3,
maxRetries: 3,
webhookUrl: 'https://yourapp.com/webhooks/batch-status'
}).then(results => {
console.log('Campaign complete:', results);
});Python
import requests
import time
from typing import List, Dict, Optional
API_BASE = 'https://v2.heppu.ai/api/v1'
API_KEY = 'your_api_key_here'
def execute_batch_campaign(
agent_id: str,
phone_numbers: List[str],
priority: int = 5,
max_retries: int = 3,
batch_size: int = 1000,
webhook_url: Optional[str] = None
) -> Dict:
"""Execute a large batch calling campaign with error handling"""
# Split into batches
batches = [
phone_numbers[i:i + batch_size]
for i in range(0, len(phone_numbers), batch_size)
]
print(f"Scheduling {len(phone_numbers)} calls in {len(batches)} batches")
results = {
'total_scheduled': 0,
'total_failed': 0,
'batch_ids': []
}
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
# Process each batch
for i, batch in enumerate(batches):
payload = {
'agentId': agent_id,
'calls': [
{
'phoneNumber': number,
'priority': priority,
'maxRetries': max_retries,
'retryOnNoAnswer': True,
'retryOnBusy': True,
'retryOnFailed': True,
'metadata': {
'batchNumber': i + 1,
'totalBatches': len(batches)
}
}
for number in batch
]
}
if webhook_url:
payload['webhookUrl'] = webhook_url
try:
response = requests.post(
f'{API_BASE}/calls/batch',
headers=headers,
json=payload
)
if response.ok:
result = response.json()
results['total_scheduled'] += result['summary']['scheduled']
results['total_failed'] += result['summary']['failed']
results['batch_ids'].append(result['batchId'])
print(f"Batch {i + 1}/{len(batches)}: "
f"{result['summary']['scheduled']} scheduled, "
f"{result['summary']['failed']} failed")
else:
print(f"Batch {i + 1} failed: {response.text}")
results['total_failed'] += len(batch)
# Rate limiting: wait 2 seconds between batches
if i < len(batches) - 1:
time.sleep(2)
except Exception as e:
print(f"Error processing batch {i + 1}: {e}")
results['total_failed'] += len(batch)
return results
# Usage
phone_numbers = [
'+12345678901',
'+12345678902',
# ... up to 10,000 numbers
]
results = execute_batch_campaign(
agent_id='agent_abc123',
phone_numbers=phone_numbers,
priority=3,
max_retries=3,
webhook_url='https://yourapp.com/webhooks/batch-status'
)
print('Campaign complete:', results)cURL
# Single batch request (up to 1,000 calls)
curl -X POST https://v2.heppu.ai/api/v1/calls/batch \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_abc123",
"calls": [
{
"phoneNumber": "+12345678901",
"priority": 3,
"maxRetries": 3,
"retryOnNoAnswer": true,
"retryOnBusy": true,
"retryOnFailed": true,
"metadata": {
"campaignId": "summer_2024",
"segment": "high_value"
}
},
{
"phoneNumber": "+12345678902",
"priority": 5,
"maxRetries": 2,
"metadata": {
"campaignId": "summer_2024",
"segment": "standard"
}
}
],
"webhookUrl": "https://yourapp.com/webhooks/batch-status"
}'Common Pitfalls and Solutions
Pitfall 1: Exceeding Batch Size Limit
Problem: Sending more than 1,000 calls in a single request
Solution: Split your calls into multiple batches
const BATCH_SIZE = 1000;
const batches = chunk(allPhoneNumbers, BATCH_SIZE);Pitfall 2: Not Handling Partial Success
Problem: Treating 207 responses as failures
Solution: Parse the response and handle failed calls separately
if (response.status === 207) {
const { scheduled, failed } = await response.json();
// Process scheduled calls
// Retry or log failed calls
}Pitfall 3: Missing Rate Limits
Problem: Sending batches too quickly overwhelms the system
Solution: Add delays between batch submissions
await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delayPitfall 4: Insufficient Retry Configuration
Problem: Calls fail permanently on first attempt
Solution: Configure appropriate retry settings
{
"maxRetries": 3,
"retryOnNoAnswer": true,
"retryOnBusy": true
}