Phone, SMS & voice
Read every call across your numbers and channels from one feed, open a call with its transcript, and fetch a fresh recording link.
GET /v1/calls is one feed of every call on your numbers: phone and WhatsApp, inbound and outbound, newest first. Each call record carries its duration, end reason, cost, and the recording and transcript when those were on.
#Read from one place, act on the channel
| Surface | Use it to |
|---|---|
/v1/calls |
Read. List, fetch and get recordings for every call on both channels, with contactId and contactName when the other party is a known contact. |
/v1/voice/calls |
Act on phone calls: place, transfer, end, browser dial, estimate. Its list and get return phone calls only. |
/v1/whatsapp/calls |
Act on WhatsApp calls. See WhatsApp calling. |
#List calls
curl "https://api.pozy.ai/v1/calls?direction=inbound&status=ended&limit=50" \
-H "Authorization: Bearer $POZY_API_KEY"let before: string | undefined;
do {
const query = new URLSearchParams({ limit: "100" });
if (before) query.set("before", before);
const res = await fetch(`https://api.pozy.ai/v1/calls?${query}`, {
headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
for (const c of data.calls) console.log(c.channel, c.direction, c.status, c.durationSeconds, c.contactName);
before = data.nextCursor ?? undefined;
} while (before);import os, requests
params = {"limit": 100}
while True:
page = requests.get(
"https://api.pozy.ai/v1/calls",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
params=params,
timeout=30,
).json()
for c in page["calls"]:
print(c["channel"], c["direction"], c["status"], c.get("durationSeconds"))
if not page["nextCursor"]:
break
params["before"] = page["nextCursor"]| Filter | Values |
|---|---|
channel |
pstn (phone network) or whatsapp |
direction |
inbound or outbound |
status |
ringing, answered, ended or failed |
number |
Calls involving one of your numbers |
search |
Digits from either side of the call |
limit |
Default 50, up to 200 |
before |
The nextCursor from the previous page. It is null on the last page. |
List rows leave out the full transcript. lastTranscriptSnippet is a preview.
#Open one call
GET /v1/calls/{id} returns the full record for a call on either channel.
{
"call": {
"_id": "66e5f6a7b8c9d0e1f2a3b4c5",
"channel": "pstn",
"direction": "inbound",
"from": "+13125550188",
"to": "+14155550142",
"status": "ended",
"endReason": "hangup",
"durationSeconds": 184,
"isVoicemail": false,
"recordingEnabled": true,
"transcriptionEnabled": true,
"transcript": [
{ "text": "Hi, I am calling about order 1234.", "confidence": 0.96, "at": "2026-10-30T09:00:06Z" }
],
"billing": { "billableCostUSD": 0.135, "currency": "USD" },
"conversationId": "66c3d2ae7b4f6c8d0e1f2a3b"
}
}| Field | Meaning |
|---|---|
endReason |
hangup, no_answer, rejected or error |
hangupCause, sipHangupCause |
The raw carrier cause and SIP code behind endReason, for example to tell a busy line from a refused destination |
isVoicemail |
The call was taken by voicemail |
answeredMachine |
With answering-machine detection on, whether a machine picked up |
callErrors |
Problems recorded during the call (failed bridge, recording error). The message text is for humans and may change, so do not parse it. |
billing.billableCostUSD |
What Pozy bills for the call |
#Get the recording
recordingUrl on a call record is signed and expires about 10 minutes after it is issued. For a fresh link, call GET /v1/calls/{id}/recording. By default it redirects (302) to a playable MP3. Add as=json to get { "url": "..." } instead.
curl "https://api.pozy.ai/v1/calls/66e5f6a7b8c9d0e1f2a3b4c5/recording?as=json" \
-H "Authorization: Bearer $POZY_API_KEY"Fetch the link when someone presses play rather than storing it. If you need to keep recordings, download the file and store it yourself.
#Turn recording and transcription on
Both are off by default. Set them on the number for every call, or per call when you dial out.
| Where | Fields |
|---|---|
| Every call on a number | recordingEnabled, transcriptionEnabled, transcriptionLanguage on POST /v1/phone-numbers/{id}/voice |
| One outbound call | recordOverride, transcribeOverride, transcriptionLanguage on POST /v1/voice/calls |
| One browser call | recordOverride on POST /v1/voice/calls/web/dial |
Recording never starts silently: on bridged calls a consent notice plays first. A recording exists only if recording was on when the call started. Voicemail messages are recorded whenever voicemail takes a call.
Recording and transcription laws differ by country and state. Some places require every party to agree before a call is recorded. The consent notice helps, but you are responsible for using recordings lawfully.
#What it costs
Recording and transcription are billed per minute on top of the call. See the usage table on Phone numbers or Pricing.
#Get calls pushed instead of polling
call.ended carries the finished call (duration, end reason, cost, and a signed recordingUrl with recordingExpiresAt when recording was on). The transcript is not in the event: fetch the call record when you need it.
| Event | When it fires |
|---|---|
call.ended |
Call ended. |
call.failed |
Call failed. |
#Errors
| Status | Cause | Fix |
|---|---|---|
404 |
The call does not exist for this key, or it has no recording | Check recordingEnabled on the call. Recording must be on before the call starts. |
502 |
The recording lookup failed | Retry |
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/calls |
List all calls (unified history) |
GET /v1/calls/{id} |
Get a call (any channel) |
GET /v1/calls/{id}/recording |
Get a call recording |