Skip to navigation

Call Metrics

Retrieve call details, transcripts, recordings, and performance data.
View as Markdown

Access detailed information about every call through the SDK.

Getting Recent Calls

Fetch a paginated list of calls:

from smallestai.atoms.call import Call
call = Call()
calls = call.get_calls(limit=5)
for log in calls["data"]["logs"]:
print(f"{log['callId']}: {log['status']} ({log['duration']}s)")

The response includes pagination info:

{
"status": true,
"data": {
"logs": [...],
"pagination": {
"total": 94,
"page": 1,
"hasMore": true,
"limit": 5
}
}
}

Filtering Calls

Narrow results using filter parameters:

from smallestai.atoms.call import Call
call = Call()
# By status
completed = call.get_calls(status="completed", limit=10)
# By agent
agent_calls = call.get_calls(agent_id="696ddd281ea16a73cb8aafbe", limit=10)
# By campaign
campaign_calls = call.get_calls(campaign_id="696ddd2a04ff172dbd8eddad", limit=10)
# By call type
outbound = call.get_calls(call_type="telephony_outbound", limit=10)
# By phone number
found = call.get_calls(search="+916366821717", limit=10)

Filter Parameters

ParameterTypeDescription
agent_idstringFilter by agent ID
campaign_idstringFilter by campaign ID
pageintPage number (default: 1)
limitintResults per page (default: 10)
statusstringcompleted, failed, in_progress, no_answer, busy
call_typestringtelephony_inbound, telephony_outbound, chat
searchstringMatch callId, fromNumber, or toNumber

Getting Single Call Details

Retrieve complete details for one call:

from smallestai.atoms.call import Call
call = Call()
call_id = "CALL-1768807723178-4561d0"
details = call.get_call(call_id)
data = details["data"]
print(f"Status: {data['status']}")
print(f"Duration: {data['duration']} seconds")
print(f"From: {data['from']} → To: {data['to']}")

Response Fields

FieldDescription
callIdUnique call identifier
statuscompleted, failed, in_progress, no_answer
durationLength in seconds
typetelephony_outbound, telephony_inbound, chat
from / toPhone numbers
transcriptArray of conversation messages
recordingUrlMono audio file URL
recordingDualUrlStereo audio file URL
callCostCost in credits
disconnectionReasonuser_hangup, agent_hangup, timeout
postCallAnalyticsAI summary and extracted metrics

Accessing Transcripts

The transcript is an array of messages with speaker roles:

details = call.get_call(call_id)
transcript = details["data"].get("transcript", [])
for msg in transcript:
print(f"{msg['role']}: {msg['content']}")

Accessing Recordings

Recordings are served through an authenticated two-step flow. Call the recording endpoint with your API key to get a short-lived presigned S3 URL, then download the audio from that URL.

The presigned URL expires after 15 minutes. Fetch a fresh one whenever you need the recording. Do not cache or embed it.

Fetch a recording

GET /atoms/v1/recordings/{callId}?channel=mono|dual returns { data: { url: "..." } } where url is the presigned S3 link.

import os, requests
CALL_ID = "CALL-1787214528441-b9eb21"
# Step 1: exchange the callId for a presigned download URL
resp = requests.get(
f"https://api.smallest.ai/atoms/v1/recordings/{CALL_ID}",
params={"channel": "mono"}, # or "dual" for stereo
headers={"Authorization": f"Bearer {os.environ['SMALLEST_API_KEY']}"},
)
resp.raise_for_status()
presigned = resp.json()["data"]["url"]
# Step 2: download the audio from the presigned URL (no auth header needed)
audio = requests.get(presigned)
audio.raise_for_status()
with open(f"{CALL_ID}.wav", "wb") as f:
f.write(audio.content)

Or in one line with curl:

curl -s -H "Authorization: Bearer $SMALLEST_API_KEY" \
"https://api.smallest.ai/atoms/v1/recordings/$CALL_ID?channel=mono" \
| jq -r .data.url \
| xargs curl -o "$CALL_ID.wav"

Channel parameter

ValueContents
mono (default)Composite recording: agent + caller mixed into a single track.
dualStereo recording: agent on one channel, caller on the other. Available when the call was captured with per-side audio.

Omitting channel returns the mono composite.

Errors

HTTPMeaning
400 Invalid call ID formatcallId must be CALL-<13-digit timestamp>-<6-hex>. Passing an internal document id fails validation.
400 Invalid channel. Expected 'mono' or 'dual'channel query param must be one of the two values.
401 UnauthorizedMissing or malformed Authorization header.
404 Call log not foundThe callId doesn’t exist in your organization. (The literal Call not found message is only returned when the call is missing entirely.)
404 Dual-channel recording not foundThe call was captured mono-only. Retry with channel=mono.

The recordingUrl and recordingDualUrl fields on the call detail response, the post-conversation webhook, and the campaign CSV export contain either a raw storage URL or the authenticated endpoint URL depending on when your organization was created. Always resolve the audio through GET /atoms/v1/recordings/{callId}. That call works either way and returns the same presigned URL shape.

Legacy path

An older endpoint, GET /atoms/v1/conversation/{callId}/recording/download-url, remains available for the mono composite only. Its response envelope is { data: { presignedUrl: "..." } }. Presigned URLs expire in 15 minutes (same TTL as the new endpoint). New integrations should use /atoms/v1/recordings/{callId} for both mono and dual and for future feature parity.


Fetch multiple calls at once with search_calls():

call_ids = ["CALL-1768807723178-4561d0", "CALL-1768807723177-4561cd"]
result = call.search_calls(call_ids)
print(f"Found {result['data']['total']} of {len(call_ids)} calls")

Maximum 100 call IDs per request.


Performance Metrics

Each call includes latency breakdowns:

details = call.get_call(call_id)
data = details["data"]
print(f"Transcriber: {data.get('average_transcriber_latency')}ms")
print(f"Agent (LLM): {data.get('average_agent_latency')}ms")
print(f"Synthesizer: {data.get('average_synthesizer_latency')}ms")
MetricDescription
average_transcriber_latencySpeech-to-text processing time
average_agent_latencyLLM response generation time
average_synthesizer_latencyText-to-speech processing time

SDK Reference

MethodDescription
get_calls(...)List calls with optional filters
get_call(call_id)Get single call with all details
search_calls(call_ids)Batch fetch by call IDs

Tips

status is the outcome (completed, failed). disconnectionReason explains why it ended:

  • user_hangup - Caller hung up
  • agent_hangup - Agent ended the call
  • dial_no_answer - No pickup
  • timeout - Call timeout

Recordings generate after the call ends. They may take a few seconds to appear. Check if recordingUrl is non-empty before accessing.

Fetch calls with get_calls(), then calculate:

calls = call.get_calls(status="completed", limit=100)
durations = [log["duration"] for log in calls["data"]["logs"]]
avg = sum(durations) / len(durations)

Use the search parameter:

calls = call.get_calls(search="+916366821717")

Currently, use pagination and filter client-side by createdAt. Date range filters are coming soon.