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"}'
Member Fan-Out
What an extension member actually rings depends on that user's Find Me / Follow Me configuration:
- No FMFM configured — the group rings the user's SIP endpoints plus their WebRTC softphone.
- FMFM configured — the group rings the targets of step 1 only, and those targets replace the user's endpoints rather than ringing alongside them. An external number in step 1 rings as part of the group.
Later FMFM steps and the FMFM fallback are never run inside a ring group. When the ring window closes, the group's own Timeout Action applies instead.
This matters for a common configuration: a user whose step 1 rings their devices and step 2 rings their cell. Dialing that user directly ladders through to the cell, but a call to the ring group rings only the devices. Put the number in step 1, or add it to the group as a phone number member, to have it ring as part of the group.
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, and the group keeps ringing its other members. 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
This setting reaches only forwarding the phone performs itself. It does not disable a member's Find Me / Follow Me or change which targets that produces, but it does apply to every leg the group dials, including a phone reached through an FMFM step-1 target. See Member Fan-Out for how a member's own routing is expanded.
await dialstack.ringGroups.update(
'rg_01h2xcejqtf2nbrexx3vqjhp51',
{ ignore_forwarding: true },
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
Confirm External
When confirm_external is true, external destinations must press 1 before being connected to the caller. This prevents external voicemail systems from picking up the call instead of a real person.
This applies to phone number members and an extension member's Find Me / Follow Me external targets. Extension members' own devices ring normally.
The prompt plays as soon as the external destination answers and repeats every few seconds. The person has 15 seconds to press 1: if they do, they are connected to the caller; if they don't, that leg is dropped and the rest of the group keeps ringing (and the group's timeout action still applies). The 15 seconds run from the moment that destination answers, independently of timeout_seconds — a destination answering near the end of the ring window keeps the group open until it confirms or the window expires.
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