Ring Groups
Route calls to multiple destinations simultaneously.
Overview
A ring group dials multiple members in parallel — all phones ring at once, and the first person to answer wins. Other ringing channels are automatically cancelled.
Use cases:
- Sales teams — Route inbound leads to all sales reps simultaneously
- Support queues — Ring all available agents at once
- Failover routing — Include external phone numbers as backup
┌─────────────┐
│ Incoming │
│ Call │
└──────┬──────┘
│
▼
┌─────────────┐
│ Ring Group │
│ "Sales Team"│
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Alice │ │ Bob │ │ +1 415 │
│ (user) │ │ (user) │ │ 555-1234│
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────┼────────────┘
│
▼
First answer wins
Creating a Ring Group
- SDK
- cURL
const ringGroup = await dialstack.ringGroups.create(
{
name: 'Sales Team',
timeout_seconds: 30,
ignore_forwarding: false,
},
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
console.log(ringGroup.id); // rg_01h2xcejqtf2nbrexx3vqjhp51
curl -X POST https://api.dialstack.ai/v1/ring_groups \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"name": "Sales Team",
"timeout_seconds": 30,
"ignore_forwarding": false
}'
Adding Members
Members can be either extensions (users, dial plans, voice apps) or phone numbers.
Extension Members
Add a user to ring when the group is called:
- SDK
- cURL
await dialstack.ringGroups.addMember(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ extension: 'user_01h2xcejqtf2nbrexx3vqjhp42' },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
curl -X POST https://api.dialstack.ai/v1/ring_groups/rg_01h2xcejqtf2nbrexx3vqjhp51/members \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{"extension": "user_01h2xcejqtf2nbrexx3vqjhp42"}'
Phone Number Members
Add an external phone number as a member:
- SDK
- cURL
await dialstack.ringGroups.addMember(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ phone_number: '+14155551234' },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
curl -X POST https://api.dialstack.ai/v1/ring_groups/rg_01h2xcejqtf2nbrexx3vqjhp51/members \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{"phone_number": "+14155551234"}'
Settings
Timeout
The timeout_seconds setting (5-300, default 20) controls how long members ring before the call is considered unanswered.
await dialstack.ringGroups.update(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ timeout_seconds: 45 },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
Ignore Forwarding
When ignore_forwarding is true, SIP 302 redirects (call forwarding) from devices are ignored. The device is treated as busy instead of following the forward. This is useful when:
- Devices have call forwarding configured but you want the ring group to control routing
- You want to prevent calls from being forwarded outside the ring group
await dialstack.ringGroups.update(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ ignore_forwarding: true },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
Confirm External
When confirm_external is true, external phone number members must press 1 before being connected to the caller. This prevents external voicemail systems from picking up the call instead of a real person. Only phone number members are affected — extension members ring normally.
await dialstack.ringGroups.update(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ confirm_external: true },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
Timeout Action
The timeout_action and timeout_target settings control what happens when no member answers within timeout_seconds. Without these, the call simply ends when the timeout expires.
Three actions are available:
"ring_user"— Ring a specific user after the timeout.timeout_targetis the user's ID."voicemail"— Send the caller to voicemail.timeout_targetis either a user (delivers to their personal voicemail) or a shared voicemail box."queue"— Overflow the caller into a call queue.timeout_targetis the queue's ID.
Both timeout_action and timeout_target must be set together.
- SDK
- cURL
// Send to voicemail after timeout
await dialstack.ringGroups.update(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{
timeout_action: 'voicemail',
timeout_target: 'user_01h2xcejqtf2nbrexx3vqjhp42',
},
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
curl -X PATCH https://api.dialstack.ai/v1/ring_groups/rg_01h2xcejqtf2nbrexx3vqjhp51 \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"timeout_action": "voicemail",
"timeout_target": "user_01h2xcejqtf2nbrexx3vqjhp42"
}'
To clear the timeout behavior, set both fields to null:
await dialstack.ringGroups.update(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ timeout_action: null, timeout_target: null },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
Routing to a Ring Group
To route calls to a ring group, create an extension that targets it:
- SDK
- cURL
// Create extension 200 that routes to the ring group
await dialstack.extensions.create(
{
number: '200',
target: 'rg_01h2xcejqtf2nbrexx3vqjhp51',
},
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
curl -X POST https://api.dialstack.ai/v1/extensions \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"number": "200",
"target": "rg_01h2xcejqtf2nbrexx3vqjhp51"
}'
Then assign that extension to a phone number, or use it in a dial plan.
Using in Dial Plans
Ring groups can be used as targets in dial plan internal dial nodes:
{
"id": "sales",
"type": "internal_dial",
"config": {
"target_id": "rg_01h2xcejqtf2nbrexx3vqjhp51",
"timeout": 30,
"next": "voicemail"
}
}
If no one answers within the timeout, the dial plan continues to the next node.
Example: Sales Team
Complete example creating a sales team ring group:
// Account context for all operations
const acct = { dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' };
// 1. Create the ring group (confirm_external prevents external voicemail pickup)
const ringGroup = await dialstack.ringGroups.create(
{ name: 'Sales Team', timeout_seconds: 30, confirm_external: true },
acct
);
// 2. Add team members
await dialstack.ringGroups.addMember(ringGroup.id, { extension: 'user_alice_...' }, acct);
await dialstack.ringGroups.addMember(ringGroup.id, { extension: 'user_bob_...' }, acct);
// 3. Add external cell phone as backup
await dialstack.ringGroups.addMember(ringGroup.id, { phone_number: '+14155551234' }, acct);
// 4. Create extension to route calls to the ring group
await dialstack.extensions.create({ number: '200', target: ringGroup.id }, acct);
// 5. Assign to a phone number
await dialstack.phoneNumbers.update('pn_main_line_...', { extension: '200' }, acct);
Loop Prevention
Ring groups can contain extensions that target other ring groups or dial plans, enabling complex routing scenarios. However, circular references are prohibited to prevent infinite loops.
Prohibited Configurations
Direct loop — A ring group cannot contain itself as a member:
RG1 → ext_a → RG1 ❌ Direct loop
Indirect loop — Ring groups cannot form a circular chain:
RG1 → ext_a → RG2 → ext_b → RG1 ❌ Indirect loop
Error Responses
When a loop is detected, the API returns a 422 Unprocessable Entity status:
{
"error": "ring group cannot contain itself as a member"
}
For indirect loops, the error shows the circular path:
{
"error": "rg_01xxx → dp_02yyy → rg_01xxx"
}
Allowed Configurations
Multiple members targeting the same ring group — Parallel references are allowed:
RG1 → ext_a → RG2
RG1 → ext_b → RG2 ✓ Same target, no loop
Chains without cycles — Linear chains are allowed:
RG1 → ext_a → RG2 → ext_b → RG3 ✓ No circular reference
Mixed chains — Ring groups and dial plans can reference each other as long as there's no cycle:
RG1 → ext_a (dial plan) → DP1 → internal_dial → RG2 ✓ No circular reference
Depth Limit
Routing nesting is limited to a maximum depth of 20 levels. Configurations exceeding this limit are rejected to prevent performance issues:
{
"error": "routing nesting exceeds maximum depth of 20"
}
In practice, most deployments use 2-3 levels of nesting at most.
API Reference
- Ring Groups — Create, update, and manage ring groups
- Extensions — Assign ring groups to extension numbers
- Dial Plans — Use ring groups in call routing flows