Designing Trunk Capacity Forecasting Models Based on Erlang-B Calculations and Growth Trends

Designing Trunk Capacity Forecasting Models Based on Erlang-B Calculations and Growth Trends

What This Guide Covers

This guide establishes a rigorous methodology for sizing SIP trunk capacity in Genesys Cloud and NICE CXone environments. It moves beyond static historical averages to build a dynamic forecasting model that accounts for seasonal volatility, compound growth rates, and the statistical probability of call blocking (Grade of Service). By the end of this process, you will have a documented capacity plan that prevents revenue loss from busy signals while avoiding the capital expenditure of over-provisioned bandwidth.

Prerequisites, Roles & Licensing

  • Platform Access: Administrator access to Genesys Cloud (Telephony Administrator role) or NICE CXone (Telephony Administrator role).
  • Data Access: Read access to historical telephony analytics (Genesys Cloud Analytics API or CXone Reporting).
  • Licensing: Standard CX licensing is sufficient for configuration, but accurate historical data often requires the Analytics Premium add-on for granular time-series data retention beyond 30 days.
  • External Dependencies:
    • Access to carrier provisioning portals to verify current port counts.
    • A spreadsheet application or Python environment for modeling.
    • Historical traffic data spanning at least 12 months to capture seasonal baselines.

The Implementation Deep-Dive

1. Establishing the Statistical Baseline Using Erlang-B

The foundation of trunk capacity is not the average number of concurrent calls, but the peak concurrent calls required to meet a specific Grade of Service (GoS). In contact centers, a GoS of 0.01 (1% call blocking probability) is the industry standard for inbound voice traffic. Using the Erlang-B formula, we calculate the number of trunks ($N$) required to handle a specific traffic intensity ($A$, in Erlangs) with a blocking probability ($B$).

The Erlang-B formula is recursive:

$$ B(N, A) = \frac{\frac{A^N}{N!}}{\sum_{i=0}^{N} \frac{A^i}{i!}} $$

Where:

  • $A$ = Traffic Intensity in Erlangs (Total Call Volume $\times$ Average Handle Time / 3600 seconds).
  • $N$ = Number of Trunks (Circuits).
  • $B$ = Grade of Service (Target Blocking Probability, e.g., 0.01).

The Trap: The most common misconfiguration is sizing trunks based on the average concurrent calls reported in the dashboard. If your average concurrent calls are 50, provisioning 50 trunks results in a blocking rate of approximately 50% during peak moments. You must size for the 95th or 99th percentile of concurrent usage, not the mean.

To determine $A$ accurately, you must isolate the “Busy Hour” or “Busy Interval” (typically 15-minute or 1-hour windows). Do not use daily totals.

Architectural Reasoning:
SIP trunks are stateless in terms of media but stateful in terms of signaling. Each concurrent call consumes one SIP dialog. If the trunk group is exhausted, the SIP server returns a 486 Busy Here or 503 Service Unavailable. Unlike agent queues, where calls can be buffered, trunk exhaustion results in immediate caller termination. Therefore, the Erlang-B calculation must be conservative.

Calculation Workflow

  1. Extract Peak Interval Data: Pull the maximum concurrent calls for each 15-minute interval over the last 12 months.
  2. Determine Peak Factor: Identify the single highest concurrent call count. This is your raw peak.
  3. Apply Safety Margin: Add a 10-15% buffer to the raw peak to account for anomalies not captured in the historical window (e.g., marketing campaigns, system outages causing retry storms).
  4. Solve for $N$: Use an Erlang-B calculator or table to find the number of trunks required to support the adjusted peak with a GoS of 0.01.

Example:

  • Historical Max Concurrent Calls: 120
  • Safety Margin (15%): $120 \times 1.15 = 138$ concurrent calls.
  • Average Handle Time (AHT): 240 seconds (4 minutes).
  • Traffic Intensity ($A$) for the peak interval: This step is often skipped by engineers who simply map 1 concurrent call to 1 trunk. While 1 concurrent call = 1 trunk is functionally true for the instant of the peak, Erlang-B helps you understand the efficiency of those trunks. However, for SIP trunking, the simpler rule is: Trunks = Peak Concurrent Calls + Buffer. The Erlang-B model is primarily used to validate that your buffer is statistically sufficient to keep blocking below 1%.

2. Integrating Compound Growth Trends

Historical data is backward-looking. To forecast future capacity, you must apply a growth model. Contact centers typically experience compound annual growth rate (CAGR) due to business expansion, seasonality, or new product launches.

The Trap: Applying a linear growth model to exponential business trends. If a company is growing by 20% year-over-year, a linear projection will severely under-provision capacity by year three. Conversely, applying a high growth rate to a mature, flat-line business leads to wasted spend.

Architectural Reasoning:
Capacity planning must be decoupled from immediate provisioning. Carriers often have lead times of 4-8 weeks for new SIP trunk ports. Your model must predict the need for capacity before the current trunk group hits its threshold.

The Forecasting Formula

Use the following formula to project future peak concurrent calls ($P_{future}$):

$$ P_{future} = P_{current} \times (1 + r)^t $$

Where:

  • $P_{current}$ = Current Peak Concurrent Calls (from Step 1).
  • $r$ = Annual Growth Rate (e.g., 0.15 for 15%).
  • $t$ = Time in years (e.g., 0.25 for a quarter).

Implementation Steps:

  1. Determine Current Baseline ($P_{current}$): Use the peak value derived from the Erlang-B analysis.
  2. Identify Growth Drivers: Consult with business stakeholders to determine if growth is organic (CAGR) or event-driven (campaigns).
  3. Calculate Future Peak: Apply the formula for the target horizon (e.g., 6 months, 1 year).
  4. Adjust for Seasonality: If the target horizon falls within a known peak season (e.g., Q4 for retail), multiply the result by the seasonal index (e.g., 1.3 for a 30% seasonal spike).

Example:

  • Current Peak: 138 trunks.
  • Annual Growth: 20% ($r = 0.20$).
  • Horizon: 1 Year ($t = 1$).
  • Seasonal Spike (Q4): 30% additional load.

$$ P_{future} = 138 \times (1.20)^1 = 165.6 $$
$$ P_{adjusted} = 165.6 \times 1.30 = 215.28 $$

Round up to 216 trunks. This is your target capacity for the next Q4.

3. Platform-Specific Configuration & Validation

Once the model provides a target trunk count, you must configure the platform to utilize these trunks efficiently. The configuration differs slightly between Genesys Cloud and NICE CXone.

Genesys Cloud CX Implementation

In Genesys Cloud, trunks are defined as Telephony Trunks within the Telephony section of Admin.

  1. Trunk Group Strategy: Do not add individual trunks to a single group if the count exceeds 50. Create multiple trunk groups (e.g., TRUNK_GROUP_A, TRUNK_GROUP_B) and add them to a Trunk Group Collection. This allows for load balancing across carriers and simplifies failover testing.
  2. Concurrency Limits: Set the Maximum Concurrent Calls on each trunk object to match the carrier’s provisioned limit. If the carrier provisions 100 ports, set the limit to 100. If you set it higher, Genesys will attempt to place calls that the carrier will reject, causing a 403 Forbidden error.
  3. Usage Policy: Configure the Usage Policy to “Load Balance” across the collection. This ensures even distribution.

The Trap: Misconfiguring the Dialing Rules or Routing Rules to prefer a trunk group that is already at capacity. If you have two trunk groups, A (100 ports, 90 in use) and B (100 ports, 10 in use), and your routing rule prefers A, you will block calls on A while B remains idle. Always use “Least Outbound Calls” or “Load Balance” strategies in your dialing rules.

NICE CXone Implementation

In NICE CXone, trunks are managed via Telephony > Trunks.

  1. Trunk Definition: Define each SIP trunk with the correct User Name, Password, and Realm provided by the carrier.
  2. Concurrency Settings: Set the Max Concurrent Calls accurately. CXone will enforce this limit internally. If the carrier limit is lower than the CXone setting, you will experience call failures.
  3. Trunk Groups: Assign trunks to Trunk Groups. Use the Load Balancing algorithm. CXone supports Weighted Load Balancing, which is useful if you have multiple carriers with different costs. Assign higher weights to cheaper carriers, but ensure the weight does not exceed the capacity of that carrier.

The Trap: Ignoring SIP OPTIONS probing. Ensure that SIP OPTIONS are enabled on your trunk settings. This allows the platform to check the health of the trunk before sending media. If OPTIONS are disabled, the platform may route calls to a dead trunk, resulting in a timeout.

4. Automating Capacity Alerts

Manual forecasting is prone to human error. Implement automated alerts to trigger when current usage approaches the forecasted limits.

Genesys Cloud Webhooks & Flows

  1. Create a Flow: Build a Flow that triggers on a Schedule (e.g., every 5 minutes).
  2. Data Lookup: Use the Get Telephony Trunk Statistics data lookup to retrieve the current concurrent call count for each trunk group.
  3. Comparison: Use a Decision node to compare the current count against a threshold (e.g., 80% of the forecasted peak).
  4. Action: If the threshold is exceeded, send a webhook to Slack, Teams, or your ITSM tool.

The Trap: Setting the threshold too low. If you alert at 50% capacity, you will generate noise. Alert at 80% for warning, and 90% for critical.

NICE CXone Reporting & Alerts

  1. Create a Report: Build a real-time report showing Active Calls per trunk group.
  2. Set Up Alert: Use the Alerts feature to trigger an email or webhook when the active calls exceed a defined value.
  3. Dynamic Thresholds: Use a calculated field in the report to compare active calls against the total provisioned trunks. Set the alert to trigger when the ratio exceeds 0.8.

The Trap: Not accounting for Call Setup Time. The alert should trigger based on concurrent calls, not just call attempts. If you alert on call attempts, you may get false positives during periods of high retry activity.

Validation, Edge Cases & Troubleshooting

Edge Case 1: The Retry Storm

The Failure Condition:
A carrier experiences a brief outage. Calls fail. The caller’s phone or the upstream PBX retries the call after 30 seconds. If the outage lasts 2 minutes, you receive 4 times the normal call volume in a short burst. This can instantly exhaust your trunk capacity, even if the underlying traffic is normal.

The Root Cause:
SIP retry logic is configured at the carrier or upstream PBX level, not in Genesys/CXone. The platform sees a spike in inbound calls that exceeds the Erlang-B calculated peak.

The Solution:
Implement Call Distribution strategies that include Overflow Routing. If the primary trunk group is at capacity, route calls to a secondary trunk group (even if it is more expensive) or to a voicemail queue. Do not simply drop calls. Configure your dialing rules to have a fallback path.

Edge Case 2: The Long-Tail AHT

The Failure Condition:
Your Erlang-B calculation assumes an average handle time (AHT) of 240 seconds. However, 5% of calls (e.g., complex technical support) last 1200 seconds. These long calls occupy trunks for extended periods, reducing the effective capacity of the trunk group during peak times.

The Root Cause:
Erlang-B assumes a Poisson distribution of call arrivals and an exponential distribution of call durations. Long-tail AHT violates the exponential distribution assumption, leading to under-provisioning.

The Solution:
Segment your traffic. Create separate trunk groups for Short-Duration (IVR, Billing) and Long-Duration (Support, Sales) calls. Calculate Erlang-B separately for each segment. This ensures that long calls do not starve short, high-volume transactions.

Edge Case 3: Carrier Rate Limiting

The Failure Condition:
You have provisioned 200 trunks with the carrier. However, the carrier has a rate-limit of 50 calls per minute (CPM) per IP address. If your peak interval generates 100 calls in one minute, 50 calls will be rejected by the carrier, even though you have available trunks.

The Root Cause:
Trunk capacity (concurrent calls) is distinct from signaling capacity (calls per minute). Most engineers focus on concurrent capacity and ignore CPM limits.

The Solution:
Verify the carrier’s CPM limits during the procurement phase. If your peak interval has a high call arrival rate, you may need to split your trunks across multiple IP addresses or multiple carriers to distribute the signaling load. In Genesys Cloud, use multiple Telephony Trunks with different Host IPs to bypass single-IP rate limits.

Official References