Heppu AI

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 successfully
    • 207: Partial success (some calls failed validation)
    • 400: Complete failure (invalid request)

Priority System

Control call execution order using the priority field (0-10 scale):

PriorityValueUse Case
High1-3Urgent callbacks, hot leads
Normal4-6Standard outreach
Low7-10Background 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 delay

Pitfall 4: Insufficient Retry Configuration

Problem: Calls fail permanently on first attempt

Solution: Configure appropriate retry settings

{
  "maxRetries": 3,
  "retryOnNoAnswer": true,
  "retryOnBusy": true
}

On this page