ResoneraDocs

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"]:
            break

since 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.

On this page