ResoneraDocs

Connect your CRM

The loop most integrations run: new leads go out as a campaign, outcomes come back to the record.

Most integrations are one scheduled job that does four things. This page shows the shape of it; the quickstart has runnable code for each call.

Using HubSpot, Zoho CRM, Pipedrive or Google Sheets? The dashboard's Connectors import contacts and log calls back without any code. The API is for everything else.

The loop

every 15 minutes:
  1. leads = your CRM's new leads that haven't been called
  2. POST /campaigns/json             one campaign for the batch, crm_id in each contact's extra
  3. POST /campaigns/{id}/launch      start dialing
  4. mark those leads "calling" in your CRM

on each call.ended webhook (or every 5 minutes, polling GET /calls?since=...):
  5. write status, outcome and summary to the record in contact.extra.crm_id

1. Pick the agent once

Agents rarely change, so look the id up once (or create the agent from code, see Managing agents) and keep it in your configuration.

2. Create a campaign per batch

Group the leads you want to call now into one campaign. Name it so you can find it in the dashboard, for example by date and source.

{
  "name": "Website leads 2026-10-05 14:00",
  "agent_id": "8b8e3307-2c1a-4f6e-9a51-0d7c2b9e4f10",
  "campaign_prompt": "They filled the pricing form. Offer a demo this week.",
  "contacts": [
    { "phone_number": "+919876543210", "name": "Asha", "extra": { "crm_id": "42", "source": "pricing-form" } }
  ]
}
  • Every number must be E.164 (+91…). One bad row rejects the whole request and names the row, so clean numbers before sending.
  • Up to 10,000 contacts per campaign.
  • extra takes any JSON. Put whatever you need to find the record again.

3. Launch

POST /campaigns/{campaign_id}/launch dials every pending contact. If you'd rather a person approve each batch, stop after step 2 and have them press Launch in the dashboard. A key with calls:write but no calls:dispatch can't launch, which enforces this.

4. Write results back

Register a webhook for call.ended and update the record in contact.extra as each call finishes. If you'd rather not run a public endpoint, poll GET /calls instead: Syncing results covers doing that without missing or double-processing calls.

What each field is good for in a CRM:

FieldUse it for
status, end_reasonWhether the call connected and how it ended.
outcomeA short label set by the agent, such as requirements_gathered. Good for pipeline stages.
summaryA sentence or two for the activity note.
duration_secondsCall length, for reporting.
transcript (from GET /calls/{call_id})The full conversation, for the record or for review.
recording (from GET /calls/{call_id}/recording-url)Fetch the link when someone wants to listen. It expires after an hour, so don't store it.

Keys for this integration

Give the job its own key with agents:read, calls:write, calls:dispatch and calls:read, and set a daily dispatch cap a little above your normal volume. If something loops, the cap stops it before it spends your balance.

On this page