Developers
API documentation
Send recordings from any phone system, dialler or storage bucket, read the results back, and receive each processed call as a signed webhook. Base URL: https://convointelligence.com
Authentication
Create an API key in Sources & API inside your workspace. Each key belongs to one source — for example “Exotel inbound” — and calls it sends are labelled with that source. The full key is shown once; we store only a fingerprint. Send it in any of these ways:
Authorization: Bearer ci_src_…— recommended.- HTTP Basic auth with the key as the password — for systems like Twilio that can only send credentials in the URL.
?key=ci_src_…on the URL — for callbacks that can't set headers at all. Treat that URL as a secret.
Keys can send recordings. To also read calls through the API, turn on read access for the source.
Send a call
POST /api/v1/ingest accepts a recording in one of two ways. Either way, you get a response straight away and the call is processed in the background, usually within a minute or two.
Upload the file (up to 4 MB)
curl -X POST https://convointelligence.com/api/v1/ingest \ -H "Authorization: Bearer ci_src_…" \ -F "recording=@call.mp3" \ -F "externalId=CALL-12345" \ -F "agentId=AG-1042" -F "agentName=Priya Nair" -F "team=Support — North"
Send a link (up to 250 MB)
For anything larger, or when your system already stores the recording, send its URL. We fetch it over HTTPS, following redirects, and refuse links to private networks. Credentials in the URL (https://user:pass@host/…) are sent as Basic auth.
curl -X POST https://convointelligence.com/api/v1/ingest \
-H "Authorization: Bearer ci_src_…" \
-H "Content-Type: application/json" \
-d '{
"recordingUrl": "https://recordings.example.com/2026/10/03/CALL-12345.mp3",
"externalId": "CALL-12345",
"agentEmail": "priya@yourcompany.in",
"direction": "inbound",
"callStartedAt": "2026-10-03T09:41:00+05:30",
"metadata": { "campaign": "Diwali sale", "queue": "Orders" }
}'Fields
| Field | Notes |
|---|---|
recording | The audio or video file (multipart only). |
recordingUrl | Link to the recording. Also accepted as RecordingUrl and recording_url. |
externalId | Your id for the call. Sending the same id from the same source again returns the original instead of processing twice — unless the original failed, in which case it is processed again. Also accepted as CallSid. |
agentId, agentName, agentEmail | Who took the call. Agents are matched by id, then email, then exact name, and created the first time they appear. DialWhomNumber is read as the agent id. |
team | The agent's team or queue. Teams are created the first time they appear. |
direction | inbound or outbound. Telephony values like incoming and outbound-dial are understood. |
customerPhone | Optional. If omitted we use From for inbound calls and To for outbound. Not stored when redaction is on. |
callStartedAt | When the call started: ISO 8601, Unix seconds or milliseconds, or a plain date and time such as 2026-10-03 09:41 or 03/10/2026 9:41 AM. A time without a zone is read in your company's time zone, and dates with slashes are day first. Also read from StartTime. Calls are dated by this time in every report, so older calls sent today land in the right week. |
schemaId | Which data contract to extract. Defaults to General Conversation. |
pipelineId | Deliver through this pipeline only. |
leadId, customerId, customerName | Optional. Your CRM or dialler id for the customer, and their name. Calls with the same id, or else the same phone number, are grouped as one customer. Phone numbers are matched through a one-way code and are not stored when redaction is on. |
metadata | A JSON object of up to 50 keys with text, number, boolean or null values. Shown on the call and included in webhooks. |
Response
202 Accepted
{ "id": "cmuso0xg70001…", "status": "queued" }
200 OK (same externalId sent again)
{ "id": "cmuso0xg70001…", "status": "completed", "duplicate": true }Telephony setup
Any system that can call a URL when a recording is ready can send calls. Fields that platforms commonly send — RecordingUrl, CallSid, From, To, Direction, StartTime — are read automatically, as JSON or as form fields.
Exotel
In your call flow, set the StatusCallback (or Passthru applet URL) to https://convointelligence.com/api/v1/ingest?key=ci_src_…. Exotel posts the recording link and call details when the call ends; the CallSid becomes the call id, so a repeated callback is never processed twice. Send the agent with your own parameters if you pass them, or use DialWhomNumber.
Twilio
Set recordingStatusCallback on the call or <Record> verb to https://convo:ci_src_…@convointelligence.com/api/v1/ingest. Twilio sends the recording link and CallSid with Basic auth, and the key is read from the password.
Ozonetel, Knowlarity and others
Use the platform's call-end or recording webhook with the same endpoint and key. If the platform can't send a recording link, upload recordings from your storage with a small script, or contact us and we'll help set it up.
Dual-channel recordings
If your system can record the agent and the customer on separate stereo channels, switch it on: it is detected automatically, each side is transcribed on its own and who said what then comes from the recording itself rather than from the words. Mono recordings work as before.
Past calls from a call log
To bring in calls you already have, export the call log from your dialler or CRM as CSV and add it under Calls → Upload calls → Add call log. Columns are recognised by their usual names (agent, team, customer phone or From/To, customer name, lead or loan id, call time or separate date and time columns, direction, call id, recording link), and any other columns, such as disposition or campaign, are kept with each call. If the log has recording links, the calls are imported straight from them; otherwise drop the recordings and each is matched to its row by file name or call id. A call already imported is not imported twice. Recording links must use HTTPS.
Check processing status
curl https://convointelligence.com/api/v1/ingest/cmuso0xg70001… -H "Authorization: Bearer ci_src_…"
{ "id": "cmuso0xg70001…", "status": "completed", "externalId": "CALL-12345",
"receivedAt": "…", "completedAt": "…", "callId": "cmusmp9m3000lxak508ikskfg" }Status is queued, processing, retrying, completed or failed. Failures include an error written for people. Temporary problems retry on their own with back-off; a failed call can be sent again with the same externalId. Rather than polling, most teams use a webhook.
Read calls
Requires a key with read access.
List calls
curl "https://convointelligence.com/api/v1/calls?since=2026-10-01T00:00:00Z&limit=50" -H "Authorization: Bearer ci_src_…"
Newest first. Filters: since, range (7d, 30d, 90d, 365d, all), q (words said), agent, sentiment, resolution, outcome (sales result), intent, result (passed, failed, unscored), reason, language. Page with limit (up to 100) and the nextCursor from the previous page as cursor.
Get one call
curl https://convointelligence.com/api/v1/calls/cmusmp9m3000lxak508ikskfg -H "Authorization: Bearer ci_src_…"
Returns the transcript, speaker turns, insights (summary, reason, sentiment, outcome, topics, risks, follow-ups, key moments), the extracted data, every scorecard result with each answer, its evidence and reasoning, and your metadata.
Both endpoints include revenue: the customer's intent (high, medium, low, none, not_applicable), the call's sales outcome (won, commitment, follow_up, lost, no_decision, not_applicable) and productInterest, what the customer wants. Send a leadId, customerId or the customer's phone number with each call so a customer's calls are grouped together.
Lead routing
Ask which agent should take a lead before your dialler assigns it. Agents are ranked by how often they convert customers who want the same thing and speak the same language, from your own analysed calls over the last 90 days. Small samples are pulled towards the team's rate, so a lucky streak doesn't outrank a long record. Requires a key with read access and the Growth plan.
curl "https://convointelligence.com/api/v1/routing?interest=2BHK%20flat%20in%20Pune&language=hi" -H "Authorization: Bearer ci_src_…"
{ "basedOnDays": 90, "teamConversion": 0.31,
"agents": [
{ "id": "AG-3110", "name": "Neha Joshi", "team": "Inside sales — Pune", "expectedConversion": 0.52,
"evidence": { "salesCalls": 41, "convertedOverall": 19, "callsForInterest": 12, "convertedForInterest": 7, "callsInLanguage": 30, "convertedInLanguage": 15 } },
…
] }Parameters: interest (matched to the call labels you see under Revenue › Customer segments), language (a two-letter code such as hi or ta), days (7 to 365) and limit (up to 50). Agents are identified by your employee id when you have set one.
Webhooks
Add a webhook in Destinations and create a pipeline to send every processed call to it as JSON. Alerts can use the same webhook. Failed deliveries retry with exponential back-off for up to five attempts; a failed delivery never marks the call itself as failed, and can be resent from the call or pipeline page.
Payload
{
"event": "conversation.processed",
"conversation_id": "cmusmp9m3000lxak508ikskfg",
"processed_at": "2026-10-03T13:51:12.000Z",
"schema": "Support ticket",
"data": {
"issue_category": "Delivery",
"order_or_account_reference": "SN48219",
"refund_requested": true,
"amount_in_question": 2499,
"promised_by": null,
"escalation_needed": true
},
"call": {
"title": "Delayed order with conditional refund",
"summary": "The customer's order missed its delivery date…",
"sentiment": "negative",
"resolution": "follow_up",
"call_reason": "Delivery delay",
"languages": [
"hi",
"en"
],
"duration_seconds": 99.72,
"qa_score": 85.5,
"qa_passed": true,
"intent": "not_applicable",
"sales_outcome": "not_applicable",
"product_interest": null,
"next_best_action": "Send the SMS update once the courier confirms the new date.",
"external_id": "CALL-12345",
"direction": "inbound",
"started_at": null,
"agent_name": "Priya Nair",
"agent_email": null,
"agent_id": "AG-1042",
"url": "https://convointelligence.com/dashboard/calls/cmusmp9m3000lxak508ikskfg"
},
"agent": {
"id": "AG-1042",
"name": "Priya Nair",
"email": null
},
"scores": [
{
"scorecard": "Customer support QA",
"score": 85.5,
"passed": true,
"auto_failed": false
}
],
"metadata": {
"campaign": "Diwali sale"
}
}data holds the data contract's fields, or only the fields you mapped. Pipelines can also include the full transcript. Alerts send the same shape with "event": "alert.triggered" and an alert object naming the rule.
Verifying the signature
Each request carries Convo-Signature: t=<unix seconds>,v1=<hex>: an HMAC-SHA256 of <t>.<raw body> with your webhook's signing secret, which is shown once when you add the webhook. Check it against the raw body, before parsing, and reject timestamps older than five minutes.
// Node.js
import crypto from "node:crypto";
export function verifyConvoSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && expected.length === parts.v1?.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}# Python
import hashlib, hmac, time
def verify_convo_signature(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(part.split("=", 1) for part in header.split(","))
expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return abs(time.time() - int(parts["t"])) < 300 and hmac.compare_digest(expected, parts.get("v1", ""))Webhooks must use HTTPS and point at a public address, and must not redirect. Respond with any 2xx status within 20 seconds. Responses of 408, 425, 429 and 5xx, timeouts and network errors are retried; other 4xx responses are treated as final.
Formats and limits
- Formats: MP3, WAV, M4A, AAC, OGG, OPUS, FLAC, WebM, MP4, MOV, AMR, 3GP or WMA. Video files are accepted; only the audio is used.
- Size: 4 MB by direct upload to the API, 250 MB by link or browser upload. Recordings longer than 45 minutes are processed in parts automatically.
- Rate: 600 requests a minute per key. Above that you get
429with aRetry-Afterheader. - Usage: each workspace has a monthly allowance of audio minutes. Once it's used, new recordings get
402until the next month or an upgrade.
Errors
Errors are JSON with a message written for people: { "error": "Include a recording file or a recordingUrl." }
| Status | Meaning |
|---|---|
| 400 | Something in the request is missing or malformed. |
| 401 | The key is missing, wrong or disabled. |
| 402 | The workspace has used its monthly audio minutes. |
| 403 | The key can't read calls; turn on read access for its source. |
| 404 | The data contract, pipeline, call or ingestion doesn't exist in this workspace. |
| 413 | The file is too large for this route; send a link instead. |
| 415 | The file isn't a supported audio or video format. |
| 429 | Too many requests; wait for the number of seconds in Retry-After. |
Questions? Get in touch.