Calls and campaigns
Place a call
POST /v1/calls
Authorization: Bearer alt_live_…
Idempotency-Key: invoice-42
{
"agent": "7d1c…",
"to": "+393331234567",
"inputs": { "name": "Luca Galli", "amount": 250, "month": "January" },
"reference": "invoice-42",
"scheduled_at": "2026-10-01T09:30:00+02:00",
"max_attempts": 2,
"callback_url": "https://your-app.example/allerto/outcomes"
}
| Field | Required | Meaning |
|---|---|---|
agent | yes | A published outbound agent |
to | yes | E.164 number |
inputs | when the agent requires them | Validated against the agent's inputs |
reference | no | Your id; returned in every event |
scheduled_at | no | Earliest time for the first attempt |
max_attempts | no | Overrides calling.maxAttempts (1–5) |
callback_url | no | HTTPS URL receiving this call's events instead of the agent's webhook |
The response is 202 with the call object. Repeating the request with the same Idempotency-Key returns 200 and the same call: it is never dialled twice.
What happens next
- The queue waits for
scheduled_atand for the agent's calling window (Italian time). - It checks the do-not-call list, the daily cap (3 attempts per number per day across the account) and the account's concurrent calls (3 by default).
- The phone rings from the agent's caller ID. Answering machines are detected and hung up before the model speaks (
voicemail, never retried). - No answer or busy: another attempt after 15 minutes, 1 hour, 3 hours, up to
max_attempts. You receivecall.attempt_failedfor each. - After a conversation the transcript is turned into the agent's
result, the outcome is classified andcall.completedis sent. do_not_calladds the number to your list.callback_requestedwith a time creates a new call at that time (inside the window) whencalling.autoCallbackis on; it links back withretry_of.
Cancel a call that has not started with POST /v1/calls/{id}/cancel.
The call object
{
"id": "…",
"object": "call",
"direction": "outbound",
"livemode": true,
"agent": { "id": "…", "version": 3, "name": "Payment reminder" },
"client": { "id": "…", "external_id": "default" },
"campaign_id": null,
"reference": "invoice-42",
"to": "+393331234567",
"from": "+390280781047",
"inputs": { "name": "Luca Galli", "amount": 250, "month": "January" },
"status": "completed",
"outcome": "completed",
"attempts": 1,
"max_attempts": 3,
"next_attempt_at": null,
"answered_at": "…",
"ended_at": "…",
"duration_seconds": 94,
"callback_at": null,
"transfer": null,
"tool_calls": [],
"ai_disclosed": true,
"recording": null,
"anonymized_at": null,
"result": {
"data": { "answer": "will_pay", "pay_by": "2026-10-05" },
"issues": [],
"summary": "…",
"sentiment": {
"label": "negative",
"score": -1,
"start": -2,
"end": 0,
"trend": "improving",
"emotions": [
{
"type": "frustration",
"intensity": "high",
"evidence": "it's the third time I've called"
}
]
}
},
"retry_of": null,
"error": null,
"created_at": "…"
}
Inbound calls have the same shape with direction: "inbound", from set to the caller and no inputs.
Status: queued → dialing → in_progress → processing → completed; or failed, cancelled; needs_review while an operator checks the data (inbound agents with missing data).
Outcome:
| Outcome | Answered | Retried | Meaning |
|---|---|---|---|
completed | yes | no | The conversation reached its purpose (whatever the answer) |
callback_requested | yes | automatic, see callback_at | Asked to be called later |
wrong_person | yes | no | Someone else answered |
refused | yes | no | Did not want to talk |
do_not_call | yes | never | Asked not to be called again |
hung_up | yes | no | Ended before saying anything useful |
transferred | yes | no | Handed to the operator of the agent's transfer settings |
no_answer, busy | no | yes | |
voicemail | no | no | Answering machine |
suppressed | no | no | Number on the do-not-call list |
invalid_number, failed, cancelled | no | no |
Transfer
A call handed to an operator has outcome: "transferred" and:
"transfer": {
"to": "+390212345678",
"status": "answered",
"requested_at": "…",
"ended_at": "…",
"duration_seconds": 184
}
status is requested or dialing while the operator's phone rings, then answered, no_answer, busy or failed; duration_seconds is the time spent with the operator. call.completed is sent as soon as the data collected before the transfer is ready, usually while the transfer is still going on: read the call for its final status. The conversation with the operator is not transcribed.
Sentiment
Every call where the person spoke comes with result.sentiment, for every agent and without configuration, like the summary.
scoregoes from-2(very negative) to2(very positive);0is a calm, factual call, the usual case.labelisnegative,neutralorpositive.startandenduse the same scale for the first and last part of the call;trendsays whether it wasimproving,stableorworsening.emotionslists up to three emotions the person expressed (frustration,anger,anxiety,confusion,satisfaction,gratitude), each with anintensity(low,medium,high) and the person's own words asevidence. An emotion is kept only if those words are really in the transcript.
It is read from what the person said, never from their voice: it is text analysis, not emotion recognition from biometric data. sentiment is null when the person did not speak or the call could not be analysed; test-mode calls carry a neutral sentiment.
Reading calls
GET /v1/calls?direction=outbound&outcome=callback_requested&since=2026-09-01T00:00:00Z&limit=50
GET /v1/calls/{id}
GET /v1/calls/{id}/transcript
GET /v1/calls/{id}/recording
ai_disclosed says whether the agent stated it is an AI in its first words (null when it never spoke). recording is { "url": "/v1/calls/{id}/recording" } when the agent records calls: a mono 8 kHz WAV. After 24 months personal data is removed and anonymized_at is set; see Privacy.
Lists are newest first; pass next_cursor as cursor for the next page. Filters: direction, agent, campaign, status, outcome, reference, since.
Campaigns
POST /v1/campaigns
{
"agent": "7d1c…",
"name": "January instalments",
"contacts": [
{ "to": "+393331234567", "inputs": { "name": "Luca Galli", "amount": 250, "month": "January" }, "reference": "A-1" },
{ "to": "+393339876543", "inputs": { "name": "Anna Verdi", "amount": 90, "month": "January" }, "reference": "A-2" }
],
"max_concurrent": 3
}
Every contact is validated before anything is queued: if one is invalid you get 400 invalid_contacts with the index of each problem, and nothing is queued. Duplicates and numbers on the do-not-call list are skipped and reported in skipped. Up to 5,000 contacts per request.
allerto campaigns create --agent <id> --csv contacts.csv --name "January instalments"
The CSV needs a to column (also phone, telefono, numero); reference is optional; every other column is an input. Commas or semicolons both work, and Italian numbers like 333 1234567 become +393331234567.
GET /v1/campaigns/{id} returns counts by_status and by_outcome. pause, resume and cancel are POST /v1/campaigns/{id}/<action>; cancelling cancels the calls still queued.
Do-not-call list
GET /v1/suppressions
POST /v1/suppressions {"number": "+393331234567", "reason": "Asked by email"}
DELETE /v1/suppressions/%2B393331234567
The list is checked when a call is queued and again right before dialling. Adding a number fails its queued calls with suppressed.