·16 min·3 views

Asterisk Analytics for BPO & Outsourced Call Centers: The Complete Guide

How to build multi-client analytics for outsourced Asterisk call centers. SLA tracking, automated client reporting, agent performance scoring.

A
Astervis
Engineering & product team

Every BPO running Asterisk faces the same invisible crisis: your clients demand proof that you're hitting SLA targets, your operations manager needs real-time visibility across campaigns, and your agents need instant feedback on performance. Yet most BPOs still rely on end-of-day spreadsheets or cobbled-together Grafana dashboards that took 200 hours to build.

This guide covers exactly how to build analytics infrastructure for an outsourced Asterisk call center — from multi-client SLA tracking to automated client reporting, agent performance scoring, and revenue optimization.

Why BPO Analytics Differs from In-House Call Centers

An in-house call center tracks metrics for one organization. A BPO juggles multiple clients, each with different:

DimensionIn-HouseBPO
SLA targetsOne set of targetsDifferent per client contract
Queue configurationUnified queuesSegregated queues per campaign
Agent allocationFixed teamsShared agents across campaigns
ReportingInternal dashboardsClient-facing reports + internal ops
BillingN/APer-minute, per-call, or per-seat
Data isolationSingle tenantMulti-tenant with strict boundaries
ComplianceOne regulatory frameworkMultiple (varies by client industry)

This complexity creates unique analytics requirements that generic monitoring tools don't address. Most Asterisk monitoring solutions assume a single-tenant environment. BPOs need multi-dimensional analytics that segment everything by client, campaign, queue, and time period simultaneously.

The 12 KPIs Every BPO Must Track (and What They Actually Mean)

Client-Facing KPIs (What Goes in Client Reports)

These metrics directly impact client satisfaction and contract renewal:

KPIFormulaBPO TargetWhy It Matters
Service LevelCalls answered within X seconds ÷ Total calls × 100Per contract (typically 80/20 or 80/30)Primary SLA metric. Penalties for non-compliance.
Average Speed of Answer (ASA)Total wait time ÷ Total calls answered< 30 secondsClient-facing quality indicator
Abandonment RateAbandoned calls ÷ Total inbound calls × 100< 5%Revenue loss indicator
First Call Resolution (FCR)Resolved on first call ÷ Total calls × 100> 70%Cost efficiency and satisfaction
Average Handle Time (AHT)(Talk time + Hold time + ACW) ÷ Total callsVaries by campaignDrives cost-per-call calculations
CSAT ScorePost-call survey average> 4.0/5.0Client retention metric

Operational KPIs (Internal BPO Management)

KPIFormulaBPO TargetWhy It Matters
Agent Utilization(Handle time ÷ Logged-in time) × 10075-85%Revenue per seat optimization
Schedule AdherenceTime in schedule ÷ Scheduled time × 100> 95%Workforce planning accuracy
Cost Per CallTotal campaign cost ÷ Total calls handledVariesProfitability per contract
Revenue Per Seat HourCampaign revenue ÷ (Agents × Hours)Track trendBusiness health metric
Occupancy RateHandle time ÷ (Handle time + Available time) × 10080-90%Capacity planning
ShrinkageNon-productive time ÷ Paid time × 100< 30%True capacity indicator

The BPO Profitability Formula

Margin per campaign = (Revenue per seat hour × Seats × Hours) - (Agent cost + Infrastructure + Overhead)

Track this daily. If a campaign drops below 15% margin, investigate immediately: either agent utilization is low, AHT is creeping up, or the contract needs renegotiation.

Multi-Client Queue Architecture in Asterisk

The foundation of BPO analytics is proper queue segmentation. Here's a production-ready architecture:

Queue Naming Convention

; Format: {client}_{campaign}_{type} ; Examples: [acme_sales_inbound] ; Acme Corp - Sales - Inbound [acme_support_inbound] ; Acme Corp - Support - Inbound [globex_billing_inbound] ; Globex Inc - Billing - Inbound [globex_collections_outbound] ; Globex Inc - Collections - Outbound

queues.conf — Multi-Client Setup

; === Client: Acme Corp === ; Contract: 80/20 SLA, max 3 min wait [acme_sales_inbound] strategy = rrmemory timeout = 15 retry = 5 maxlen = 50 servicelevel = 20 weight = 2 announce-frequency = 30 announce-holdtime = once monitor-format = wav eventwhencalled = vars eventmemberstatus = yes [acme_support_inbound] strategy = leastrecent timeout = 20 retry = 5 maxlen = 30 servicelevel = 30 weight = 1 ; === Client: Globex Inc === ; Contract: 80/30 SLA, max 5 min wait [globex_billing_inbound] strategy = rrmemory timeout = 20 retry = 5 maxlen = 40 servicelevel = 30 weight = 1 [globex_collections_outbound] strategy = linear timeout = 25 retry = 3 maxlen = 0

Agent Assignment with Penalties

; agents.conf or queue members ; Shared agents across campaigns with skill-based routing ; Agent 101: Primary Acme Sales, Secondary Globex Billing member => SIP/101,1,Agent101,SIP/101 ; Acme Sales (penalty 1 = primary) member => SIP/101,3,Agent101,SIP/101 ; Globex Billing (penalty 3 = overflow) ; Agent 102: Primary Globex, Secondary Acme member => SIP/102,1,Agent102,SIP/102 ; Globex Billing (primary) member => SIP/102,3,Agent102,SIP/102 ; Acme Sales (overflow)

SQL Queries for Multi-Client Analytics

All these queries work with standard Asterisk CDR/queue_log stored in PostgreSQL (or MySQL with minor adjustments).

1. Real-Time SLA Dashboard by Client

-- Real-time SLA compliance per client (today) WITH queue_clients AS ( SELECT CASE WHEN queuename LIKE 'acme_%' THEN 'Acme Corp' WHEN queuename LIKE 'globex_%' THEN 'Globex Inc' WHEN queuename LIKE 'initech_%' THEN 'Initech LLC' ELSE 'Unknown' END AS client, queuename, event, data1::int AS wait_time, time FROM queue_log WHERE time >= CURRENT_DATE AND event IN ('CONNECT', 'ABANDON', 'EXITWITHTIMEOUT') ), sla_calc AS ( SELECT client, COUNT(*) FILTER (WHERE event = 'CONNECT') AS answered, COUNT(*) FILTER (WHERE event = 'CONNECT' AND wait_time <= 20) AS answered_in_sla, COUNT(*) FILTER (WHERE event IN ('ABANDON', 'EXITWITHTIMEOUT')) AS abandoned, COUNT(*) AS total_calls, ROUND(AVG(wait_time) FILTER (WHERE event = 'CONNECT'), 1) AS avg_speed_answer, MAX(wait_time) AS max_wait FROM queue_clients GROUP BY client ) SELECT client, total_calls, answered, abandoned, ROUND(100.0 * answered_in_sla / NULLIF(answered, 0), 1) AS sla_pct, ROUND(100.0 * abandoned / NULLIF(total_calls, 0), 1) AS abandon_pct, avg_speed_answer AS asa_seconds, max_wait AS longest_wait FROM sla_calc ORDER BY client;

2. Agent Performance Across Campaigns

-- Which agents are most productive across which campaigns? SELECT agent, CASE WHEN queuename LIKE 'acme_%' THEN 'Acme Corp' WHEN queuename LIKE 'globex_%' THEN 'Globex Inc' ELSE 'Other' END AS client, COUNT(*) FILTER (WHERE event = 'CONNECT') AS calls_handled, ROUND(AVG(data2::int) FILTER (WHERE event = 'COMPLETECALLER' OR event = 'COMPLETEAGENT'), 0) AS avg_talk_time, ROUND(AVG(data1::int) FILTER (WHERE event = 'CONNECT'), 0) AS avg_wait_given, COUNT(*) FILTER (WHERE event = 'RINGNOANSWER') AS missed_calls FROM queue_log WHERE time >= CURRENT_DATE - INTERVAL '7 days' AND event IN ('CONNECT', 'COMPLETECALLER', 'COMPLETEAGENT', 'RINGNOANSWER') GROUP BY agent, client ORDER BY client, calls_handled DESC;

3. Hourly Campaign Heatmap

-- Call volume and SLA by hour per campaign (for staffing optimization) SELECT EXTRACT(HOUR FROM time) AS hour, queuename AS campaign, COUNT(*) AS total_calls, COUNT(*) FILTER (WHERE event = 'CONNECT') AS answered, COUNT(*) FILTER (WHERE event = 'ABANDON') AS abandoned, ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'CONNECT' AND data1::int <= 20) / NULLIF(COUNT(*) FILTER (WHERE event = 'CONNECT'), 0), 1) AS sla_pct, ROUND(AVG(data1::int) FILTER (WHERE event = 'CONNECT'), 0) AS asa FROM queue_log WHERE time >= CURRENT_DATE AND event IN ('CONNECT', 'ABANDON', 'EXITWITHTIMEOUT') GROUP BY hour, campaign ORDER BY campaign, hour;

4. Revenue and Profitability by Campaign

-- Campaign profitability analysis (requires billing config table) WITH campaign_billing AS ( -- Replace with your actual billing rates SELECT 'acme_sales_inbound' AS queue, 0.85 AS rate_per_minute, 'per_minute' AS model UNION ALL SELECT 'globex_billing_inbound', 1.20, 'per_minute' UNION ALL SELECT 'initech_support_inbound', 15.00, 'per_call' ), call_stats AS ( SELECT queuename, COUNT(*) FILTER (WHERE event IN ('COMPLETECALLER', 'COMPLETEAGENT')) AS completed_calls, SUM(data2::int) FILTER (WHERE event IN ('COMPLETECALLER', 'COMPLETEAGENT')) AS total_talk_seconds FROM queue_log WHERE time >= DATE_TRUNC('month', CURRENT_DATE) AND event IN ('COMPLETECALLER', 'COMPLETEAGENT') GROUP BY queuename ) SELECT cs.queuename AS campaign, cs.completed_calls, ROUND(cs.total_talk_seconds / 60.0, 0) AS total_minutes, cb.model AS billing_model, CASE WHEN cb.model = 'per_minute' THEN ROUND(cs.total_talk_seconds / 60.0 * cb.rate_per_minute, 2) WHEN cb.model = 'per_call' THEN cs.completed_calls * cb.rate_per_minute END AS estimated_revenue FROM call_stats cs JOIN campaign_billing cb ON cs.queuename = cb.queue ORDER BY estimated_revenue DESC;

5. Client SLA Breach Alert

-- Detect campaigns at risk of SLA breach (rolling 30-minute window) WITH rolling AS ( SELECT queuename, COUNT(*) FILTER (WHERE event = 'CONNECT' AND data1::int <= 20) AS in_sla, COUNT(*) FILTER (WHERE event = 'CONNECT') AS answered, COUNT(*) FILTER (WHERE event = 'ABANDON') AS abandoned FROM queue_log WHERE time >= NOW() - INTERVAL '30 minutes' AND event IN ('CONNECT', 'ABANDON', 'EXITWITHTIMEOUT') GROUP BY queuename ) SELECT queuename AS campaign, ROUND(100.0 * in_sla / NULLIF(answered, 0), 1) AS current_sla, abandoned, CASE WHEN 100.0 * in_sla / NULLIF(answered, 0) < 70 THEN 'CRITICAL' WHEN 100.0 * in_sla / NULLIF(answered, 0) < 80 THEN 'WARNING' ELSE 'OK' END AS status FROM rolling WHERE answered > 0 ORDER BY current_sla ASC;

Automated Client Reporting

BPO clients expect regular reports. Here's how to automate them:

Weekly Report Structure

Every client report should include these sections:

  1. Executive Summary — SLA compliance, total calls, key highlights
  2. Service Level Performance — Daily SLA chart, target vs actual
  3. Call Volume Analysis — Hourly distribution, peak patterns, trends
  4. Agent Performance — Top/bottom performers, training recommendations
  5. Quality Metrics — FCR, CSAT, transfer rates
  6. Recommendations — Data-driven suggestions for improvement

Generating Reports with SQL

-- Weekly executive summary for client report SELECT DATE(time) AS report_date, COUNT(*) FILTER (WHERE event IN ('CONNECT', 'ABANDON')) AS total_offered, COUNT(*) FILTER (WHERE event = 'CONNECT') AS total_answered, COUNT(*) FILTER (WHERE event = 'ABANDON') AS total_abandoned, ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'CONNECT') / NULLIF(COUNT(*) FILTER (WHERE event IN ('CONNECT', 'ABANDON')), 0), 1) AS answer_rate, ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'CONNECT' AND data1::int <= 20) / NULLIF(COUNT(*) FILTER (WHERE event = 'CONNECT'), 0), 1) AS sla_pct, ROUND(AVG(data1::int) FILTER (WHERE event = 'CONNECT'), 0) AS avg_wait_sec, ROUND(AVG(data2::int) FILTER (WHERE event IN ('COMPLETECALLER', 'COMPLETEAGENT')), 0) AS avg_talk_sec FROM queue_log WHERE queuename LIKE 'acme_%' AND time >= CURRENT_DATE - INTERVAL '7 days' AND event IN ('CONNECT', 'ABANDON', 'COMPLETECALLER', 'COMPLETEAGENT') GROUP BY DATE(time) ORDER BY report_date;

Bash Script for Automated PDF Reports

#!/bin/bash # generate_client_report.sh — Run weekly via cron # Requires: psql, python3, wkhtmltopdf CLIENT="acme" WEEK_START=$(date -d "last monday" +%Y-%m-%d) WEEK_END=$(date -d "last sunday" +%Y-%m-%d) REPORT_DIR="/var/reports/${CLIENT}/${WEEK_START}" mkdir -p "$REPORT_DIR" # Extract data psql -h localhost -U asterisk -d asterisk_cdr -t -A -F',' \ -c "SELECT DATE(time), COUNT(*) FILTER (WHERE event='CONNECT'), COUNT(*) FILTER (WHERE event='ABANDON'), ROUND(AVG(data1::int) FILTER (WHERE event='CONNECT'),0) FROM queue_log WHERE queuename LIKE '${CLIENT}_%' AND time BETWEEN '${WEEK_START}' AND '${WEEK_END}' GROUP BY DATE(time) ORDER BY 1" \ > "${REPORT_DIR}/daily_stats.csv" echo "Report generated: ${REPORT_DIR}" # Convert to PDF, email to client contact

Five Dashboards Every BPO Needs

1. Client SLA Wallboard (Real-Time)

Purpose: Large screen display showing live SLA status for all clients.

WidgetContentUpdate
Client SLA cardsCurrent SLA % with green/yellow/redEvery 10 seconds
Queue depthCallers waiting per campaignReal-time
Longest waitCurrent max wait timeReal-time
Agent status gridAvailable / On call / ACW / OfflineReal-time
Abandon tickerRolling 30-min abandon countEvery minute

2. Campaign Operations Dashboard

Purpose: Operations manager view for cross-campaign resource allocation.

WidgetContent
Campaign comparison tableSLA, ASA, AHT, utilization side-by-side
Agent allocation matrixWhich agents on which campaigns
Overflow alertsCampaigns needing additional agents
Hourly forecast vs actualPredicted vs actual call volume

Tired of guessing what's happening in your queues?

Astervis gives you 30+ real-time charts, operator KPIs, and CRM integration for your Asterisk PBX. Self-hosted. Install in 5 minutes. From $119/mo flat unlimited operators.

Try Free

3. Agent Leaderboard

Purpose: Real-time agent ranking to drive performance.

WidgetContent
Top 10 agents (calls handled)Ranked by volume today
Quality leadersHighest FCR, shortest AHT
Coaching flagsAgents with high transfer rate or long ACW
Login/break trackingCurrent status and time in each state

4. Financial Dashboard

Purpose: CFO/CEO view of BPO profitability.

WidgetContent
Revenue by campaignMTD revenue per client
Cost per call trend30-day trend line
Margin by campaignRevenue minus agent/infra cost
Utilization efficiencyRevenue per paid agent hour

5. Client Portal Dashboard

Purpose: Read-only dashboard shared with clients via secure link.

WidgetContent
SLA performance chartDaily SLA for current month
Call volume trendsHourly and daily patterns
Quality metricsFCR, CSAT, AHT trends
HighlightsAutomated commentary on changes

Three Approaches to BPO Analytics on Asterisk

Approach 1: Build It Yourself (Grafana + PostgreSQL)

Effort: 120-200 hours initial setup, 10-20 hours/month maintenance

What you need:

  • PostgreSQL with CDR and queue_log tables
  • Grafana with PostgreSQL datasource
  • Custom SQL queries for each dashboard
  • Role-based access for client isolation
  • Scheduled report generation scripts

Pros: Full customization, no per-agent licensing cost Cons: Massive engineering time, fragile, no support, every new client needs custom work

Approach 2: QueueMetrics

Effort: 2-4 weeks setup, ongoing license cost

Cost: CHF 8/agent/month (50 agents = ~$480/month)

What you get: Queue monitoring, basic reporting, wallboard What you don't get: Multi-client revenue tracking, automated client reports, modern UI, real-time campaign comparison, easy agent reallocation

Pros: Industry standard, stable Cons: Expensive at scale, legacy Java UI, limited BPO-specific features, no built-in client portal

Approach 3: Astervis

Effort: 15 minutes to install, immediate results

Cost: From $49/month (10 agents) to $199/month (50 agents)

What you get:

  • 30+ pre-built dashboards including queue heatmaps, agent performance, and trunk analytics
  • Real-time monitoring across all campaigns simultaneously
  • Operator management with KPI tracking and leaderboards
  • CRM integration (Bitrix24, AmoCRM) for lead tracking
  • Self-hosted — your data stays on your servers
  • One-command install: curl -sSL https://get.astervis.io | bash

Why it works for BPOs:

  • Multi-queue visibility: See all client campaigns in one view
  • Agent performance tracking: Know who's excelling and who needs coaching
  • Cost efficiency: $199/month vs $480/month (QueueMetrics for 50 agents) or 200 hours of Grafana engineering
  • Self-hosted: Critical for BPOs with data residency requirements
  • 14-day free trial: No credit card, no commitment

Comparison Matrix

FeatureDIY GrafanaQueueMetricsAstervis
Setup time120-200 hours2-4 weeks15 minutes
Multi-queue real-timeCustom buildLimited✅ Built-in
Agent performanceCustom SQLBasic✅ 30+ charts
HeatmapsCustom✅ Built-in
CRM integrationCustom✅ Bitrix24, AmoCRM
Cost (50 agents)$0 + 200hr eng~$480/month$199/month
Self-hosted
Modern UIDepends❌ Legacy Java✅ Modern web
SupportCommunityPaidIncluded
Maintenance10-20 hr/monthLowZero

Common BPO Analytics Mistakes

1. Tracking Too Many Metrics

Problem: 50+ KPIs in client reports. Nobody reads them. Fix: 5-7 metrics per client report, aligned to their contract SLAs.

2. Reporting Lag

Problem: Client gets last week's report on Wednesday. Useless for real-time decisions. Fix: Real-time dashboards + automated daily email summaries + weekly detailed reports.

3. No Per-Campaign Cost Tracking

Problem: You know total costs but not which campaigns are profitable. Fix: Track agent time per queue using queue_log, calculate cost per handled call per campaign.

4. Shared Agent Blind Spots

Problem: Agent works across 3 campaigns but you only see aggregate stats. Fix: Track performance per queue per agent. Identify which campaigns each agent excels at.

5. Ignoring Abandoned Call Patterns

Problem: You report "5% abandonment" but don't analyze when and why. Fix: Track abandonment by hour, day, queue position, and wait time. Most abandons happen in specific patterns that are fixable with staffing adjustments.

6. Manual Client Reporting

Problem: Someone spends 4 hours every Friday building Excel reports for 10 clients. Fix: Automate SQL → template → PDF → email. Zero human time after setup.

Getting Started: BPO Analytics in 30 Minutes

Step 1: Audit Your Queue Structure (5 minutes)

Verify your queues follow a client_campaign_type naming convention. If not, rename them — this is the foundation of multi-client analytics.

asterisk -rx "queue show" | grep -E "^[a-z]"

Step 2: Verify Data Collection (5 minutes)

Ensure queue_log and CDR are writing to your database:

# Check queue_log is flowing tail -5 /var/log/asterisk/queue_log # Check CDR database psql -U asterisk -c "SELECT COUNT(*) FROM cdr WHERE calldate >= CURRENT_DATE"

Step 3: Install Astervis (5 minutes)

curl -sSL https://get.astervis.io | bash

Follow the setup wizard — it auto-detects your Asterisk configuration and begins importing data immediately.

Step 4: Configure Campaign Views (10 minutes)

Group your queues by client in Astervis. Set up agent assignments and SLA targets per campaign.

Step 5: Set Up Alerts (5 minutes)

Configure alerts for:

  • SLA dropping below contract threshold
  • Abandonment rate exceeding 5%
  • Queue depth exceeding maximum
  • Agent availability dropping below minimum

Key Takeaways

  1. BPO analytics is fundamentally different from in-house — multi-tenant, multi-SLA, multi-billing model complexity requires purpose-built tools.
  2. Track 12 core KPIs split between client-facing (SLA, ASA, FCR) and operational (utilization, cost per call, revenue per seat).
  3. Queue naming convention is the foundation — use client_campaign_type format for automatic segmentation.
  4. Automate client reporting — manual report generation doesn't scale past 3 clients.
  5. Monitor profitability per campaign — knowing aggregate numbers hides underperforming contracts.
  6. Astervis provides BPO-ready analytics in 15 minutes vs 200+ hours of DIY engineering, with 30+ dashboards, real-time monitoring, and CRM integration at $199/month for 50 agents.

Start your free 14-day trial at astervis.io — no credit card required. See every client campaign, every agent metric, and every SLA target in one dashboard within minutes.

Stop guessing. Start monitoring.

See your Asterisk call center's real performance — queue wait times, agent activity, trunk usage, and 30+ charts. Self-hosted on your server. Install in 5 minutes. No credit card required.

From $119/mo flat. Unlimited operators. 14-day free trial.

Share this article