Implementing Internal Mobility Recommendation Engines Based on Agent Skill and Interest Profiles
What This Guide Covers
This guide details the architecture and configuration required to build an internal mobility recommendation system that matches agents to open roles, cross-training queues, or project assignments using platform skill data, performance metrics, and agent interest declarations. When deployed, the system continuously scores agents against open mobility targets and delivers prioritized recommendations to workforce management planners and agent self-service portals.
Prerequisites, Roles & Licensing
- Licensing Tiers: Genesys Cloud CX 1 (minimum) with WFM Add-on, Custom Objects Tables (COT) license, and Analytics Add-on. NICE CXone: CXone Core with Workforce Management, Data Tables, and Advanced Analytics.
- Platform Permissions:
Telephony > Skills > Edit,Administration > Custom Objects Tables > Edit,Analytics > Reports > View,Integrations > Create,Workforce Management > Scheduling > Edit,Users > View - OAuth Scopes:
custom-objects:read,custom-objects:write,analytics:read,routing:skills:read,users:read,wfm:scheduling:read,wfm:scheduling:write - External Dependencies: Python or Node.js runtime environment for scoring logic, scheduled orchestrator (cron, Airflow, or Genesys Cloud Scheduled Workflows), WFM planning interface, and agent self-service frontend framework.
The Implementation Deep-Dive
1. Data Ingestion & Profile Normalization
Internal mobility engines fail when they compare raw metrics across disparate operational contexts. An agent with a 180-second average handle time in a complex technical support queue is highly proficient, while the same metric in a simple billing queue indicates severe performance degradation. You must normalize all performance data before it enters the recommendation pipeline.
Begin by establishing a Custom Objects Table (COT) in Genesys Cloud to serve as the single source of truth for agent mobility profiles. In NICE CXone, use a Data Table with a primary key mapped to the agent user ID. The table schema must support relational mapping between agents, skills, performance baselines, and interest declarations.
Create a COT named MobilityAgentProfiles with the following fields:
agent_id(String, Primary Key)base_skills(JSON Array)normalized_performance(JSON Object)declared_interests(JSON Array)last_sync_timestamp(DateTime)skill_decay_factor(Float)
Populate base_skills by querying the routing profile API. Do not store skill names as plain text. Store the internal skill IDs and proficiency levels to prevent mapping drift during skill reorganizations.
The Trap: Storing raw performance metrics directly in the profile table without normalization. When the scoring engine compares AHT, CSAT, or QA scores across queues with different complexity tiers, the algorithm systematically penalizes agents in high-complexity queues and over-rewards agents in low-complexity queues. This creates a mobility pipeline that pushes top performers out of critical queues and floods simple queues with underqualified staff.
Architectural Solution: Calculate Z-scores or percentile ranks relative to queue-specific baselines. Use the Analytics API to pull historical performance data over a 90-day rolling window. Compute the mean and standard deviation per queue, then normalize each agent metric to a 0.0 to 1.0 scale. Store only the normalized values in the COT.
Execute the following API call to retrieve routing skills for a batch of agents. This payload demonstrates the exact structure required to map skills to the profile table.
GET /api/v2/users?expand=routingProfile&size=200
Authorization: Bearer {access_token}
Content-Type: application/json
Response payload excerpt:
{
"entities": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"routingProfile": {
"id": "rp-98765432-1234-5678-90ab-cdef12345678",
"skills": [
{
"id": "skill-001",
"name": "Technical_Support_L2",
"proficiency": "Advanced"
},
{
"id": "skill-002",
"name": "Billing_Modification",
"proficiency": "Intermediate"
}
]
}
}
]
}
After extracting skills and calculating normalized performance metrics externally, upsert the profile into the COT using the records endpoint. Always use upsert logic keyed on agent_id to prevent duplicate records during sync cycles.
POST /api/v2/custom-objects/tables/mobilityagentprofiles/records
Authorization: Bearer {access_token}
Content-Type: application/json
{
"fields": {
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"base_skills": [
{ "skill_id": "skill-001", "proficiency": 0.92 },
{ "skill_id": "skill-002", "proficiency": 0.65 }
],
"normalized_performance": {
"aht_percentile": 0.78,
"csat_percentile": 0.85,
"qa_percentile": 0.71
},
"declared_interests": [
{ "target_skill_id": "skill-003", "interest_score": 0.90 },
{ "target_skill_id": "skill-004", "interest_score": 0.40 }
],
"last_sync_timestamp": "2024-05-15T14:30:00Z",
"skill_decay_factor": 0.05
}
}
In NICE CXone, replace the COT endpoint with the Data Tables API (POST /v1/data-tables/{tableId}/records). The architectural pattern remains identical, but CXone requires explicit field type definitions during table creation and does not support JSON array storage natively without string serialization. Serialize arrays to JSON strings and parse them in the scoring engine.
2. Recommendation Logic & Scoring Engine
The scoring engine calculates a composite match score for each agent against each open mobility target. The formula must weight skill alignment, performance readiness, interest declaration, and operational availability. Hardcoding static weights causes the engine to produce stagnant recommendations as business priorities shift. Implement a configurable weight matrix that WFM planners can adjust via a management dashboard or configuration file.
Standard scoring formula:
Match_Score = (W_skill * Skill_Match) + (W_perf * Normalized_Perf) + (W_interest * Interest_Align) + (W_avail * Capacity_Factor)
Deploy the scoring logic as an external microservice rather than relying on platform-native workflow engines. Genesys Cloud Architect flows and NICE CXone Studio flows are optimized for synchronous routing decisions, not batch data processing. Running complex scoring calculations inside Architect triggers HTTP timeout limits, exhausts flow execution concurrency, and creates unpredictable latency during peak IVR routing hours.
The Trap: Executing the recommendation engine synchronously during real-time routing or wrap-up events. When an agent completes a call, the platform triggers a webhook that initiates the scoring pipeline. Under load, the scoring service receives thousands of concurrent requests, exceeds API rate limits, and returns 429 responses. The platform queues the failed requests, causing cascading timeouts that degrade call routing performance and wrap-up code processing.
Architectural Solution: Implement asynchronous batch processing. Schedule the scoring engine to execute on a fixed interval (e.g., every 6 hours or daily during low-traffic windows). The engine pulls the latest profile data from the COT, computes match scores against a static list of open mobility targets, and writes the results to a secondary COT named MobilityRecommendations. This decouples scoring latency from real-time telephony operations.
The Python microservice below demonstrates production-ready scoring logic with configurable weights and hard filters.
import requests
import json
import math
GENESYS_BASE = "https://{organization}.mypurecloud.com/api/v2"
COT_TABLE_ID = "mobilityagentprofiles"
REC_TABLE_ID = "mobilityrecommendations"
WEIGHTS = {
"skill": 0.40,
"performance": 0.30,
"interest": 0.20,
"availability": 0.10
}
MIN_SKILL_THRESHOLD = 0.60
def calculate_match_score(agent_profile, target_skill_id):
skill_match = 0.0
for skill in agent_profile["base_skills"]:
if skill["skill_id"] == target_skill_id:
skill_match = skill["proficiency"]
break
if skill_match < MIN_SKILL_THRESHOLD:
return None # Hard filter: insufficient baseline proficiency
perf_score = (
agent_profile["normalized_performance"]["aht_percentile"] * 0.3 +
agent_profile["normalized_performance"]["csat_percentile"] * 0.4 +
agent_profile["normalized_performance"]["qa_percentile"] * 0.3
)
interest_score = 0.0
for interest in agent_profile["declared_interests"]:
if interest["target_skill_id"] == target_skill_id:
interest_score = interest["interest_score"]
break
# Apply skill decay based on days since last usage (simplified)
decay = agent_profile["skill_decay_factor"]
adjusted_skill = skill_match * math.exp(-decay)
match_score = (
WEIGHTS["skill"] * adjusted_skill +
WEIGHTS["performance"] * perf_score +
WEIGHTS["interest"] * interest_score +
WEIGHTS["availability"] * 0.85 # Placeholder for WFM capacity check
)
return round(match_score, 4)
def upsert_recommendation(agent_id, target_skill_id, score):
payload = {
"fields": {
"agent_id": agent_id,
"target_skill_id": target_skill_id,
"match_score": score,
"generated_at": "2024-05-15T18:00:00Z"
}
}
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
}
requests.post(
f"{GENESYS_BASE}/custom-objects/tables/{REC_TABLE_ID}/records",
headers=headers,
json=payload
)
Schedule this engine via cron or a platform orchestrator. Query the MobilityAgentProfiles table, iterate through each open mobility target, calculate scores, and upsert results to MobilityRecommendations. Sort results by match_score descending and apply a top-N limit per target to prevent overwhelming WFM planners.
3. Delivery & WFM Integration
Recommendations lose business value if they remain trapped in a data table. You must route scored matches to WFM planners for scheduling decisions and to agents for self-service visibility. The delivery layer requires strict separation between advisory recommendations and binding assignments. The engine advises; WFM planners decide.
Integrate with the WFM Scheduling API to create or update planning groups based on high-confidence recommendations. Do not auto-assign agents. Auto-assignment bypasses manager approval, violates labor agreement constraints, and ignores real-time shrinkage events. Instead, use the WFM API to populate candidate lists for open shifts or cross-training blocks.
The Trap: Hardcoding queue IDs or routing strategy references in recommendation payloads. Queue reorganizations, strategy merges, or seasonal campaign rotations invalidate static identifiers. When WFM planners attempt to assign agents based on outdated queue references, the scheduling job fails with validation errors, and agents are left without clear mobility pathways.
Architectural Solution: Target recommendations by skill sets rather than queue IDs. WFM planners assign agents to queues based on skill requirements defined in routing strategies. When a queue is renamed or restructured, the underlying skill requirement remains stable. Store target_skill_id as the primary mobility anchor. Map skills to queues dynamically during the WFM planning phase using the routing profile API.
Push recommendations to WFM using the scheduling groups endpoint. This payload creates a candidate pool for a specific mobility target.
POST /api/v2/wfm/scheduling/groups
Authorization: Bearer {access_token}
Content-Type: application/json
{
"name": "Mobility_Candidate_Pool_TechSupport_L3",
"description": "Auto-generated candidate pool based on scoring engine v2.4",
"members": [
{ "userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "matchScore": 0.8912 },
{ "userId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "matchScore": 0.8745 },
{ "userId": "c3d4e5f6-a7b8-9012-cdef-123456789012", "matchScore": 0.8620 }
],
"tags": [
"mobility_engine",
"skill_target:skill-003",
"confidence:high"
]
}
For agent self-service delivery, expose a read-only API endpoint that queries the MobilityRecommendations table filtered by agent_id. The frontend renders a dashboard showing recommended roles, match confidence percentages, and required skill gaps. Agents submit interest confirmations via a form that updates the declared_interests field in the profile table. This closes the feedback loop and allows the scoring engine to recalibrate based on explicit agent intent.
In NICE CXone, replace the WFM scheduling group creation with the Planning API (POST /v1/planning/groups). The Data Tables API handles recommendation storage identically to Genesys COTs. CXone requires explicit integration tokens for cross-module API calls, so ensure the integration identity possesses wfm:scheduling:write and data-tables:read scopes.
Validation, Edge Cases & Troubleshooting
Edge Case 1: Skill Decay and Stale Profiles
The failure condition: Agents recommended for mobility targets fail training or underperform because their baseline proficiency has degraded since the last scoring cycle. The engine assumes static skill retention.
The root cause: Contact center agents experience skill atrophy when removed from a queue for extended periods. A 90-day performance snapshot does not account for months of non-usage. The scoring engine overweights historical proficiency without temporal decay.
The solution: Implement exponential decay in the scoring formula. Multiply the base skill proficiency by e^(-λ * days_since_last_interaction). Set λ to 0.02 for complex technical skills and 0.01 for procedural skills. Query the Analytics API for last interaction timestamps per skill. Update the skill_decay_factor field in the COT during the nightly sync. Re-run the scoring engine weekly to refresh decay calculations.
Edge Case 2: Queue Capacity vs. Recommendation Volume
The failure condition: The engine generates 45 high-confidence recommendations for a mobility target that only requires 5 additional agents. WFM planners receive excessive noise, and the recommendation system loses credibility.
The root cause: The scoring engine operates in isolation from operational capacity constraints. It optimizes for individual agent fit without evaluating queue shrinkage, scheduled adherence, or current utilization targets.
The solution: Inject capacity-aware filtering before writing recommendations to the output table. Query the WFM API for target queue capacity, current headcount, and shrinkage rates. Calculate Available_Capacity = Target_Headcount - (Current_Headcount * (1 - Shrinkage_Rate)). Cap the number of recommendations per target at Available_Capacity * Utilization_Target. Discard or archive excess scores. Tag discarded recommendations with status:capacity_exceeded for historical auditing.
Edge Case 3: Interest-Skill Mismatch Bias
The failure condition: Agents with high declared interest but low baseline proficiency receive top recommendations. Managers assign them to targets, resulting in high ramp-up costs and elevated quality assurance failures.
The root cause: The interest weight in the scoring formula overrides skill thresholds. The engine prioritizes motivation over readiness, violating operational risk controls.
The solution: Enforce hard skill thresholds before interest weighting applies. The scoring function must return null if Skill_Match < MIN_SKILL_THRESHOLD. Configure the threshold dynamically per mobility target. Critical compliance queues require 0.75 baseline proficiency. General customer service queues require 0.60. Document threshold policies in the configuration file. Notify agents via the self-service portal when their interest is acknowledged but skill requirements are unmet, and provide direct links to recommended training paths.