curl --request POST \
--url https://app.thinnest.ai/api/v1/calls/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"agent": "Skyline Sales",
"purpose": "I'm calling from Skyline Homes about the new tower launching at Baner.",
"callingHours": {
"start": "10:00",
"end": "19:00",
"days": [
"mon",
"tue",
"wed",
"thu",
"fri",
"sat"
]
},
"extract": [
{
"name": "interest",
"choices": [
"Hot",
"Warm",
"Cold"
]
}
],
"summary": true,
"variables": {
"project": "Sky Towers"
},
"retry": {
"count": 1
},
"calls": [
{
"to": "919876543210",
"name": "Asha Rao",
"reference": "LSQ-42",
"variables": {
"lead_name": "Asha"
}
},
{
"to": "+91 99200 11223",
"name": "Vikram Shah",
"reference": "LSQ-43"
},
{
"to": "12345",
"reference": "LSQ-44"
}
]
}
EOFconst options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
agent: 'Skyline Sales',
purpose: 'I\'m calling from Skyline Homes about the new tower launching at Baner.',
callingHours: {start: '10:00', end: '19:00', days: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat']},
extract: [{name: 'interest', choices: ['Hot', 'Warm', 'Cold']}],
summary: true,
variables: {project: 'Sky Towers'},
retry: {count: 1},
calls: [
{
to: '919876543210',
name: 'Asha Rao',
reference: 'LSQ-42',
variables: {lead_name: 'Asha'}
},
{to: '+91 99200 11223', name: 'Vikram Shah', reference: 'LSQ-43'},
{to: '12345', reference: 'LSQ-44'}
]
})
};
fetch('https://app.thinnest.ai/api/v1/calls/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.thinnest.ai/api/v1/calls/batch"
payload = {
"agent": "Skyline Sales",
"purpose": "I'm calling from Skyline Homes about the new tower launching at Baner.",
"callingHours": {
"start": "10:00",
"end": "19:00",
"days": ["mon", "tue", "wed", "thu", "fri", "sat"]
},
"extract": [
{
"name": "interest",
"choices": ["Hot", "Warm", "Cold"]
}
],
"summary": True,
"variables": { "project": "Sky Towers" },
"retry": { "count": 1 },
"calls": [
{
"to": "919876543210",
"name": "Asha Rao",
"reference": "LSQ-42",
"variables": { "lead_name": "Asha" }
},
{
"to": "+91 99200 11223",
"name": "Vikram Shah",
"reference": "LSQ-43"
},
{
"to": "12345",
"reference": "LSQ-44"
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)Place Batch Calls
Rings up to 200 people with one request. Every option Place Call takes may be set once for the batch and again on an entry, the entry winning (an entry’s variables sit on top of the batch’s); purpose and agent are the batch’s. Every entry is queued and paced inside your plan’s lines and balance — none rings inline — and each gets its own sch_… id that reports through call.analysed and Get Call. A bad entry is listed under refused and does not stop the rest. Needs a full key.
curl --request POST \
--url https://app.thinnest.ai/api/v1/calls/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"agent": "Skyline Sales",
"purpose": "I'm calling from Skyline Homes about the new tower launching at Baner.",
"callingHours": {
"start": "10:00",
"end": "19:00",
"days": [
"mon",
"tue",
"wed",
"thu",
"fri",
"sat"
]
},
"extract": [
{
"name": "interest",
"choices": [
"Hot",
"Warm",
"Cold"
]
}
],
"summary": true,
"variables": {
"project": "Sky Towers"
},
"retry": {
"count": 1
},
"calls": [
{
"to": "919876543210",
"name": "Asha Rao",
"reference": "LSQ-42",
"variables": {
"lead_name": "Asha"
}
},
{
"to": "+91 99200 11223",
"name": "Vikram Shah",
"reference": "LSQ-43"
},
{
"to": "12345",
"reference": "LSQ-44"
}
]
}
EOFconst options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
agent: 'Skyline Sales',
purpose: 'I\'m calling from Skyline Homes about the new tower launching at Baner.',
callingHours: {start: '10:00', end: '19:00', days: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat']},
extract: [{name: 'interest', choices: ['Hot', 'Warm', 'Cold']}],
summary: true,
variables: {project: 'Sky Towers'},
retry: {count: 1},
calls: [
{
to: '919876543210',
name: 'Asha Rao',
reference: 'LSQ-42',
variables: {lead_name: 'Asha'}
},
{to: '+91 99200 11223', name: 'Vikram Shah', reference: 'LSQ-43'},
{to: '12345', reference: 'LSQ-44'}
]
})
};
fetch('https://app.thinnest.ai/api/v1/calls/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.thinnest.ai/api/v1/calls/batch"
payload = {
"agent": "Skyline Sales",
"purpose": "I'm calling from Skyline Homes about the new tower launching at Baner.",
"callingHours": {
"start": "10:00",
"end": "19:00",
"days": ["mon", "tue", "wed", "thu", "fri", "sat"]
},
"extract": [
{
"name": "interest",
"choices": ["Hot", "Warm", "Cold"]
}
],
"summary": True,
"variables": { "project": "Sky Towers" },
"retry": { "count": 1 },
"calls": [
{
"to": "919876543210",
"name": "Asha Rao",
"reference": "LSQ-42",
"variables": { "lead_name": "Asha" }
},
{
"to": "+91 99200 11223",
"name": "Vikram Shah",
"reference": "LSQ-43"
},
{
"to": "12345",
"reference": "LSQ-44"
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)Authorizations
Your API key (ta_live_…) from Settings → API keys, sent as Authorization: Bearer <key>. Keep it on a server: it can message every customer you have. A key is full, build or read-only; a request its level does not allow is refused with 403.
Headers
Any unique string. A retry with the same key within 24 hours returns the first request's answer instead of acting twice.
255Developers only: the customer workspace this request acts in — its org_… id from POST /customers. Leave it out to act in your own workspace.
"org_3fKq9TzQ1mN8vB2xR7cLpA"
Body
Up to 200 calls with one purpose and one agent. The options below apply to every entry unless the entry says otherwise.
Said aloud first on every call. Under 300 characters.
1 - 299The people to ring.
1 - 200 elementsShow child attributes
Show child attributes
Which agent calls — its ag_… id or name. Needed only when more than one agent answers the phone.
The same as the Idempotency-Key header. One key covers the whole batch.
The lead's name. A number that is not a contact yet becomes one with this name; an existing contact's name is never overwritten.
120Where the lead came from, e.g. 99acres enquiry form — recorded on a new contact.
120Your own id for this lead, never interpreted. It comes back on the response, every report and both call webhooks.
200Values the agent's instructions use as {{name}}. Up to 20; names are lower-cased with spaces turned into _, values cut to 150 characters.
Show child attributes
Show child attributes
When this person may be rung, in their own time. Leave it out for any day, 09:00 to 21:00.
Show child attributes
Show child attributes
Outside the calling hours right now: schedule queues the call for the minute they open; refuse answers 409 with nextOpening and does nothing. Does not apply to a call with scheduledAt, nor to a batch (which always queues).
schedule, refuse The details to fill from the call, keyed back exactly as named. A value that does not fit its type is left out rather than sent wrong. Leave it out for the agent's own collected fields.
30Show child attributes
Show child attributes
true writes two or three sentences about the call, false skips it. Leave it out to follow the agent's own switch.
Your own flat data, returned exactly as sent on every report and webhook. Up to 20 keys of 1–60 characters, 4,000 characters serialised; values are strings, numbers, booleans or null — a nested object or list is refused.
Show child attributes
Show child attributes
Don't ring before this instant, written with its offset (2026-10-07T10:00:00+05:30). Up to 30 days ahead; queued, then rung at this time or the next opening of the calling hours after it. A time already past means now.
Try again when the person was not reached (no answer, busy, an answering machine, or a pick-up the agent never reached). A declined call, a dead number and a call somebody answered are never retried. A retry keeps the same id, rings only inside the calling hours, and is checked again against opt-outs and the do-not-call list.
Show child attributes
Show child attributes
{ "count": 2, "noAnswerMinutes": 60, "busyMinutes": 15 }
Which of the agent's numbers rings — its own, or one lent to it for calling (List Phone Numbers). Leave it out for the agent's own line. On an agent with several numbers, a chosen number places at most 200 calls a day.
Changes to the agent for this call only; the agent itself is not edited. Any other key is refused by name.
Show child attributes
Show child attributes
{ "voice": "priya", "language": "Hindi", "maxCallSeconds": 300 }