Calls
Calls, their outcomes, recordings and transcripts. Call a lead now.
Every example sends your key ID and secret as CALLVIEW_KEY_ID and CALLVIEW_SECRET (see Keys and authentication). The base address is https://v2-api.callview.ai/api/v1/external.
List calls
Section titled “List calls”GET /calls
Newest first. Cursor-paged: pass the last call’s id as starting_after for the next page. Needs calls:read; a key limited to some campaigns sees only their calls.
Deprecated for one release: offset and status (and a limit over 100) still answer the old list shape, with a Deprecation header.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
limit |
query | integer | Default 50. 1 to 100. |
|
starting_after |
query | string (uuid) | ||
campaign_id |
query | string (uuid) | ||
lead_id |
query | string (uuid) | ||
external_id |
query | string | The lead’s ID in your system. | |
outcome |
query | string | One of interested / not_interested / callback / voicemail / no_answer / busy / failed / dnc / wrong_number / hung_up / screened / language_barrier / not_qualified / undetermined / other. |
|
created[gte] |
query | string | ||
created[lte] |
query | string | ||
offset |
query | integer | ||
status |
query | string |
curl -X GET 'https://v2-api.callview.ai/api/v1/external/calls' \ -u "$CALLVIEW_KEY_ID:$CALLVIEW_SECRET"const auth = Buffer.from(`${process.env.CALLVIEW_KEY_ID}:${process.env.CALLVIEW_SECRET}`).toString('base64');
const res = await fetch('https://v2-api.callview.ai/api/v1/external/calls', { method: 'GET', headers: { Authorization: `Basic ${auth}` },});console.log(res.status, res.headers.get('request-id'));console.log(await res.text());import os
import requests
res = requests.request( "GET", "https://v2-api.callview.ai/api/v1/external/calls", auth=(os.environ["CALLVIEW_KEY_ID"], os.environ["CALLVIEW_SECRET"]), timeout=30,)print(res.status_code, res.headers.get("Request-Id"))print(res.text)<?php$ch = curl_init('https://v2-api.callview.ai/api/v1/external/calls');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_USERPWD => getenv('CALLVIEW_KEY_ID') . ':' . getenv('CALLVIEW_SECRET'), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false,]);$body = curl_exec($ch);echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $body, "\n";200 A page of calls, newest first
{ "data": [ { "id": "e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b", "object": "call", "livemode": true, "lead": { "id": "2b8e4c1d-9f3a-4b7e-8c21-6a5d4e3f2b10", "external_id": "crm-10442", "name": "LEAD_NAME", "phone": "+15555550142" }, "campaign_id": "7c1f2b9e-4a3d-4e8f-9b21-5d6a7e8f9a01", "direction": "outbound", "status": "ended", "outcome": "interested", "outcome_label": "Interested", "note": "Wants a quote for a 6 kW system; home owner, roof replaced 2019.", "duration_seconds": 412, "started": "2026-10-06T16:58:06.000Z", "answered": "2026-10-06T16:58:14.000Z", "ended": "2026-10-06T17:04:58.000Z", "updated": "2026-10-06T17:05:20.000Z", "handled_by": { "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "name": "SDR_NAME", "role": "sdr" }, "owner": null, "tth_seconds": 9.3, "callback_at": null, "recording_available": true, "transcript_available": true, "started_by": { "type": "api", "api_key_id": "key_example" } } ], "meta": { "next_cursor": null, "has_more": false }}Errors
| Status | Codes |
|---|---|
| 400 | invalid_request |
| 401 | authentication_failed |
| 403 | permission_denied |
| 404 | not_found |
| 429 | rate_limited, too_many_concurrent_requests |
| 500 | internal_error |
Get a call
Section titled “Get a call”GET /calls/{id}
Needs calls:read. A call in another organization, or in a campaign outside the key’s, is 404.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
id |
path | string (uuid) | yes |
curl -X GET 'https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b' \ -u "$CALLVIEW_KEY_ID:$CALLVIEW_SECRET"const auth = Buffer.from(`${process.env.CALLVIEW_KEY_ID}:${process.env.CALLVIEW_SECRET}`).toString('base64');
const res = await fetch('https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b', { method: 'GET', headers: { Authorization: `Basic ${auth}` },});console.log(res.status, res.headers.get('request-id'));console.log(await res.text());import os
import requests
res = requests.request( "GET", "https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b", auth=(os.environ["CALLVIEW_KEY_ID"], os.environ["CALLVIEW_SECRET"]), timeout=30,)print(res.status_code, res.headers.get("Request-Id"))print(res.text)<?php$ch = curl_init('https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_USERPWD => getenv('CALLVIEW_KEY_ID') . ':' . getenv('CALLVIEW_SECRET'), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false,]);$body = curl_exec($ch);echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $body, "\n";200 The call
{ "data": { "id": "e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b", "object": "call", "livemode": true, "lead": { "id": "2b8e4c1d-9f3a-4b7e-8c21-6a5d4e3f2b10", "external_id": "crm-10442", "name": "LEAD_NAME", "phone": "+15555550142" }, "campaign_id": "7c1f2b9e-4a3d-4e8f-9b21-5d6a7e8f9a01", "direction": "outbound", "status": "ended", "outcome": "interested", "outcome_label": "Interested", "note": "Wants a quote for a 6 kW system; home owner, roof replaced 2019.", "duration_seconds": 412, "started": "2026-10-06T16:58:06.000Z", "answered": "2026-10-06T16:58:14.000Z", "ended": "2026-10-06T17:04:58.000Z", "updated": "2026-10-06T17:05:20.000Z", "handled_by": { "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "name": "SDR_NAME", "role": "sdr" }, "owner": null, "tth_seconds": 9.3, "callback_at": null, "recording_available": true, "transcript_available": true, "started_by": { "type": "api", "api_key_id": "key_example" } }}Errors
| Status | Codes |
|---|---|
| 400 | invalid_request |
| 401 | authentication_failed |
| 403 | permission_denied |
| 404 | not_found |
| 429 | rate_limited, too_many_concurrent_requests |
| 500 | internal_error |
A link to the call’s recording, valid for 5 minutes
Section titled “A link to the call’s recording, valid for 5 minutes”GET /calls/{id}/recording
Made on request and never stored: fetch a fresh one each time. The link is a signed storage URL that stops working at expires_at. 404 recording_not_available when there is none (recording is off for your organization, or it is past retention). Needs calls:read.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
id |
path | string (uuid) | yes |
curl -X GET 'https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/recording' \ -u "$CALLVIEW_KEY_ID:$CALLVIEW_SECRET"const auth = Buffer.from(`${process.env.CALLVIEW_KEY_ID}:${process.env.CALLVIEW_SECRET}`).toString('base64');
const res = await fetch('https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/recording', { method: 'GET', headers: { Authorization: `Basic ${auth}` },});console.log(res.status, res.headers.get('request-id'));console.log(await res.text());import os
import requests
res = requests.request( "GET", "https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/recording", auth=(os.environ["CALLVIEW_KEY_ID"], os.environ["CALLVIEW_SECRET"]), timeout=30,)print(res.status_code, res.headers.get("Request-Id"))print(res.text)<?php$ch = curl_init('https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/recording');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_USERPWD => getenv('CALLVIEW_KEY_ID') . ':' . getenv('CALLVIEW_SECRET'), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false,]);$body = curl_exec($ch);echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $body, "\n";200 A link that works for 5 minutes
{ "data": { "url": "https://storage.example.com/recordings/e4d3c2b1.mp3?X-Signature=example", "expires_at": "2026-10-06T17:10:20.000Z", "livemode": true }}Errors
| Status | Codes |
|---|---|
| 400 | invalid_request |
| 401 | authentication_failed |
| 403 | permission_denied |
| 404 | not_found, recording_not_available |
| 429 | rate_limited, too_many_concurrent_requests |
| 500 | internal_error |
| 503 | service_unavailable |
The call’s transcript
Section titled “The call’s transcript”GET /calls/{id}/transcript
The lines in order: speaker ai, lead or agent (the person who took the call), with a name and a time. ?format=text answers plain text, one “Name: text” line per turn. Needs calls:read.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
id |
path | string (uuid) | yes | |
format |
query | string | One of json / text. Default "json". |
curl -X GET 'https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/transcript' \ -u "$CALLVIEW_KEY_ID:$CALLVIEW_SECRET"const auth = Buffer.from(`${process.env.CALLVIEW_KEY_ID}:${process.env.CALLVIEW_SECRET}`).toString('base64');
const res = await fetch('https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/transcript', { method: 'GET', headers: { Authorization: `Basic ${auth}` },});console.log(res.status, res.headers.get('request-id'));console.log(await res.text());import os
import requests
res = requests.request( "GET", "https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/transcript", auth=(os.environ["CALLVIEW_KEY_ID"], os.environ["CALLVIEW_SECRET"]), timeout=30,)print(res.status_code, res.headers.get("Request-Id"))print(res.text)<?php$ch = curl_init('https://v2-api.callview.ai/api/v1/external/calls/e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b/transcript');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_USERPWD => getenv('CALLVIEW_KEY_ID') . ':' . getenv('CALLVIEW_SECRET'), CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false,]);$body = curl_exec($ch);echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $body, "\n";200 The transcript, or plain text with ?format=text
{ "data": { "call_id": "e4d3c2b1-a09f-4e8d-9c7b-6a5f4e3d2c1b", "lines": [ { "speaker": "ai", "name": "AI", "text": "Hi, is this Dana? This is Alex from Example Solar.", "at": "2026-10-06T16:58:15.000Z" }, { "speaker": "lead", "name": "LEAD_NAME", "text": "Yes, I'd like to hear what it would cost for my house.", "at": "2026-10-06T16:59:02.000Z" }, { "speaker": "agent", "name": "SDR_NAME", "text": "Hi Dana, I can help with that.", "at": "2026-10-06T16:59:12.000Z" } ], "livemode": true }}Errors
| Status | Codes |
|---|---|
| 400 | invalid_request |
| 401 | authentication_failed |
| 403 | permission_denied |
| 404 | not_found |
| 429 | rate_limited, too_many_concurrent_requests |
| 500 | internal_error |
Call this lead now
Section titled “Call this lead now”POST /calls
Puts an existing lead at the front of its campaign’s dial queue (the same queue as POST /leads with call_now): it is dialed next, never above the campaign’s lines. The same checks and the same reply as POST /leads: dialing, queued (with the place in line), scheduled (outside the lead’s calling hours) or held (a paused campaign). Do Not Call, calling hours and the other dialing rules always apply. At most 3 API-started calls per lead in 24 hours. Needs calls:write.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
Idempotency-Key |
header | string | Any string you choose (1 to 255 visible characters), new for each new action. Send the same key again within 24 hours and you get the first answer back instead of a second lead or call. At most 255 characters. |
Body
| Field | Type | Required | Notes |
|---|---|---|---|
lead_id |
string (uuid) | ||
external_id |
string | Your ID for the lead (send campaign_id too if it is in more than one campaign) | |
campaign_id |
string (uuid) |
curl -X POST 'https://v2-api.callview.ai/api/v1/external/calls' \ -u "$CALLVIEW_KEY_ID:$CALLVIEW_SECRET" \ -H 'Idempotency-Key: crm-10442-postCalls' \ -H 'Content-Type: application/json' \ -d '{ "external_id": "crm-10442"}'import { randomUUID } from 'node:crypto';
const auth = Buffer.from(`${process.env.CALLVIEW_KEY_ID}:${process.env.CALLVIEW_SECRET}`).toString('base64');
const res = await fetch('https://v2-api.callview.ai/api/v1/external/calls', { method: 'POST', headers: { Authorization: `Basic ${auth}`, 'Content-Type': 'application/json', 'Idempotency-Key': randomUUID() }, body: JSON.stringify({ "external_id": "crm-10442" }),});console.log(res.status, res.headers.get('request-id'));console.log(await res.text());import os, uuid
import requests
res = requests.request( "POST", "https://v2-api.callview.ai/api/v1/external/calls", auth=(os.environ["CALLVIEW_KEY_ID"], os.environ["CALLVIEW_SECRET"]), headers={"Idempotency-Key": str(uuid.uuid4())}, json={ "external_id": "crm-10442", }, timeout=30,)print(res.status_code, res.headers.get("Request-Id"))print(res.text)<?php$ch = curl_init('https://v2-api.callview.ai/api/v1/external/calls');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_USERPWD => getenv('CALLVIEW_KEY_ID') . ':' . getenv('CALLVIEW_SECRET'), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Idempotency-Key: ' . bin2hex(random_bytes(16))], CURLOPT_POSTFIELDS => <<<'JSON'{ "external_id": "crm-10442"}JSON, CURLOPT_HEADER => false,]);$body = curl_exec($ch);echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $body, "\n";202 Accepted. status says what happens next.
{ "data": { "lead_id": "2b8e4c1d-9f3a-4b7e-8c21-6a5d4e3f2b10", "external_id": "crm-10442", "campaign_id": "7c1f2b9e-4a3d-4e8f-9b21-5d6a7e8f9a01", "status": "dialing", "place_in_line": 1, "estimated_wait_seconds": 1, "scheduled_for": "2026-10-06T17:05:20Z", "scheduled_reason": "outside_calling_hours", "held_reason": "campaign_paused", "calls_left_today": 1, "livemode": true }}Errors