Call History & Recordings
List every call across your numbers and both channels with GET /v1/calls, open one call, and fetch a fresh recording URL.
When you finish this page you can list every call on your numbers, open one and play its recording. You need at least one call on a voice-enabled number. Read history from /v1/calls, which covers both channels; act on a call (place, transfer, end) through the channel's own surface.
Which surface to use
| Surface | Job |
|---|---|
/v1/calls | Read. Lists, fetches and pulls recordings for every call on both channels (phone and WhatsApp), with contactId and contactName when the counterparty matches a CRM contact. |
/v1/voice/calls | Write for phone (PSTN) calls: place, transfer, end, browser dial, estimate. Its list, get and recording routes return phone calls only, scoped to one number with number. |
/v1/whatsapp/calls | Write for WhatsApp calls: place, check permissions, estimate. Its list, get and recording routes are scoped to one accountId. |
Read from /v1/calls; write on the channel surface. The channel-scoped reads still exist because they enforce account-scoped access, so use them only when you need that scoping.
List calls
Call GET /v1/calls. It returns every call across all of your numbers and both channels, inbound and outbound, newest first, with no accountId and no request per number. Filter with channel, direction, status, number (calls involving one of your numbers) or search (digits from either side).
import Zernio from '@zernio/node';
const zernio = new Zernio();
const { data: history } = await zernio.calls.listCalls({ query: { limit: 50 } });
for (const c of history.calls) {
console.log(c.channel, c.direction, c.status, c.durationSeconds, c.contactName);
}
// Next page: pass history.nextCursor as `before`Response (200):
{
"calls": [
{
"_id": "66e5f6a7b8c9d0e1f2a3b4c5",
"channel": "pstn",
"direction": "outbound",
"from": "+14155550100",
"to": "+13105551234",
"status": "ended",
"endReason": "hangup",
"durationSeconds": 184,
"recordingEnabled": true,
"recordingUrl": "https://...",
"lastTranscriptSnippet": null,
"contactId": null,
"contactName": null,
"conversationId": "66c3d2ae7b4f6c8d0e1f2a3b",
"startedAt": "2027-01-01T12:00:00Z",
"endedAt": "2027-01-01T12:03:04Z"
}
],
"nextCursor": "2027-01-01T12:00:00.000Z"
}Pass nextCursor as before for the next page; it is null on the last page. List rows omit transcript; lastTranscriptSnippet is the preview.
Fetch a call and its recording
Call GET /v1/calls/{id} for one call on either channel. It returns the full record, including transcript segments when transcription was on.
curl "https://zernio.com/api/v1/calls/66e5f6a7b8c9d0e1f2a3b4c5" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"call": {
"_id": "66e5f6a7b8c9d0e1f2a3b4c5",
"channel": "pstn",
"direction": "outbound",
"status": "ended",
"durationSeconds": 184,
"recordingEnabled": true,
"transcriptionEnabled": true,
"transcript": [
{ "text": "Hi, this is Acme calling about your order.", "confidence": 0.97, "at": "2027-01-01T12:00:06Z" }
],
"billing": { "telnyxSeconds": 184, "billableCostUSD": 0.135 }
}
}recordingUrl on a call record is signed and expires about 10 minutes after signing. Call GET /v1/calls/{id}/recording for a fresh one: by default it answers 302 to a playable MP3 URL; pass as=json to get { "url": "..." } instead.
curl "https://zernio.com/api/v1/calls/66e5f6a7b8c9d0e1f2a3b4c5/recording?as=json" \
-H "Authorization: Bearer $ZERNIO_API_KEY"Response (200):
{
"url": "https://recordings.example.com/66e5f6a7b8c9d0e1f2a3b4c5.mp3?token=..."
}A recording exists only when recording was enabled on the number or the call at call time.
If it fails
A 404 on GET /v1/calls/{id}/recording means the call does not exist under your key or has no recording:
{
"error": "Call not found, or no recording is available for this call",
"type": "not_found"
}Check recordingEnabled on the call record; recording has to be on before the call starts. A 502 means the recording provider lookup failed; retry. Every error uses the envelope in error handling.
Related
- List all calls, Get a call and Get a call recording: every field and filter.
- Setup: turn on recording and transcription.
- Call webhooks:
call.endedpushes each finished call to you instead of polling. - WhatsApp Calling: the other channel in this feed.