feat(voice): reuse open conversation for Twilio outbound calls

Accept an optional conversation_id in POST /contacts/:id/call. When the
hint matches the picked voice inbox, OutboundCallBuilder reuses that
conversation instead of creating a new one — mirroring the WhatsApp
calling flow so the agent stays in the same thread.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tanmay Deep Sharma
2026-05-06 13:44:28 +07:00
co-authored by Claude Opus 4.7
parent 0b1caa7286
commit 2866b29f3a
2 changed files with 16 additions and 5 deletions
@@ -10,7 +10,8 @@ class Api::V1::Accounts::Contacts::CallsController < Api::V1::Accounts::BaseCont
account: Current.account,
inbox: voice_inbox,
user: Current.user,
contact: contact
contact: contact,
conversation: existing_conversation
)
render json: {
@@ -38,4 +39,13 @@ class Api::V1::Accounts::Contacts::CallsController < Api::V1::Accounts::BaseCont
inbox
end
end
# Reuse the open conversation when the caller is already inside it; ignore
# the hint if it doesn't belong to the picked voice inbox.
def existing_conversation
return nil if params[:conversation_id].blank?
conversation = Current.account.conversations.find_by(display_id: params[:conversation_id])
conversation if conversation && conversation.inbox_id == voice_inbox.id
end
end
@@ -1,15 +1,16 @@
class Voice::OutboundCallBuilder
attr_reader :account, :inbox, :user, :contact
def self.perform!(account:, inbox:, user:, contact:)
new(account: account, inbox: inbox, user: user, contact: contact).perform!
def self.perform!(account:, inbox:, user:, contact:, conversation: nil)
new(account: account, inbox: inbox, user: user, contact: contact, conversation: conversation).perform!
end
def initialize(account:, inbox:, user:, contact:)
def initialize(account:, inbox:, user:, contact:, conversation: nil)
@account = account
@inbox = inbox
@user = user
@contact = contact
@existing_conversation = conversation
end
def perform!
@@ -18,7 +19,7 @@ class Voice::OutboundCallBuilder
ActiveRecord::Base.transaction do
contact_inbox = ensure_contact_inbox!
conversation = create_conversation!(contact_inbox)
conversation = @existing_conversation || create_conversation!(contact_inbox)
call_sid = initiate_call!
call = create_call!(conversation, call_sid)
message = Voice::CallMessageBuilder.new(call).perform!