feat: add per-inbox toggle to disable incoming calls (#14645)

## Description

Adds a per-inbox "Allow incoming calls" toggle for voice-enabled
WhatsApp and Twilio inboxes. When turned off, the setting is persisted
on the channel; actually rejecting inbound calls is handled in a
follow-up PR.

## Type of change

- [ ] New feature (non-breaking change which adds functionality)

## Screenshot
<img width="804" height="384" alt="Screenshot 2026-06-04 at 11 44 07 AM"
src="https://github.com/user-attachments/assets/df8bb026-0387-4031-bcba-6d9a56872eb7"
/>


## Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
This commit is contained in:
Tanmay Deep Sharma
2026-06-11 14:22:44 +05:30
committed by GitHub
parent ce93ddec78
commit 8d5d02ea97
19 changed files with 282 additions and 2 deletions
@@ -49,6 +49,59 @@ RSpec.describe 'Enterprise Inboxes API', type: :request do
end
end
describe 'POST /api/v1/accounts/{account.id}/inboxes/:id/set_inbound_calls' do
before do
allow(Twilio::VoiceWebhookSetupService).to receive(:new)
.and_return(instance_double(Twilio::VoiceWebhookSetupService, perform: "AP#{SecureRandom.hex(16)}"))
end
context 'when administrator' do
it 'disables inbound calls on a Twilio voice inbox' do
channel = create(:channel_twilio_sms, :with_voice, account: account)
post "/api/v1/accounts/#{account.id}/inboxes/#{channel.inbox.id}/set_inbound_calls",
headers: admin.create_new_auth_token,
params: { inbound_calls_enabled: false },
as: :json
expect(response).to have_http_status(:ok)
expect(channel.reload.inbound_calls_enabled?).to be false
end
it 'enables inbound calls on a WhatsApp inbox without re-validating provider config' do
account.enable_features('channel_voice')
account.save!
channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud',
validate_provider_config: false, sync_templates: false)
channel.update!(provider_config: channel.provider_config.merge('calling_enabled' => true, 'inbound_calls_enabled' => false))
post "/api/v1/accounts/#{account.id}/inboxes/#{channel.inbox.id}/set_inbound_calls",
headers: admin.create_new_auth_token,
params: { inbound_calls_enabled: true },
as: :json
expect(response).to have_http_status(:ok)
expect(channel.reload.inbound_calls_enabled?).to be true
end
end
context 'when agent' do
let(:agent) { create(:user, account: account, role: :agent) }
it 'is forbidden' do
channel = create(:channel_twilio_sms, :with_voice, account: account)
post "/api/v1/accounts/#{account.id}/inboxes/#{channel.inbox.id}/set_inbound_calls",
headers: agent.create_new_auth_token,
params: { inbound_calls_enabled: false },
as: :json
expect(response).to have_http_status(:unauthorized)
expect(channel.reload.inbound_calls_enabled?).to be true
end
end
end
describe 'PATCH /api/v1/accounts/{account.id}/inboxes/:id' do
let(:inbox) { create(:inbox, account: account, auto_assignment_config: { max_assignment_limit: 5 }) }
@@ -112,6 +112,23 @@ RSpec.describe 'Twilio::VoiceController', type: :request do
}
expect(response).to have_http_status(:not_found)
end
it 'rejects the inbound contact leg without building a call when inbound calls are disabled' do
channel.update!(provider_config: { 'inbound_calls_enabled' => false })
expect(Voice::InboundCallBuilder).not_to receive(:perform!)
expect do
post "/twilio/voice/call/#{digits}", params: {
'CallSid' => call_sid,
'From' => from_number,
'To' => to_number,
'Direction' => 'inbound'
}
end.not_to change(Call, :count)
expect(response).to have_http_status(:ok)
expect(response.body).to include('<Reject')
end
end
describe 'POST /twilio/voice/status/:phone' do
@@ -38,6 +38,19 @@ RSpec.describe Channel::TwilioSms do
end
end
describe '#inbound_calls_enabled?' do
it 'returns true by default when nothing has been toggled' do
channel = create(:channel_twilio_sms, :with_voice, account: account)
expect(channel.inbound_calls_enabled?).to be true
end
it 'returns false only when explicitly disabled in provider_config' do
channel = create(:channel_twilio_sms, :with_voice, account: account,
provider_config: { 'inbound_calls_enabled' => false })
expect(channel.inbound_calls_enabled?).to be false
end
end
describe '#voice_call_webhook_url' do
it 'returns the webhook URL based on phone number' do
channel = create(:channel_twilio_sms, :with_voice)
@@ -30,6 +30,20 @@ describe Whatsapp::IncomingCallService do
end
end
context 'when inbound calls are disabled on the channel' do
it 'rejects the call with Meta without creating a Call or Conversation' do
channel.provider_config = channel.provider_config.merge('inbound_calls_enabled' => false)
channel.save!
provider_service = instance_double(Whatsapp::Providers::WhatsappCloudService, reject_call: true)
allow(inbox.channel).to receive(:provider_service).and_return(provider_service)
params = call_payload(event: 'connect', session: { sdp: "v=0\r\n...sdp...", sdp_type: 'offer' })
expect { described_class.new(inbox: inbox, params: params).perform }
.to not_change(Call, :count).and not_change(Conversation, :count)
expect(provider_service).to have_received(:reject_call).with(provider_call_id)
end
end
describe 'inbound connect' do
let(:sdp_offer) { "v=0\r\n...sdp..." }
let!(:agent) { create(:user, account: account) }
+18
View File
@@ -248,4 +248,22 @@ RSpec.describe Channel::Whatsapp do
expect(channel.voice_enabled?).to be false
end
end
describe '#inbound_calls_enabled?' do
let(:account) { create(:account) }
it 'returns true by default when nothing has been toggled' do
channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud',
validate_provider_config: false, sync_templates: false)
expect(channel.inbound_calls_enabled?).to be true
end
it 'returns false only when explicitly disabled in provider_config' do
channel = create(:channel_whatsapp, account: account, provider: 'whatsapp_cloud',
validate_provider_config: false, sync_templates: false)
channel.update!(provider_config: channel.provider_config.merge('inbound_calls_enabled' => false))
expect(channel.inbound_calls_enabled?).to be false
end
end
end