Batch Call Upload API
Upload multiple calls (1-100) in a single API request for efficient bulk import.
Overview
The Batch Upload API allows you to import historical calls or integrate with external systems to automatically upload calls to Aila.
Endpoint
POST /v1/calls/batch-uploadAuthentication
Requires API key in header:
Authorization: Bearer YOUR_API_KEYRequest Format
Request Body
{
"calls": [
{
"external_id": "call_12345",
"audio_url": "https://yourdomain.com/recordings/call1.mp3",
"user_email": "agent@company.com",
"contact_name": "John Doe",
"contact_phone": "+15551234567",
"contact_email": "john@example.com",
"call_date": "2025-01-15T14:30:00Z",
"direction": "outbound",
"duration": 350,
"metadata": {
"campaign": "Q1 2025",
"product": "Enterprise Plan"
}
},
{
"external_id": "call_12346",
"audio_url": "s3://your-bucket/call2.mp3",
"user_email": "agent2@company.com",
"contact_name": "Jane Smith",
"contact_phone": "+15559876543",
"call_date": "2025-01-15T15:45:00Z",
"direction": "inbound",
"duration": 420
}
]
}Parameters
Required:
external_id- Your unique identifier for the callaudio_url- URL to audio file (HTTP/HTTPS or S3)user_email- Team member's emailcall_date- ISO 8601 timestamp
Optional:
contact_name- Contact's full namecontact_phone- Phone number (E.164 format recommended)contact_email- Contact's emaildirection-inboundoroutboundduration- Call duration in secondsmetadata- Custom key-value pairs
Audio File Requirements
Supported Formats:
- MP3
- WAV
- M4A
- AAC
File Access:
- Public URLs (no auth required)
- Signed S3 URLs
- Aila S3 bucket (auto-detected)
File Size:
- Maximum: 2GB per file
- Recommended: Under 200MB
Response Format
Success Response
{
"success": true,
"batch_id": "batch_abc123",
"calls_submitted": 2,
"status": "processing"
}Tracking Batch Status
GET /v1/calls/batch-upload/{batch_id}Response:
{
"success": true,
"batch_id": "batch_abc123",
"status": "processing",
"submitted_at": "2025-01-15T16:00:00Z",
"total_calls": 2,
"processed": 1,
"failed": 0,
"pending": 1,
"calls": [
{
"external_id": "call_12345",
"status": "completed",
"aila_call_id": "aila_xyz789",
"processed_at": "2025-01-15T16:03:00Z"
},
{
"external_id": "call_12346",
"status": "processing",
"progress": "transcribing"
}
]
}Error Response
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid calls array",
"details": {
"calls": [
{
"index": 0,
"external_id": "call_12345",
"errors": [
"audio_url is not accessible",
"duration must be positive number"
]
}
]
}
}
}Batch Status Values
submitted- Batch received, queued for processingprocessing- Calls being processedcompleted- All calls processed successfullypartial- Some calls succeeded, some failedfailed- All calls failed
Individual Call Status
pending- Waiting to processdownloading- Fetching audio filetranscribing- Converting speech to textanalyzing- AI analysis in progresscompleted- Fully processedfailed- Processing failed
Automatic Actions
When batch upload completes, Aila automatically:
- Creates Users - New users created from
user_email - Creates Contacts - New contacts from contact info
- Matches Existing - Links to existing users/contacts
- Transcribes - Generates full transcript
- Analyzes - Extracts AI insights
- Scores Quality - Evaluates against checklists
- Syncs CRM - Updates connected systems
Examples
JavaScript/Node.js
const fetch = require('node-fetch');
async function batchUploadCalls(calls) {
const response = await fetch('https://api.aila.com/v1/calls/batch-upload', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.AILA_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ calls })
});
const result = await response.json();
console.log('Batch ID:', result.batch_id);
// Poll for status
const batchId = result.batch_id;
while (true) {
const statusResponse = await fetch(
`https://api.aila.com/v1/calls/batch-upload/${batchId}`,
{
headers: {
'Authorization': `Bearer ${process.env.AILA_API_KEY}`
}
}
);
const status = await statusResponse.json();
console.log(`Status: ${status.status}, Processed: ${status.processed}/${status.total_calls}`);
if (status.status === 'completed' || status.status === 'partial') {
return status;
}
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5s
}
}
// Usage
const calls = [
{
external_id: 'call_001',
audio_url: 'https://example.com/call1.mp3',
user_email: 'agent@company.com',
contact_name: 'John Doe',
call_date: new Date().toISOString()
}
];
batchUploadCalls(calls);Python
import requests
import time
import os
def batch_upload_calls(calls):
response = requests.post(
'https://api.aila.com/v1/calls/batch-upload',
headers={
'Authorization': f'Bearer {os.environ["AILA_API_KEY"]}',
'Content-Type': 'application/json'
},
json={'calls': calls}
)
result = response.json()
batch_id = result['batch_id']
print(f'Batch ID: {batch_id}')
# Poll for status
while True:
status_response = requests.get(
f'https://api.aila.com/v1/calls/batch-upload/{batch_id}',
headers={'Authorization': f'Bearer {os.environ["AILA_API_KEY"]}'}
)
status = status_response.json()
print(f'Status: {status["status"]}, Processed: {status["processed"]}/{status["total_calls"]}')
if status['status'] in ['completed', 'partial']:
return status
time.sleep(5)
# Usage
calls = [
{
'external_id': 'call_001',
'audio_url': 'https://example.com/call1.mp3',
'user_email': 'agent@company.com',
'contact_name': 'John Doe',
'call_date': '2025-01-15T14:30:00Z'
}
]
batch_upload_calls(calls)Best Practices
Batch Sizing
- Recommended: 10-50 calls per batch
- Maximum: 100 calls per batch
- Larger batches for better efficiency
- Smaller batches for faster per-call feedback
Error Handling
const failed = status.calls.filter(c => c.status === 'failed');
if (failed.length > 0) {
console.error('Failed calls:', failed);
// Retry failed calls
const retryCalls = failed.map(f =>
originalCalls.find(c => c.external_id === f.external_id)
);
await batchUploadCalls(retryCalls);
}Audio File Hosting
Best Practices:
- Use signed URLs for private files
- Set reasonable expiration (1-24 hours)
- Ensure high availability
- Consider using S3 for reliability
Progress Monitoring
Poll every 5-10 seconds:
async function waitForBatch(batchId) {
const maxWait = 30 * 60 * 1000; // 30 minutes
const pollInterval = 5000; // 5 seconds
const startTime = Date.now();
while (Date.now() - startTime < maxWait) {
const status = await getBatchStatus(batchId);
if (status.status === 'completed' || status.status === 'partial') {
return status;
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
throw new Error('Batch processing timeout');
}Troubleshooting
Audio File Not Accessible
Error: "audio_url is not accessible"
Solutions:
- Verify URL is publicly accessible
- Check signed URL hasn't expired
- Ensure CORS headers if needed
- Test URL in browser
User Not Found
Error: "user_email not found"
Solution:
- Create user first via dashboard
- Or API will auto-create (if enabled)
- Verify email format
- Check organization membership
Processing Timeout
If calls stuck in processing:
- Check batch status endpoint
- Review individual call errors
- Verify audio file format
- Contact support with batch_id
Rate Limits
- Batches per hour: 100
- Calls per hour: 10,000
- Concurrent batches: 10
Enterprise customers can request higher limits.
Next Steps
- API Overview - Full API documentation
- Authentication - Get API keys