Skip to main content

CDR export & billing reconciliation feed

Finance reconciliation on a usage-based plan comes down to two data feeds: the per-call records behind your voice charges (call detail records, CDR) and the per-message counters behind your messaging charges (billable usage records). Devotel Orbit ships both as API-level surfaces, so your finance stack — not a shared spreadsheet — reconciles invoices. This page covers the three pieces that make up the feed:
  1. GET /messages/usage/records — per-category usage counters (Twilio /v1/Usage/Records parity), optionally split per country.
  2. GET /voice/calls/export — streamed per-call detail records, CSV or JSON.
  3. The Insights › Costs panel that renders the same counters in the dashboard.

1. Usage records — GET /messages/usage/records

Returns one counter row per billing category over a rolling window: SMS inbound/outbound, MMS inbound/outbound, and voice inbound/outbound. Each row carries its category, channel, direction, count, summed price, and currency.
Example record when country_splits=1:
Three counting rules to know before you reconcile against your invoice:
  • SMS counts by segment, MMS and voice by message or call. A multipart SMS bills per segment, so the usage counter matches what the invoice charges.
  • Inbound traffic is included. Inbound SMS/MMS and inbound voice are billable categories and appear alongside outbound.
  • Unresolvable recipients land in an UNKNOWN country bucket in the split view rather than being dropped — your split totals always reconcile to the category total.
Currency is preserved per bucket: an account that bill in more than one currency gets one record per (category, currency) pair, so mixed windows never sum apples with oranges.

2. Call detail records — GET /voice/calls/export

Exports per-call detail records as a streamed file — CSV by default, JSON on request — filtered by the same dimensions as the calls list.
Each exported row carries the call’s id, direction, from/to numbers, final status, duration, the rated price and currency for that call, SIP response code, timestamps, and a downloadable recording URL when a recording exists. The export is capped at 100,000 rows per request. Wide windows should be pulled month-by-month. When a request would exceed the cap it is rejected outright with a 422 error and code EXPORT_TOO_LARGE, which includes details.max_rows (100000) — narrow the date range or filters and retry; the failure happens before any bytes stream, so a partial file is never written. Recording URLs are pre-signed download links — the raw storage path never leaves the API.

3. Reconciling the Costs panel against the Billed messages tile

The Insights › Costs page renders the same usage-records feed next to a “Billed messages” summary tile. They intentionally count different things, and the panel explains this inline:
  • Billable usage records counts SMS by segment and includes inbound traffic.
  • Billed messages counts outbound messages that incurred a charge.
So the usage-records total being higher than the tile total is the expected relationship — it is not a discrepancy. When your finance system reconciles, use the usage-records counters (segment-level, inbound-inclusive), because that matches the invoice’s own counting.

4. Warehouse sync, if you need the rows in your own dataset

If your finance stack runs in a warehouse, the reverse-ETL mechanism mirrors rated usage records and voice call detail records to BigQuery on a schedule — the same per-call and per-message contract, landed in tables you own. See the reverse-ETL configuration guides for the usage_events and call_logs tables.