List all calls (unified history)
Unified call history across ALL of your numbers: both channels
(WhatsApp Business Calling + regular phone/PSTN), inbound and outbound,
newest first. Unlike GET /v1/voice/calls (PSTN-only) and
GET /v1/whatsapp/calls (one account at a time), this endpoint needs no
accountId and never requires fanning out one request per number.
Any row can be opened channel-agnostically via GET /v1/calls/{id} and
GET /v1/calls/{id}/recording; no branching on channel needed. When
the counterparty number matches a CRM contact, contactId and
contactName are set.
Cursor pagination: pass the returned nextCursor as before to fetch
the next page. nextCursor is null on the last page.
API key authentication - use your Zernio API key as a Bearer token
In: header
Query Parameters
"whatsapp" | "pstn""ringing" | "answered" | "ended" | "failed""inbound" | "outbound"Exact filter: calls involving this number (typically one of YOUR numbers, to scope history to a single line). E.164, leading + optional.
Free-text match on the from/to numbers. Non-digits are stripped, so partial queries like 302 or +1 302 work.
Return calls with startedAt strictly before this instant (use the previous page's nextCursor).
date-time501 <= value <= 200Response Body
application/json
application/json
import Zernio from '@zernio/node';const zernio = new Zernio({ apiKey: process.env.ZERNIO_API_KEY });const { data } = await zernio.calls.listCalls();console.log(data);{
"calls": [
{
"_id": "string",
"accountId": "string",
"conversationId": "string",
"contactId": "string",
"channel": "whatsapp",
"direction": "inbound",
"from": "string",
"to": "string",
"forwardTo": "string",
"greeting": "string",
"status": "ringing",
"isVoicemail": true,
"amd": true,
"answeredMachine": true,
"forwardCallerId": "business",
"recordingEnabled": true,
"transcriptionEnabled": true,
"transcriptionLanguage": "auto",
"startedAt": "2019-08-24T14:15:22Z",
"answeredAt": "2019-08-24T14:15:22Z",
"endedAt": "2019-08-24T14:15:22Z",
"transferredAt": "2019-08-24T14:15:22Z",
"durationSeconds": 0,
"endReason": "hangup",
"hangupCause": "string",
"sipHangupCause": "string",
"callErrors": [
{
"code": 0,
"message": "string"
}
],
"recordingUrl": "string",
"lastTranscriptSnippet": "string",
"transcript": [
{
"text": "string",
"confidence": 0,
"at": "2019-08-24T14:15:22Z"
}
],
"billing": {
"metaMinutes": 0,
"telnyxSeconds": 0,
"transcriptionSeconds": 0,
"transcriptionCostUSD": 0,
"metaCostUSD": 0,
"telnyxCostUSD": 0,
"recordingCostUSD": 0,
"billableCostUSD": 0,
"totalCostUSD": 0,
"currency": "string"
},
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z",
"contactName": "string"
}
],
"nextCursor": "2019-08-24T14:15:22Z"
}{
"error": "Unauthorized"
}Pre-validate KYC address POST
Optional early check for the address step of a Tier 4 (end-user identity) registration: validates a postal address for deliverability BEFORE the full KYC submit, so it can be corrected before any documents are uploaded. The full submit (POST /v1/phone-numbers/kyc) re-validates the address, so this call is purely a fast feedback path and skipping it is safe. Only the postal address is sent (no documents, no gov-ID fields). A region (`administrative_area`) is required by the validator; when it is omitted the pre-check is skipped and `{ ok: true, skipped: true }` is returned (the final submit still validates).
Get a call recording (any channel) GET
Channel-agnostic recording fetch: resolves a fresh, playable MP3 URL for any call regardless of channel (provider-signed URLs expire ~10 minutes after signing, so this re-signs on demand). Default responds `302 Found` redirecting to the fresh URL; pass `as=json` to receive `{ url }` instead.