#884·fonoster

CallDetailRecord.duration includes ring time; no way to get answered/talk duration

Author: psandersCreated Sep 7, 2026Updated Sep 7, 2026
Labelsenhancement

The gap

A CallDetailRecord's duration is measured from call setup, not from answer, so it includes ring time. There is no way for an API consumer to obtain the answered (billable/talk) duration of a call.

  • mods/apiserver/src/calls/createFetchSingleCall.ts:41-44 computes duration: int(v: r.endedAt) - int(v: r.startedAt).
  • startedAt comes from the call-start event's startTime (mods/apiserver/src/events/transformEvent.ts:40-45), which corresponds to the initial INVITE — before ringing.
  • No answer timestamp is recorded anywhere in the CDR pipeline, and CallDetailRecord in mods/common/src/protos/calls.proto:62-84 has no field for one.

Evidence of the size of the discrepancy

From an internal incident investigation (2026-08-30). For one call, the start event was timestamped 18:08:52.072, while the SIP 200 OK (answer) arrived at 18:08:56.145 per a packet capture — roughly 4 seconds of ring folded into duration. Ring times in that same campaign ranged from under a second up to ~26 seconds, so the error is variable and can dominate a short call.

Impact

Consumers doing per-call billing, talk-time reporting, or answer-rate analysis cannot use duration and must measure it themselves inside their voice application — which only works for calls that reach their application at all, and gives a number that does not reconcile with Fonoster's own CDR or with the call recording.

Suggested direction (a suggestion, not a prescription)

Record an answer timestamp when the channel is answered and expose it — either as a new answered_at field on CallDetailRecord alongside the existing started_at/ended_at, or as a separate answered_duration. Additive proto fields, backward compatible. Leave the existing duration semantics unchanged so nothing breaks; just make the answered figure obtainable.