From 4137a1f9fc6d66dd3139f6bcb8f1c8088aab26de Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Wed, 5 Aug 2026 19:24:42 +0530 Subject: [PATCH] Stop classifying Recall bot-detection timeouts as NOT_RECORDED in call-recorder (#23812) Removes the two `timeout_exceeded_only_bots_detected_*` sub codes from `NOT_RECORDED_RECALL_SUB_CODES`, so a bot-detection leave is handled like any other call ending (`call_ended` -> PROCESSING -> artifact import -> COMPLETED). These sub codes are leave reasons, not capture verdicts. Bot detection only fires when participants are present (otherwise `noone_joined` fires first), and any participant starts the recording, so a bot-detection ending virtually always has a real recording behind it. It is also the app's own configured exit path whenever a third-party notetaker (Fireflies, Otter) lingers after the humans leave, since a lingering bot keeps `everyone_left_timeout` from ever firing. Classifying it as NOT_RECORDED stamped successfully recorded calls as failures and skipped artifact import; bots in the production Recall workspace end with this sub code near-daily. Review in cubic --- .../public/call-recorder/package.json | 2 +- .../not-recorded-recall-sub-codes.ts | 2 - .../__tests__/handle-recall-webhook.test.ts | 47 +++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/twenty-apps/public/call-recorder/package.json b/packages/twenty-apps/public/call-recorder/package.json index cfaa9e8a5d..33f949abaf 100644 --- a/packages/twenty-apps/public/call-recorder/package.json +++ b/packages/twenty-apps/public/call-recorder/package.json @@ -1,6 +1,6 @@ { "name": "@twentyhq/call-recorder", - "version": "1.7.0", + "version": "1.7.1", "license": "MIT", "engines": { "node": "^24.5.0", diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/not-recorded-recall-sub-codes.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/not-recorded-recall-sub-codes.ts index 6acfaba101..9735865a21 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/not-recorded-recall-sub-codes.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/constants/not-recorded-recall-sub-codes.ts @@ -2,8 +2,6 @@ export const NOT_RECORDED_RECALL_SUB_CODES: readonly string[] = [ 'meeting_not_started', 'timeout_exceeded_noone_joined', - 'timeout_exceeded_only_bots_detected_using_participant_names', - 'timeout_exceeded_only_bots_detected_using_participant_events', 'timeout_exceeded_waiting_room', 'call_ended_by_platform_waiting_room_timeout', 'bot_kicked_from_waiting_room', diff --git a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts index 1735a4860b..91a543bd1a 100644 --- a/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts +++ b/packages/twenty-apps/public/call-recorder/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts @@ -323,6 +323,53 @@ describe('handleRecallWebhook', () => { ]); }); + it('treats a bot-detection timeout as a normal call ending', async () => { + const client = new FakeCoreApiClient([ + { + id: 'call-recording-1', + status: 'RECORDING', + externalBotId: 'recall-bot-1', + }, + ]); + + const result = await handleRecallWebhook({ + client: client as unknown as CoreApiClient, + body: { + event: 'bot.status_change', + data: { + bot: { + id: 'recall-bot-1', + metadata: { + twentyWorkspaceId: WORKSPACE_ID, + twentyCallRecordingId: 'call-recording-1', + }, + }, + status: { + code: 'call_ended', + sub_code: + 'timeout_exceeded_only_bots_detected_using_participant_names', + }, + }, + }, + }); + + expect(result).toEqual({ + status: 'updated', + event: 'bot.status_change', + callRecordingId: 'call-recording-1', + callRecordingStatus: 'PROCESSING', + }); + expect(client.mutations).toEqual([ + { + id: 'call-recording-1', + data: { + status: 'PROCESSING', + externalBotId: 'recall-bot-1', + }, + }, + ]); + }); + it('ignores a no-capture sub code on a non-terminal status code', async () => { const client = new FakeCoreApiClient([ {