Call records pulled from two VoIP providers through their APIs into one workbook, with a filterable dashboard and a records view.
Loading call records…
Total Calls
Total Outbound Dials
Connected Calls
Connection Rate
Total Call Time
Total Ringing Time
Total Talk Time
Average Talk Time
Average Call Duration
Average Calls / Day
First Call
Last Call
Change the filters above to update every KPI automatically. Duration totals use [hours]:minutes:seconds.
Data coverage and API refresh status appear in the Refresh Log sheet. The Filter Details sheet lists every record behind these numbers.
Filter Details – records matching the Dashboard filters
No records match the current Dashboard filters.
Agent Mapping
Each provider reports agent names its own way. This sheet maps every source name to one display name, so the Agent filter and the KPIs treat them as the same person.
Provider
Source Agent
Display Agent
Include
Refresh Log
Timestamp
Action
Status
Records Added
Details
What this is
Calls were tracked in two separate VoIP systems, each with its own report format. I connected both to a single Google Sheets workbook through their APIs using Google Apps Script, so call activity could be tracked in one place and records pulled without exporting from each system. This page is a web replica of that workbook: the same sheets, the same KPI formulas, and the same records.
Pipeline
VOIP 1 APISign in for an access token, then page through call records for a time window.
VOIP 2 APIAPI-key requests for call records filtered by date, plus the user list for agent names.
→
Apps Script: fetch and normalizeEach provider's fields are converted to one 19-column schema: direction, outcome, and durations in seconds.
Apps Script: de-duplicate and appendEvery call gets a "provider|call id" record key. Keys already in the sheet are skipped, so overlapping windows never double-count.
→
CDR Data sheetOne row per call from both providers.
Dashboard and Filter DetailsCOUNTIFS / SUMIFS formulas driven by six filters, and a QUERY that lists the matching records.
How it stays current
Hourly trigger. A time-driven trigger runs the refresh even when nobody has the sheet open. A menu item turns it on or off.
Overlap window. Each run re-reads from one day before the latest stored call and stops five minutes before "now", so late-arriving and still-in-progress calls are picked up on a later run.
Locking. A script lock stops a manual refresh and a scheduled one from running at the same time.
Logging. Every run writes the range requested, records fetched, records added, and duplicates skipped to the Refresh Log, including failures and the provider's error message.
History. Older calls were loaded once from each provider's CSV export, then checked against expected counts before the API refresh took over.
Credentials. API keys and logins live in Script Properties, not in the code or the sheet.