Syncing results
Poll for finished calls without missing any or processing one twice.
Webhooks push each result to you as soon as a call ends, with no polling. Polling, below, works too, and makes a good nightly safety net alongside webhooks.
There are two good polling patterns; pick the one that matches how you start calls.
Per campaign
If each batch is its own campaign, watch the campaign until it finishes, then read its calls once.
Wait for it to finish
GET /campaigns/{campaign_id} returns contacts_by_status. The campaign is done dialing when
there are no pending or initiated contacts left.
{ "status": "running", "contacts_by_status": { "pending": 12, "initiated": 3, "completed": 74, "no_answer": 6, "failed": 5 } }Read every call
GET /calls?campaign_id={campaign_id}&limit=200, paging with offset until you have total
calls.
Continuous
If calls start all the time, poll for recent calls instead.
import os, requests
from datetime import datetime, timedelta, timezone
API = "https://api.resonera.ai"
H = {"X-API-Key": os.environ["RESONERA_KEY"]}
def sync(last_run: datetime):
# Overlap the window: a call that started before the last run may only just have finished.
since = (last_run - timedelta(hours=2)).isoformat()
offset = 0
while True:
page = requests.get(f"{API}/calls", headers=H, params={
"since": since, "status": "completed", "limit": 200, "offset": offset,
}).json()
for call in page["calls"]:
if already_synced(call["call_id"]): # your own record of handled ids
continue
save_to_crm(call) # uses call["contact"]["extra"]
mark_synced(call["call_id"])
offset += page["limit"]
if offset >= page["total"]:
breaksince and until filter on when a call started. Overlap each window by your longest
expected call length plus a margin, and skip call_ids you've already handled. That way a call
that was still in progress at the last poll is picked up the next time.
Getting the transcript
List responses leave transcripts out to stay small. For each call you need it for, fetch
GET /calls/{call_id}:
{
"call_id": "…",
"outcome": "requirements_gathered",
"summary": "Wants a demo on Friday afternoon.",
"transcript": [
{ "role": "agent", "text": "Namaste, this is Neha from Acme Dental.", "timestamp": "…" },
{ "role": "user", "text": "Haan, boliye.", "timestamp": "…" }
]
}Recordings
Recording links expire after an hour, so store the call_id and request a fresh link from
GET /calls/{call_id}/recording-url when someone wants to listen.
How often to poll
Every few minutes is plenty. Calls take minutes, not seconds, and reads don't use your balance.