Developers · API
v1Fetch call results
A call appears in the list once it has ended and its summary is stored. Newest first.
GET /v1/tenants/{customerId}/calls — limit sets the page size (1–200, default 50); pass the previous response's nextCursor as cursor to read the next page. since returns only calls at or after the time you pass (an integer of epoch milliseconds).
Each entry carries the call identifier (the same value as the webhook's callId), a time, a one-line title, the summary, the call length, the caller's number, whether the call was handed over, the outcome, the caller's sentiment and topics. Every field and its possible values are in the API reference.
The caller's number (callerNumber) shows only the last four digits (*******5678). Transcripts and recordings are not provided, and calls the operator deleted in the console do not appear.
List or webhook — which to use
| Method | Use it when |
|---|---|
The list (GET …/calls, this page) | Showing calls on a screen, or filling a gap you missed |
Webhooks (call.completed) | You need to act the moment a call ends |
Read recent calls
TENANT_ID="<customerId from the create response>"
curl -s "$LS_BASE_URL/v1/tenants/$TENANT_ID/calls?limit=20" \
-H "Authorization: Bearer $LS_PARTNER_KEY"const BASE_URL = process.env.LS_BASE_URL;
const API_KEY = process.env.LS_PARTNER_KEY;
const tenantId = '<customerId from the create response>';
const url = new URL(`${BASE_URL}/v1/tenants/${tenantId}/calls`);
url.searchParams.set('limit', '20');
const page = await (
await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` } })
).json();
for (const call of page.calls) {
console.log(call.startedAt, call.title, call.resolution);
}import os, requests
BASE_URL = os.environ["LS_BASE_URL"]
API_KEY = os.environ["LS_PARTNER_KEY"]
tenant_id = "<customerId from the create response>"
page = requests.get(
f"{BASE_URL}/v1/tenants/{tenant_id}/calls",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"limit": 20},
).json()
for call in page["calls"]:
print(call["startedAt"], call["title"], call["resolution"]){
"calls": [
{
"callId": "6f1e0c2a-4b7d-4c1e-9a52-3f8d2f10ab34",
"startedAt": "2026-09-09T04:31:08.000Z",
"title": "Delivery status inquiry",
"summary": "The caller asked about the delivery status of an order and was told it ships today.",
"durationSecs": 74,
"callType": "phone",
"callerNumber": "*******5678",
"transferred": false,
"transferTo": null,
"resolution": "Resolved",
"sentiment": "positive",
"topics": ["delivery"]
}
],
"nextCursor": "eyJvZmZzZXQiOjIwfQ"
}
Poll for new calls only
Store the startedAt of the last call you processed and pass it as since next time to get only what came after. since includes the exact time, so a call on the boundary can come back once more — de-duplicate by callId before storing.
TENANT_ID="<customerId from the create response>"
# Only calls whose summary was stored after 2026-09-09T00:00:00Z
curl -s "$LS_BASE_URL/v1/tenants/$TENANT_ID/calls?since=1788912000000&limit=200" \
-H "Authorization: Bearer $LS_PARTNER_KEY"const BASE_URL = process.env.LS_BASE_URL;
const API_KEY = process.env.LS_PARTNER_KEY;
async function fetchCallsSince(tenantId, sinceMs) {
const calls = [];
let cursor = null;
do {
const url = new URL(`${BASE_URL}/v1/tenants/${tenantId}/calls`);
url.searchParams.set('limit', '200');
url.searchParams.set('since', String(sinceMs));
if (cursor) url.searchParams.set('cursor', cursor);
const page = await (
await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` } })
).json();
calls.push(...page.calls);
cursor = page.nextCursor;
} while (cursor);
return calls;
}import os, requests
BASE_URL = os.environ["LS_BASE_URL"]
API_KEY = os.environ["LS_PARTNER_KEY"]
def fetch_calls_since(tenant_id, since_ms):
calls, cursor = [], None
while True:
params = {"limit": 200, "since": since_ms}
if cursor:
params["cursor"] = cursor
page = requests.get(
f"{BASE_URL}/v1/tenants/{tenant_id}/calls",
headers={"Authorization": f"Bearer {API_KEY}"},
params=params,
).json()
calls += page["calls"]
cursor = page.get("nextCursor")
if not cursor:
return callsAn invalid or expired cursor answers 400 INVALID_CURSOR — start again without a cursor. A non-integer since also answers 400. All failure responses are in Errors & limits.
To be told the moment a call ends instead of polling, see Webhooks.