Batch Schedule Calls
Schedule up to 1,000 calls at once with timezone-aware scheduling and partial success handling
Batch Schedule Calls
Schedule up to 1,000 outbound calls in a single request with advanced features including timezone-aware scheduling, retry configuration, and partial success handling.
Endpoint
POST /api/v1/calls/batchAuthentication
Supports API key or session-based authentication:
# API Key
x-api-key: YOUR_API_KEY
# Bearer Token
Authorization: Bearer YOUR_API_KEYRequest Body
Required Fields
| Field | Type | Description |
|---|---|---|
agentId | string (UUID) | ID of the voice agent to use for all calls |
calls | array | Array of call objects (max: 1,000) |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
defaultPriority | integer (0-10) | 5 | Default priority for all calls |
defaultScheduledAt | string (ISO 8601) | null | Default scheduled time for calls without individual scheduledAt |
stopOnError | boolean | false | Stop processing remaining calls if one fails |
retryConfig | object | See below | Retry configuration for all calls |
timezoneAwareConfig | object | null | Timezone-aware scheduling configuration |
Call Object
Each call in the calls array:
| Field | Type | Required | Description |
|---|---|---|---|
contactPhone | string | Yes | Phone number in E.164 format |
contactName | string | No | Contact's full name |
contactEmail | string | No | Contact's email address |
scheduledAt | string | No | Override default scheduled time |
priority | integer (0-10) | No | Override default priority |
timezone | string | No | IANA timezone (e.g., "America/New_York") |
localScheduleTime | string | No | Local time to call (e.g., "09:00") |
callMetadata | object | No | Rich contact and call context (same as Schedule Call) |
metadata | object | No | Additional custom metadata |
Retry Configuration
You can configure automatic retries using either relative intervals or fixed schedules:
Using Relative Intervals (Recommended)
{
retryConfig: {
maxRetries: number; // Auto-calculated from retryIntervals length if not set
retryOnNoAnswer: boolean; // Default: true
retryOnBusy: boolean; // Default: true
retryOnFailed: boolean; // Default: false
retryIntervals: [ // Relative retry intervals
{ value: 2, unit: "hours" }, // 1st retry: 2 hours after initial call
{ value: 1, unit: "days" }, // 2nd retry: 1 day after 1st retry
{ value: 3, unit: "days" } // 3rd retry: 3 days after 2nd retry
]
}
}Supported units: "minutes", "hours", "days"
Using Fixed Schedule
{
retrySchedule: [ // Array of ISO 8601 timestamps
"2025-12-02T14:00:00Z", // 1st retry at specific time
"2025-12-03T10:00:00Z", // 2nd retry at specific time
"2025-12-04T16:00:00Z" // 3rd retry at specific time
],
retryConfig: {
retryOnNoAnswer: boolean; // Default: true
retryOnBusy: boolean; // Default: true
retryOnFailed: boolean; // Default: false
}
}Retry Behavior
maxRetriesis automatically calculated fromretryIntervalsorretrySchedulelength if not explicitly set- If no retry configuration is provided, calls will not be retried on failure
- Use
retryIntervalsfor multi-timezone campaigns (relative timing) - Use
retrySchedulefor single-timezone campaigns (absolute timing)
Timezone-Aware Configuration
{
enabled: boolean; // Enable timezone-aware scheduling
localScheduleTime: string; // Local time to call (e.g., "10:00")
respectBusinessHours: boolean; // Only call during business hours
businessHoursStart: string; // Start of business hours (e.g., "09:00")
businessHoursEnd: string; // End of business hours (e.g., "17:00")
excludeWeekends: boolean; // Skip Saturday and Sunday
allowedDays: number[]; // Days of week (0=Sunday, 6=Saturday)
maxCallsPerDay: number; // Maximum calls per day
staggering: {
enabled: boolean;
intervalMinutes: number; // Minutes between calls
};
}Response
Response Schema
{
"data": {
"agentId": "string",
"agent": {
"id": "string",
"name": "string"
},
"summary": {
"totalRequested": "integer",
"successful": "integer",
"failed": "integer",
"partialSuccess": "boolean",
"stopped": "boolean"
},
"results": [
{
"index": "integer",
"success": "boolean",
"callId": "string",
"error": "string",
"contactPhone": "string"
}
]
},
"message": "string"
}HTTP Status Codes:
201- All calls scheduled successfully207- Partial success (some calls failed)400- All calls failed
Examples
Basic Batch Schedule
curl -X POST https://v2.heppu.ai/api/v1/calls/batch \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"calls": [
{
"contactPhone": "+14155552671",
"contactName": "John Doe"
},
{
"contactPhone": "+14155552672",
"contactName": "Jane Smith"
},
{
"contactPhone": "+14155552673",
"contactName": "Bob Johnson"
}
]
}'const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '550e8400-e29b-41d4-a716-446655440000',
calls: [
{ contactPhone: '+14155552671', contactName: 'John Doe' },
{ contactPhone: '+14155552672', contactName: 'Jane Smith' },
{ contactPhone: '+14155552673', contactName: 'Bob Johnson' }
]
})
});
const data = await response.json();
console.log(`Scheduled: ${data.data.summary.successful}/${data.data.summary.totalRequested}`);import requests
response = requests.post(
'https://v2.heppu.ai/api/v1/calls/batch',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'agentId': '550e8400-e29b-41d4-a716-446655440000',
'calls': [
{'contactPhone': '+14155552671', 'contactName': 'John Doe'},
{'contactPhone': '+14155552672', 'contactName': 'Jane Smith'},
{'contactPhone': '+14155552673', 'contactName': 'Bob Johnson'}
]
}
)
data = response.json()
summary = data['data']['summary']
print(f"Scheduled: {summary['successful']}/{summary['totalRequested']}")Scheduled Batch with Priority
curl -X POST https://v2.heppu.ai/api/v1/calls/batch \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"defaultPriority": 3,
"defaultScheduledAt": "2025-12-02T10:00:00Z",
"calls": [
{
"contactPhone": "+14155552671",
"contactName": "VIP Customer",
"priority": 0
},
{
"contactPhone": "+14155552672",
"contactName": "Regular Customer"
}
]
}'const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(10, 0, 0, 0);
const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '550e8400-e29b-41d4-a716-446655440000',
defaultPriority: 3,
defaultScheduledAt: tomorrow.toISOString(),
calls: [
{
contactPhone: '+14155552671',
contactName: 'VIP Customer',
priority: 0 // Override with high priority
},
{
contactPhone: '+14155552672',
contactName: 'Regular Customer'
// Will use defaultPriority (3)
}
]
})
});from datetime import datetime, timedelta
tomorrow = datetime.utcnow() + timedelta(days=1)
tomorrow = tomorrow.replace(hour=10, minute=0, second=0, microsecond=0)
response = requests.post(
'https://v2.heppu.ai/api/v1/calls/batch',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'agentId': '550e8400-e29b-41d4-a716-446655440000',
'defaultPriority': 3,
'defaultScheduledAt': tomorrow.isoformat() + 'Z',
'calls': [
{
'contactPhone': '+14155552671',
'contactName': 'VIP Customer',
'priority': 0 # Override with high priority
},
{
'contactPhone': '+14155552672',
'contactName': 'Regular Customer'
# Will use defaultPriority (3)
}
]
}
)Timezone-Aware Batch Scheduling
curl -X POST https://v2.heppu.ai/api/v1/calls/batch \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"timezoneAwareConfig": {
"enabled": true,
"localScheduleTime": "10:00",
"respectBusinessHours": true,
"businessHoursStart": "09:00",
"businessHoursEnd": "17:00",
"excludeWeekends": true,
"staggering": {
"enabled": true,
"intervalMinutes": 5
}
},
"calls": [
{
"contactPhone": "+14155552671",
"contactName": "John (NYC)",
"timezone": "America/New_York"
},
{
"contactPhone": "+442071838750",
"contactName": "Jane (London)",
"timezone": "Europe/London"
},
{
"contactPhone": "+61291234567",
"contactName": "Bob (Sydney)",
"timezone": "Australia/Sydney"
}
]
}'const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '550e8400-e29b-41d4-a716-446655440000',
timezoneAwareConfig: {
enabled: true,
localScheduleTime: '10:00',
respectBusinessHours: true,
businessHoursStart: '09:00',
businessHoursEnd: '17:00',
excludeWeekends: true,
staggering: {
enabled: true,
intervalMinutes: 5
}
},
calls: [
{
contactPhone: '+14155552671',
contactName: 'John (NYC)',
timezone: 'America/New_York'
},
{
contactPhone: '+442071838750',
contactName: 'Jane (London)',
timezone: 'Europe/London'
},
{
contactPhone: '+61291234567',
contactName: 'Bob (Sydney)',
timezone: 'Australia/Sydney'
}
]
})
});
const data = await response.json();
console.log('Timezone-aware scheduling complete:', data.data.summary);response = requests.post(
'https://v2.heppu.ai/api/v1/calls/batch',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'agentId': '550e8400-e29b-41d4-a716-446655440000',
'timezoneAwareConfig': {
'enabled': True,
'localScheduleTime': '10:00',
'respectBusinessHours': True,
'businessHoursStart': '09:00',
'businessHoursEnd': '17:00',
'excludeWeekends': True,
'staggering': {
'enabled': True,
'intervalMinutes': 5
}
},
'calls': [
{
'contactPhone': '+14155552671',
'contactName': 'John (NYC)',
'timezone': 'America/New_York'
},
{
'contactPhone': '+442071838750',
'contactName': 'Jane (London)',
'timezone': 'Europe/London'
},
{
'contactPhone': '+61291234567',
'contactName': 'Bob (Sydney)',
'timezone': 'Australia/Sydney'
}
]
}
)
print('Timezone-aware scheduling complete')Batch with Retry Intervals
curl -X POST https://v2.heppu.ai/api/v1/calls/batch \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"retryConfig": {
"retryOnNoAnswer": true,
"retryOnBusy": true,
"retryIntervals": [
{ "value": 2, "unit": "hours" },
{ "value": 1, "unit": "days" }
]
},
"calls": [
{
"contactPhone": "+14155552671",
"contactName": "John Doe",
"contactEmail": "john@example.com",
"callMetadata": {
"firstName": "John",
"lastName": "Doe",
"company": "Acme Corp",
"callPurpose": "product_demo",
"campaignId": "holiday-2025"
}
}
]
}'const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '550e8400-e29b-41d4-a716-446655440000',
retryConfig: {
retryOnNoAnswer: true,
retryOnBusy: true,
retryIntervals: [
{ value: 2, unit: 'hours' }, // Retry after 2 hours
{ value: 1, unit: 'days' } // Then retry after 1 day
]
},
calls: [
{
contactPhone: '+14155552671',
contactName: 'John Doe',
contactEmail: 'john@example.com',
callMetadata: {
firstName: 'John',
lastName: 'Doe',
company: 'Acme Corp',
callPurpose: 'product_demo',
campaignId: 'holiday-2025'
}
}
]
})
});response = requests.post(
'https://v2.heppu.ai/api/v1/calls/batch',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'agentId': '550e8400-e29b-41d4-a716-446655440000',
'retryConfig': {
'retryOnNoAnswer': True,
'retryOnBusy': True,
'retryIntervals': [
{'value': 2, 'unit': 'hours'}, # Retry after 2 hours
{'value': 1, 'unit': 'days'} # Then retry after 1 day
]
},
'calls': [
{
'contactPhone': '+14155552671',
'contactName': 'John Doe',
'contactEmail': 'john@example.com',
'callMetadata': {
'firstName': 'John',
'lastName': 'Doe',
'company': 'Acme Corp',
'callPurpose': 'product_demo',
'campaignId': 'holiday-2025'
}
}
]
}
)Batch with Fixed Retry Schedule
For single-timezone campaigns where you want retries at specific times:
curl -X POST https://v2.heppu.ai/api/v1/calls/batch \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"retrySchedule": [
"2025-12-02T14:00:00Z",
"2025-12-03T10:00:00Z"
],
"retryConfig": {
"retryOnNoAnswer": true,
"retryOnBusy": true
},
"calls": [
{
"contactPhone": "+14155552671",
"contactName": "John Doe"
}
]
}'const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: '550e8400-e29b-41d4-a716-446655440000',
retrySchedule: [
'2025-12-02T14:00:00Z', // 1st retry at 2 PM UTC
'2025-12-03T10:00:00Z' // 2nd retry next day at 10 AM UTC
],
retryConfig: {
retryOnNoAnswer: true,
retryOnBusy: true
},
calls: [
{
contactPhone: '+14155552671',
contactName: 'John Doe'
}
]
})
});response = requests.post(
'https://v2.heppu.ai/api/v1/calls/batch',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'agentId': '550e8400-e29b-41d4-a716-446655440000',
'retrySchedule': [
'2025-12-02T14:00:00Z', # 1st retry at 2 PM UTC
'2025-12-03T10:00:00Z' # 2nd retry next day at 10 AM UTC
],
'retryConfig': {
'retryOnNoAnswer': True,
'retryOnBusy': True
},
'calls': [
{
'contactPhone': '+14155552671',
'contactName': 'John Doe'
}
]
}
)Response Examples
Complete Success (201)
{
"data": {
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"agent": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Sales Agent"
},
"summary": {
"totalRequested": 3,
"successful": 3,
"failed": 0,
"partialSuccess": false,
"stopped": false
},
"results": [
{
"index": 0,
"success": true,
"callId": "call_abc123",
"contactPhone": "+14155552671"
},
{
"index": 1,
"success": true,
"callId": "call_def456",
"contactPhone": "+14155552672"
},
{
"index": 2,
"success": true,
"callId": "call_ghi789",
"contactPhone": "+14155552673"
}
]
},
"message": "All 3 calls scheduled successfully"
}Partial Success (207)
{
"data": {
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"agent": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Sales Agent"
},
"summary": {
"totalRequested": 3,
"successful": 2,
"failed": 1,
"partialSuccess": true,
"stopped": false
},
"results": [
{
"index": 0,
"success": true,
"callId": "call_abc123",
"contactPhone": "+14155552671"
},
{
"index": 1,
"success": false,
"error": "Invalid phone number format. Must be E.164 format",
"contactPhone": "4155552672"
},
{
"index": 2,
"success": true,
"callId": "call_ghi789",
"contactPhone": "+14155552673"
}
]
},
"message": "Scheduled 2 calls, 1 failed"
}Error Responses
Missing Required Fields
{
"error": {
"message": "Missing required fields: agentId and calls array are required",
"code": 400
}
}Empty Calls Array
{
"error": {
"message": "Calls array cannot be empty",
"code": 400
}
}Exceeds Maximum Batch Size
{
"error": {
"message": "Batch size exceeds maximum limit of 1000 calls",
"code": 400
}
}Invalid Timezone Configuration
{
"error": {
"message": "Invalid timezone configuration: localScheduleTime must be in HH:mm format",
"code": 400
}
}Use Cases
Import Contacts from CSV
import csv
import requests
def schedule_calls_from_csv(csv_file, agent_id):
"""Schedule batch calls from CSV file"""
calls = []
with open(csv_file, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
calls.append({
'contactPhone': row['phone'],
'contactName': row['name'],
'contactEmail': row.get('email'),
'callMetadata': {
'firstName': row.get('first_name'),
'lastName': row.get('last_name'),
'company': row.get('company'),
'timezone': row.get('timezone'),
'campaignId': row.get('campaign_id')
}
})
# Process in batches of 1000
batch_size = 1000
results = []
for i in range(0, len(calls), batch_size):
batch = calls[i:i+batch_size]
response = requests.post(
'https://v2.heppu.ai/api/v1/calls/batch',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'agentId': agent_id,
'calls': batch,
'retryConfig': {
'maxRetries': 3,
'retryOnNoAnswer': True,
'retryOnBusy': True
}
}
)
data = response.json()
results.append(data['data']['summary'])
print(f"Batch {i//batch_size + 1}: {data['data']['summary']['successful']} scheduled")
return resultsGlobal Campaign with Timezone Awareness
async function scheduleGlobalCampaign(contacts, agentId) {
const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: agentId,
timezoneAwareConfig: {
enabled: true,
localScheduleTime: '10:00',
respectBusinessHours: true,
businessHoursStart: '09:00',
businessHoursEnd: '17:00',
excludeWeekends: true,
allowedDays: [1, 2, 3, 4, 5], // Monday-Friday
staggering: {
enabled: true,
intervalMinutes: 3
}
},
calls: contacts.map(contact => ({
contactPhone: contact.phone,
contactName: contact.name,
contactEmail: contact.email,
timezone: contact.timezone,
callMetadata: {
firstName: contact.firstName,
lastName: contact.lastName,
company: contact.company,
preferredLanguage: contact.language,
campaignId: 'global-outreach-2025'
}
})),
retryConfig: {
maxRetries: 2,
retryOnNoAnswer: true,
retryOnBusy: false
}
})
});
return response.json();
}Handle Partial Success
async function scheduleBatchWithErrorHandling(calls, agentId) {
const response = await fetch('https://v2.heppu.ai/api/v1/calls/batch', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
agentId: agentId,
calls: calls
})
});
const data = await response.json();
const { summary, results } = data.data;
console.log(`Total: ${summary.totalRequested}`);
console.log(`Success: ${summary.successful}`);
console.log(`Failed: ${summary.failed}`);
// Log failures
const failures = results.filter(r => !r.success);
if (failures.length > 0) {
console.log('\nFailed calls:');
failures.forEach(failure => {
console.log(`- ${failure.contactPhone}: ${failure.error}`);
});
// Optionally retry failures with corrected data
const retryData = failures
.filter(f => f.error.includes('phone number'))
.map(f => ({
contactPhone: correctPhoneFormat(f.contactPhone),
// ... other fields
}));
if (retryData.length > 0) {
console.log(`\nRetrying ${retryData.length} failed calls...`);
return scheduleBatchWithErrorHandling(retryData, agentId);
}
}
return data;
}
function correctPhoneFormat(phone) {
// Remove spaces, dashes, etc.
const cleaned = phone.replace(/[\s\-\(\)]/g, '');
// Add + if missing
return cleaned.startsWith('+') ? cleaned : `+${cleaned}`;
}Validation Rules
Batch Size Limits
- Minimum: 1 call
- Maximum: 1,000 calls per request
Phone Number Validation
Each phone number must be in E.164 format (^\+[1-9]\d{1,14}$)
Timezone Configuration Validation
localScheduleTimemust be in HH:mm format (e.g., "10:00")- Business hours must be valid time ranges
- Allowed days must be 0-6 (0=Sunday, 6=Saturday)
Best Practices
- Validate phone numbers - Ensure all numbers are E.164 format before sending
- Use timezone-aware scheduling - Better contact rates when calling at appropriate times
- Enable staggering - Prevent overwhelming your system with simultaneous calls
- Handle partial success - Check individual results even on 207 status
- Implement retry logic - Network issues may cause timeouts
- Monitor batch limits - Split large lists into 1,000-call batches
- Use appropriate priority - Balance urgency with system capacity
- Include rich metadata - More context improves agent performance
Related Endpoints
- Schedule Call - Schedule individual calls
- List Calls - View scheduled calls
- Cancel Call - Cancel scheduled calls
- Outbound Call - Initiate immediate calls