Endpoint
POST https://api.graine.ai/api/v1/batches/
Also available as POST /batches/upload (Merlin alias — identical behaviour).
Creating a batch immediately begins dispatching calls to all contacts (unless scheduled_start is set).
Header Required Description AuthorizationYes Bearer gat_<token>Content-TypeYes application/json
Request Body
Field Type Required Description campaign_idstring Yes The campaign this batch belongs to contactsContact[] Yes List of contacts to call (see schema below) organization_idstring No Defaults to token’s org scheduled_startdatetime No When to begin dispatching. null = immediately concurrency_limitinteger No Max concurrent calls. Overrides campaign setting max_retries_overrideinteger No Overrides campaign.retry_policy.max_retries for this batch call_contextobject No Additional context passed to the telephony layer metadataobject No Arbitrary metadata stored with the batch
Field Type Required Description phone_numberstring Yes E.164 format (e.g. +919876543210) call_variablesobject No Per-contact substitution variables for the agent
callee_name must be in call_variables for each contact (not in campaign default_call_variables). The AI agent uses this in its greeting — without it, the agent cannot address the person by name.
scheduled_start timezone handling
Value Behaviour null (default)Dispatch begins immediately Naive datetime (no Z / offset) Interpreted in the campaign’s timezone (e.g. "2026-05-10T11:00:00" → 11 AM IST) UTC datetime (with Z) Used as-is
Example Request
{
"campaign_id" : "cmp_abc123" ,
"organization_id" : "org_xyz" ,
"contacts" : [
{
"phone_number" : "+919876543210" ,
"call_variables" : {
"callee_name" : "Rajesh Sharma" ,
"current_plan" : "Basic Health" ,
"renewal_date" : "2026-07-01" ,
"premium" : "12000"
}
},
{
"phone_number" : "+919876543211" ,
"call_variables" : {
"callee_name" : "Priya Mehta" ,
"current_plan" : "Family Floater" ,
"renewal_date" : "2026-06-15" ,
"premium" : "28000"
}
}
],
"concurrency_limit" : 10 ,
"scheduled_start" : null
}
Schedule for a specific time
{
"campaign_id" : "cmp_abc123" ,
"contacts" : [ ... ],
"scheduled_start" : "2026-05-12T09:00:00"
}
This starts at 9:00 AM in the campaign’s timezone (e.g. Asia/Kolkata).
Responses
200 OK
{
"batch_id" : "btc_xyz001" ,
"campaign_id" : "cmp_abc123" ,
"organization_id" : "org_xyz" ,
"status" : "in_progress" ,
"total_contacts" : 2 ,
"created_at" : "2026-05-10T09:00:00Z"
}
Save the returned batch_id — use it to track progress, export results, or pause/cancel this batch independently.
400 Bad Request
{ "detail" : "campaign_id is required" }
404 Not Found
{ "detail" : "Campaign not found or does not belong to this org" }
Code Examples
import requests
url = "https://api.graine.ai/api/v1/batches/"
headers = {
"Authorization" : "Bearer gat_your_token" ,
"Content-Type" : "application/json"
}
payload = {
"campaign_id" : "cmp_abc123" ,
"contacts" : [
{
"phone_number" : "+919876543210" ,
"call_variables" : {
"callee_name" : "Rajesh Sharma" ,
"current_plan" : "Basic Health" ,
"renewal_date" : "2026-07-01" ,
"premium" : "12000"
}
}
],
"concurrency_limit" : 10
}
response = requests.post(url, json = payload, headers = headers)
batch = response.json()
print (batch[ "batch_id" ])
const response = await fetch ( "https://api.graine.ai/api/v1/batches/" , {
method: "POST" ,
headers: {
"Authorization" : "Bearer gat_your_token" ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
campaign_id: "cmp_abc123" ,
contacts: [
{
phone_number: "+919876543210" ,
call_variables: {
callee_name: "Rajesh Sharma" ,
current_plan: "Basic Health" ,
renewal_date: "2026-07-01" ,
premium: "12000"
}
}
],
concurrency_limit: 10
})
});
const batch = await response . json ();
console . log ( batch . batch_id );
Next Steps
Monitor Contacts Track per-contact call status
Export Results Download call outcomes as CSV
Pause or Cancel Control batch execution mid-flight
Troubleshoot Stuck Batch Debug why calls aren’t going out