A call detail record is the row a telephony system writes about one call: who called whom, what happened, how long it lasted and what it cost. In most of the industry it is a billing artefact first and a debugging one second, which is why it often arrives late and in bulk. On Bird it is the same record for both jobs, readable immediately, which makes it more useful and introduces two wrinkles worth knowing before you reconcile anything against it.

## What is in a record?

Three groups of fields, answering three different questions.

**What happened.** The `status` is the outcome, one of answered, no answer, rejected, failed or unknown. Alongside it, the `SIP response` is the final SIP code, and where the call was refused by Bird rather than by a carrier there is a rejection reason naming the check it failed. Those three read together are the diagnosis, and [what SIP response codes mean](/explained/voice/what-do-sip-response-codes-mean) covers how they combine.

**When it happened.** `Started` is when Bird received the call, `Answered` is when it was picked up or the fact that it was not, and `Ended` is when it was torn down. On an incoming call, `Inbound route` records what the number that was dialed was set to do, and `Trunk` names the trunk the call came in on or was delivered to.

**What it cost.** Billable time, total duration and the call's cost.

Two identifiers sit alongside those, and they are not interchangeable. The **call ID** (`vcl_…`) is the record's own identifier, and it is the one to quote to support and to correlate against your own logs. The **session ID** (`vcs_…`) is shared by every leg of one call.

## Why does one call show up as two records?

Because a record describes a leg rather than a call, and some calls have more than one.

A forwarded call is the clearest case: the inbound leg and the outbound leg are separate records, sharing one session ID. If you count records as calls, you will overcount exactly the calls that were forwarded, and if you sum durations across records you will double-count the time they overlapped.

The session ID is the fix. Group by it when you want calls, and read the records individually when you want legs. Which you want depends on the question: a delivery problem is a leg question, and a customer-experience or volume question is usually a session one.

## Why does my cost not match the duration?

Two reasons, and both are ordinary.

**Billable time is not total duration.** Billable time runs from answer to hangup. Total duration includes the time the call spent ringing before anyone picked up. A record carries both, so a reconciliation that quietly uses the wrong one drifts by the length of the ringing on every answered call, and by the whole duration on every call that was never answered.

**The cost arrives after the call does.** A record carries its cost once it has been rated, which is not the instant the call ends, so a record fetched immediately can be complete in every other respect and carry no price. If you are computing spend in near real time, read the cost when it is there and never read its absence as zero. That is not a nicety: an absent cost and a cost of zero are different facts here, and treating them alike understates spend.

Waiting is not always the answer, though. An unanswered call has no cost and never will. An answered call that stays without one is not waiting for rating either, and the record will not tell you which of those you are looking at, so contact support with the call ID rather than continuing to poll it.

## Which calls never appear?

This is the limitation worth designing around, because a log is easy to mistake for a complete account.

**A call Bird cannot admit at all is turned away before a record exists**, so it never reaches the log. The clearest example is the trunk's IP allow list: a call from an address that is not on it is refused at the SIP layer, and it usually does not produce a record. So an attacker probing your trunk, or a misconfigured PBX sending from a new address, can generate a great deal of traffic that leaves no trace in the place you would naturally go looking.

The practical consequence is that "there is nothing in the call log" answers a narrower question than it appears to. It means no call was admitted, not that nothing was attempted. When a phone system reports that its calls are failing and the log is empty, the allow list is the first thing to check rather than the last, and [voice troubleshooting](/docs/guides/voice/troubleshooting) covers the rest of that path.

## How do I get the records out?

For one call, open it in the [call log](/docs/guides/voice/call-log) and read the fields above; the log also supports filtering and searching over the list, and shows calls in progress separately from finished ones.

For many, the voice API returns calls as records you can page through, and the voice statistics endpoints aggregate them by day, hour, country and response code, which is usually what you actually want when the question is about a pattern rather than a call. Reaching for the raw records to answer an aggregate question is the common way to spend an afternoon rebuilding something that already exists.

[The call log](/docs/guides/voice/call-log) documents every field a record carries and how the list is filtered.