From 4282dec7d6ccf354d8f4e937b4ba43161e4b8be7 Mon Sep 17 00:00:00 2001 From: Muhsin <12408980+muhsin-k@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:41:20 +0400 Subject: [PATCH] fix(voice): thread callSid through agent leg + conference API clients Required by the stricter server-side call_sid requirements from the previous commit. Without this, agents would fail to join or leave calls because the client side still sent only conversation_id. - TwilioVoiceClient.joinClientCall now forwards callSid as a Device.connect param, which becomes params[:call_sid] on the TwiML webhook. - VoiceAPI.leaveConference takes callSid and passes it as a query param so the DELETE /conference endpoint can resolve the exact call. - useCallSession and FloatingCallWidget pass callSid through. --- .../dashboard/api/channel/voice/twilioVoiceClient.js | 3 ++- app/javascript/dashboard/api/channel/voice/voiceAPIClient.js | 4 ++-- .../dashboard/components/widgets/FloatingCallWidget.vue | 1 + app/javascript/dashboard/composables/useCallSession.js | 5 +++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/javascript/dashboard/api/channel/voice/twilioVoiceClient.js b/app/javascript/dashboard/api/channel/voice/twilioVoiceClient.js index 67f74a171..13f61a16c 100644 --- a/app/javascript/dashboard/api/channel/voice/twilioVoiceClient.js +++ b/app/javascript/dashboard/api/channel/voice/twilioVoiceClient.js @@ -68,7 +68,7 @@ class TwilioVoiceClient extends EventTarget { this.inboxId = null; } - async joinClientCall({ to, conversationId }) { + async joinClientCall({ to, conversationId, callSid }) { if (!this.device || !this.initialized || !to) return null; if (this.activeConnection) return this.activeConnection; @@ -76,6 +76,7 @@ class TwilioVoiceClient extends EventTarget { To: to, is_agent: 'true', conversation_id: conversationId, + call_sid: callSid, }; const connection = await this.device.connect({ params }); diff --git a/app/javascript/dashboard/api/channel/voice/voiceAPIClient.js b/app/javascript/dashboard/api/channel/voice/voiceAPIClient.js index 6e1e548c8..41ff7007c 100644 --- a/app/javascript/dashboard/api/channel/voice/voiceAPIClient.js +++ b/app/javascript/dashboard/api/channel/voice/voiceAPIClient.js @@ -12,10 +12,10 @@ class VoiceAPI extends ApiClient { return ContactsAPI.initiateCall(contactId, inboxId).then(r => r.data); } - leaveConference(inboxId, conversationId) { + leaveConference({ inboxId, conversationId, callSid }) { return axios .delete(`${this.baseUrl()}/inboxes/${inboxId}/conference`, { - params: { conversation_id: conversationId }, + params: { conversation_id: conversationId, call_sid: callSid }, }) .then(r => r.data); } diff --git a/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue b/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue index 4515b5c33..4086c733c 100644 --- a/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue +++ b/app/javascript/dashboard/components/widgets/FloatingCallWidget.vue @@ -44,6 +44,7 @@ const handleEndCall = async () => { await endCallSession({ conversationId: call.conversationId, inboxId, + callSid: call.callSid, }); }; diff --git a/app/javascript/dashboard/composables/useCallSession.js b/app/javascript/dashboard/composables/useCallSession.js index f58d784b8..0d682821d 100644 --- a/app/javascript/dashboard/composables/useCallSession.js +++ b/app/javascript/dashboard/composables/useCallSession.js @@ -42,8 +42,8 @@ export function useCallSession() { ); }); - const endCall = async ({ conversationId, inboxId }) => { - await VoiceAPI.leaveConference(inboxId, conversationId); + const endCall = async ({ conversationId, inboxId, callSid }) => { + await VoiceAPI.leaveConference({ inboxId, conversationId, callSid }); TwilioVoiceClient.endClientCall(); durationTimer.stop(); callsStore.clearActiveCall(); @@ -66,6 +66,7 @@ export function useCallSession() { await TwilioVoiceClient.joinClientCall({ to: joinResponse?.conference_sid, conversationId, + callSid, }); callsStore.setCallActive(callSid);