Skill Profiles
Skill profiles assign queue-group skills to agents. Use them when calls should route to agents with specific capabilities, such as language, product specialization, or escalation training.
Workflow
- Identify the queue group, queue, agent group, and agent.
- Confirm that the agent is assigned to the queue that will receive the call.
- Create the group skill under the same queue group as the queue.
- Find or create the queue's Route to Agent event.
- Add the group skill to that Route to Agent event.
- Create a skill profile for the agent.
- Assign the same group skill to the agent's skill profile.
- Place a test call to a DNIS assigned to the queue and confirm that the call routes to an available agent with the matching skill.
Skill routing only works when the queue event and the agent skill profile reference the same group skill. The agent must also have access to the queue where the skill is used.
Before You Start
Before configuring skill routing, collect the following identifiers:
| Identifier | Description |
|---|---|
accountId |
RingCX account ID. |
gateGroupId |
Queue group ID. Group skills are created under this queue group. |
gateId |
Queue ID for the inbound queue that should use skill routing. |
agentGroupId |
Agent group ID for the target agent. |
agentId |
Agent ID for the user who should receive matching calls. |
Use these queue assignment endpoints to verify that the target agent can receive calls from the queue:
| Operation | Method and path |
|---|---|
| List agents assigned to a queue | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/assignedAgents |
| Assign agents to a queue | PUT https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/assignAgents |
SDK Setup
SDK examples in this article use JWT authentication and load credentials from environment variables.
npm install ringcentral-engage-voice-client dotenv
pip3 install ringcentral_engage_voice python-dotenv
Create a .env file in the directory where you run the sample:
RC_CLIENT_ID=<clientId>
RC_CLIENT_SECRET=<clientSecret>
RC_JWT=<jwt>
The SDK wrapper reads these values, signs in with RingCentral, and exchanges the RingCentral access token for a RingCX access token before calling RingCX APIs.
Queue Event Basics
Queue events define what happens after a call enters a queue. Each event has a queueEvent value that stores one or more semicolon-delimited event tokens.
For skill routing, update the queue's Route to Agent event:
| Token | Purpose |
|---|---|
ROUTE-TO-AGENT:TRUE; |
Routes the caller to an available agent. |
SKILL-ROUTE:{skillId}; |
Limits that route to agents whose skill profile includes the specified group skill. |
Retrieve the queue events for the queue and look for the event whose queueEvent value contains ROUTE-TO-AGENT. When you update an existing event, preserve any existing event settings that are still needed for the queue and add the SKILL-ROUTE:{skillId}; token.
Create a Group Skill
Group skills are managed under queue groups.
POST https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/skills
Authorization: Bearer <ringcxAccessToken>
Content-Type: application/json
{
"skillName": "Spanish Language",
"skillDesc": "Spanish-language support",
"active": true
}
import requests
account_id = "<accountId>"
gate_group_id = "<gateGroupId>"
access_token = "<ringcxAccessToken>"
payload = {
"skillName": "Spanish Language",
"skillDesc": "Spanish-language support",
"active": True,
}
response = requests.post(
f"https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{account_id}/gateGroups/{gate_group_id}/skills",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
},
json=payload,
)
response.raise_for_status()
print(response.json())
const accountId = "<accountId>";
const gateGroupId = "<gateGroupId>";
const accessToken = "<ringcxAccessToken>";
const payload = {
skillName: "Spanish Language",
skillDesc: "Spanish-language support",
active: true
};
const response = await fetch(
`https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/${accountId}/gateGroups/${gateGroupId}/skills`,
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
const EngageVoice = require("ringcentral-engage-voice-client").default;
require("dotenv").config();
async function main() {
const ev = new EngageVoice({
clientId: process.env.RC_CLIENT_ID,
clientSecret: process.env.RC_CLIENT_SECRET
});
await ev.authorize({ jwt: process.env.RC_JWT });
const payload = {
skillName: "Spanish Language",
skillDesc: "Spanish-language support",
active: true
};
const response = await ev.post(
"/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/skills",
payload
);
console.log(response.data);
}
main().catch(console.error);
import os
from dotenv import load_dotenv
from ringcentral_engage_voice import RingCentralEngageVoice
load_dotenv()
ev = RingCentralEngageVoice(
os.environ["RC_CLIENT_ID"],
os.environ["RC_CLIENT_SECRET"],
)
ev.authorize(jwt=os.environ["RC_JWT"])
payload = {
"skillName": "Spanish Language",
"skillDesc": "Spanish-language support",
"active": True,
}
response = ev.post(
"/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/skills",
payload,
).json()
print(response)
Response example
{
"skillId": 1455,
"skillName": "Spanish Language",
"skillDesc": "Spanish-language support",
"active": true
}
See Group Skills for the full group-skill workflow.
Add the Skill to Queue Routing
First, retrieve the queue events for the queue:
GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/gateQueueEvents
Authorization: Bearer <ringcxAccessToken>
Find the Route to Agent event, then update that event so its queueEvent value includes both ROUTE-TO-AGENT:TRUE; and SKILL-ROUTE:{skillId};.
PUT https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/gateQueueEvents/{eventId}
Authorization: Bearer <ringcxAccessToken>
Content-Type: application/json
{
"eventId": 67882,
"eventRank": 10,
"queueEvent": "ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:1455;",
"eventDuration": 120,
"enableDtmf": 0
}
import requests
account_id = "<accountId>"
gate_group_id = "<gateGroupId>"
gate_id = "<gateId>"
event_id = "<eventId>"
access_token = "<ringcxAccessToken>"
payload = {
"eventId": 67882,
"eventRank": 10,
"queueEvent": "ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:1455;",
"eventDuration": 120,
"enableDtmf": 0,
}
response = requests.put(
f"https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{account_id}/gateGroups/{gate_group_id}/gates/{gate_id}/gateQueueEvents/{event_id}",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
},
json=payload,
)
response.raise_for_status()
print(response.json())
const accountId = "<accountId>";
const gateGroupId = "<gateGroupId>";
const gateId = "<gateId>";
const eventId = "<eventId>";
const accessToken = "<ringcxAccessToken>";
const payload = {
eventId: 67882,
eventRank: 10,
queueEvent: "ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:1455;",
eventDuration: 120,
enableDtmf: 0
};
const response = await fetch(
`https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/${accountId}/gateGroups/${gateGroupId}/gates/${gateId}/gateQueueEvents/${eventId}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
const EngageVoice = require("ringcentral-engage-voice-client").default;
require("dotenv").config();
async function main() {
const ev = new EngageVoice({
clientId: process.env.RC_CLIENT_ID,
clientSecret: process.env.RC_CLIENT_SECRET
});
await ev.authorize({ jwt: process.env.RC_JWT });
const payload = {
eventId: 67882,
eventRank: 10,
queueEvent: "ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:1455;",
eventDuration: 120,
enableDtmf: 0
};
const response = await ev.put(
"/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/gateQueueEvents/{eventId}",
payload
);
console.log(response.data);
}
main().catch(console.error);
import os
from dotenv import load_dotenv
from ringcentral_engage_voice import RingCentralEngageVoice
load_dotenv()
ev = RingCentralEngageVoice(
os.environ["RC_CLIENT_ID"],
os.environ["RC_CLIENT_SECRET"],
)
ev.authorize(jwt=os.environ["RC_JWT"])
payload = {
"eventId": 67882,
"eventRank": 10,
"queueEvent": "ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:1455;",
"eventDuration": 120,
"enableDtmf": 0,
}
response = ev.put(
"/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/gateQueueEvents/{eventId}",
payload,
).json()
print(response)
Response example
{
"eventId": 67882,
"eventRank": 10,
"queueEvent": "ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:1455;",
"eventDuration": 120,
"enableDtmf": 0
}
Manage Agent Skill Profiles
| Operation | Method and path |
|---|---|
| List skill profiles for an agent | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles |
| Get skill profile | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId} |
| Create skill profile | POST https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles |
| Update skill profile | PUT https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId} |
| Delete skill profile | DELETE https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId} |
| List assigned skills | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills |
| Assign skills to profile | PUT https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills |
| Assign one skill | PUT https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills/{skillId} |
| Remove one skill | DELETE https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills/{skillId} |
Create a Skill Profile
POST https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles
Authorization: Bearer <ringcxAccessToken>
Content-Type: application/json
{
"profileName": "Spanish Speaker",
"profileDesc": "Routes Spanish-language calls to this agent",
"isDefault": false,
"gateGroupSkills": [
{
"skillId": 1455,
"skillName": "Spanish Language",
"active": true
}
],
"chatGroupSkills": []
}
import requests
account_id = "<accountId>"
agent_group_id = "<agentGroupId>"
agent_id = "<agentId>"
access_token = "<ringcxAccessToken>"
payload = {
"profileName": "Spanish Speaker",
"profileDesc": "Routes Spanish-language calls to this agent",
"isDefault": False,
"gateGroupSkills": [
{
"skillId": 1455,
"skillName": "Spanish Language",
"active": True,
}
],
"chatGroupSkills": [],
}
response = requests.post(
f"https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{account_id}/agentGroups/{agent_group_id}/agents/{agent_id}/skillProfiles",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
},
json=payload,
)
response.raise_for_status()
print(response.json())
const accountId = "<accountId>";
const agentGroupId = "<agentGroupId>";
const agentId = "<agentId>";
const accessToken = "<ringcxAccessToken>";
const payload = {
profileName: "Spanish Speaker",
profileDesc: "Routes Spanish-language calls to this agent",
isDefault: false,
gateGroupSkills: [
{
skillId: 1455,
skillName: "Spanish Language",
active: true
}
],
chatGroupSkills: []
};
const response = await fetch(
`https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/${accountId}/agentGroups/${agentGroupId}/agents/${agentId}/skillProfiles`,
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
const EngageVoice = require("ringcentral-engage-voice-client").default;
require("dotenv").config();
async function main() {
const ev = new EngageVoice({
clientId: process.env.RC_CLIENT_ID,
clientSecret: process.env.RC_CLIENT_SECRET
});
await ev.authorize({ jwt: process.env.RC_JWT });
const payload = {
profileName: "Spanish Speaker",
profileDesc: "Routes Spanish-language calls to this agent",
isDefault: false,
gateGroupSkills: [
{
skillId: 1455,
skillName: "Spanish Language",
active: true
}
],
chatGroupSkills: []
};
const response = await ev.post(
"/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles",
payload
);
console.log(response.data);
}
main().catch(console.error);
import os
from dotenv import load_dotenv
from ringcentral_engage_voice import RingCentralEngageVoice
load_dotenv()
ev = RingCentralEngageVoice(
os.environ["RC_CLIENT_ID"],
os.environ["RC_CLIENT_SECRET"],
)
ev.authorize(jwt=os.environ["RC_JWT"])
payload = {
"profileName": "Spanish Speaker",
"profileDesc": "Routes Spanish-language calls to this agent",
"isDefault": False,
"gateGroupSkills": [
{
"skillId": 1455,
"skillName": "Spanish Language",
"active": True,
}
],
"chatGroupSkills": [],
}
response = ev.post(
"/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles",
payload,
).json()
print(response)
Response example
{
"profileId": 215271,
"profileName": "Spanish Speaker",
"profileDesc": "Routes Spanish-language calls to this agent",
"isDefault": false,
"gateGroupSkills": [
{
"skillId": 1455,
"skillName": "Spanish Language",
"active": true
}
],
"chatGroupSkills": []
}
Common Fields
| Field | Required | Description |
|---|---|---|
profileName |
Yes | Display name for the profile. |
profileDesc |
No | Short description of the profile. |
isDefault |
No | Whether this is the default skill profile for the agent. |
gateGroupSkills |
No | Queue group skills assigned to the profile. |
chatGroupSkills |
No | Digital chat group skills assigned to the profile, when applicable. |
Assign Skills to an Existing Profile
PUT https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills/{skillId}
Authorization: Bearer <ringcxAccessToken>
Content-Type: application/json
import requests
account_id = "<accountId>"
agent_group_id = "<agentGroupId>"
agent_id = "<agentId>"
skill_profile_id = "<skillProfileId>"
skill_id = "<skillId>"
access_token = "<ringcxAccessToken>"
response = requests.put(
f"https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{account_id}/agentGroups/{agent_group_id}/agents/{agent_id}/skillProfiles/{skill_profile_id}/skills/{skill_id}",
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
},
)
response.raise_for_status()
print(response.json())
const accountId = "<accountId>";
const agentGroupId = "<agentGroupId>";
const agentId = "<agentId>";
const skillProfileId = "<skillProfileId>";
const skillId = "<skillId>";
const accessToken = "<ringcxAccessToken>";
const response = await fetch(
`https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/${accountId}/agentGroups/${agentGroupId}/agents/${agentId}/skillProfiles/${skillProfileId}/skills/${skillId}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
}
}
);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
const EngageVoice = require("ringcentral-engage-voice-client").default;
require("dotenv").config();
async function main() {
const ev = new EngageVoice({
clientId: process.env.RC_CLIENT_ID,
clientSecret: process.env.RC_CLIENT_SECRET
});
await ev.authorize({ jwt: process.env.RC_JWT });
const response = await ev.put(
"/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills/{skillId}"
);
console.log(response.data);
}
main().catch(console.error);
import os
from dotenv import load_dotenv
from ringcentral_engage_voice import RingCentralEngageVoice
load_dotenv()
ev = RingCentralEngageVoice(
os.environ["RC_CLIENT_ID"],
os.environ["RC_CLIENT_SECRET"],
)
ev.authorize(jwt=os.environ["RC_JWT"])
response = ev.put(
"/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId}/skills/{skillId}"
).json()
print(response)
Retrieve the profile after assignment to verify that the expected skills are present.
Assigned profile response example
{
"profileId": 215271,
"profileName": "Spanish Speaker",
"gateGroupSkills": [
{
"skillId": 1455,
"skillName": "Spanish Language",
"active": true
}
],
"chatGroupSkills": []
}
Validate Skill Routing
Before sending production traffic to the queue, verify the complete route:
| Check | How to verify |
|---|---|
| Agent queue access | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/assignedAgents includes the target agentId. |
| Queue routing event | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/gateGroups/{gateGroupId}/gates/{gateId}/gateQueueEvents/{eventId} returns a Route to Agent event whose queueEvent includes ROUTE-TO-AGENT:TRUE;SKILL-ROUTE:{skillId};. |
| Agent skill profile | GET https://ringcx.ringcentral.com/voice/api/v1/admin/accounts/{accountId}/agentGroups/{agentGroupId}/agents/{agentId}/skillProfiles/{skillProfileId} includes the same skillId in gateGroupSkills. |
| Agent state | The agent is logged in, available, and able to receive inbound queue calls. |
Place a test call to a DNIS assigned to the queue. If the call does not route to the expected agent, review the queue event rank, the agent's queue assignment, the agent's skill profile, the agent's availability state, and any other available agents that share the same skill.