diff --git a/src/ozonetel/kookoo-ivr.controller.ts b/src/ozonetel/kookoo-ivr.controller.ts
index 9c2e542..b33e56d 100644
--- a/src/ozonetel/kookoo-ivr.controller.ts
+++ b/src/ozonetel/kookoo-ivr.controller.ts
@@ -25,12 +25,10 @@ export class KookooIvrController {
// New outbound call — customer answered, put them in a conference room
// The room ID is based on the call SID so we can join from the browser
if (event === 'NewCall') {
- // Try dialing the SIP extension with 0 prefix (internal routing)
- const ext = query.ext ?? `0${this.sipId}`;
- this.logger.log(`Customer ${cid} answered — dialing agent at ${ext}`);
+ this.logger.log(`Customer ${cid} answered — dialing DID ${this.callerId} to route to agent`);
return `
-${ext}
+${this.callerId}
`;
}
diff --git a/src/ozonetel/ozonetel-agent.controller.ts b/src/ozonetel/ozonetel-agent.controller.ts
index 762e57d..73308e5 100644
--- a/src/ozonetel/ozonetel-agent.controller.ts
+++ b/src/ozonetel/ozonetel-agent.controller.ts
@@ -53,6 +53,29 @@ export class OzonetelAgentController {
}
}
+ @Post('agent-ready')
+ async agentReady() {
+ this.logger.log(`Force ready: logging out and back in agent ${this.defaultAgentId}`);
+
+ try {
+ await this.ozonetelAgent.logoutAgent({
+ agentId: this.defaultAgentId,
+ password: this.defaultAgentPassword,
+ });
+ const result = await this.ozonetelAgent.loginAgent({
+ agentId: this.defaultAgentId,
+ password: this.defaultAgentPassword,
+ phoneNumber: this.defaultSipId,
+ mode: 'blended',
+ });
+ return result;
+ } catch (error: any) {
+ const message = error.response?.data?.message ?? error.message ?? 'Force ready failed';
+ this.logger.error(`Force ready failed: ${message}`);
+ throw new HttpException(message, error.response?.status ?? 502);
+ }
+ }
+
@Post('dispose')
async dispose(
@Body() body: {
diff --git a/src/worklist/kookoo-callback.controller.ts b/src/worklist/kookoo-callback.controller.ts
index 932e0c2..9b7b60e 100644
--- a/src/worklist/kookoo-callback.controller.ts
+++ b/src/worklist/kookoo-callback.controller.ts
@@ -49,6 +49,7 @@ export class KookooCallbackController {
name: `Outbound — ${phoneNumber}`,
direction: 'OUTBOUND',
callStatus,
+ callerNumber: { primaryPhoneNumber: `+91${phoneNumber}` },
startedAt: startTime ? new Date(startTime).toISOString() : new Date().toISOString(),
endedAt: endTime ? new Date(endTime).toISOString() : null,
durationSec: duration,
@@ -82,8 +83,9 @@ export class KookooCallbackController {
}
return { received: true, processed: true, callId: callResult.createCall.id };
- } catch (err) {
- this.logger.error(`Kookoo callback processing failed: ${err}`);
+ } catch (err: any) {
+ const responseData = err?.response?.data ? JSON.stringify(err.response.data) : '';
+ this.logger.error(`Kookoo callback processing failed: ${err.message} ${responseData}`);
return { received: true, processed: false };
}
}
diff --git a/src/worklist/missed-call-webhook.controller.ts b/src/worklist/missed-call-webhook.controller.ts
index df5a37c..6fa3bb5 100644
--- a/src/worklist/missed-call-webhook.controller.ts
+++ b/src/worklist/missed-call-webhook.controller.ts
@@ -105,8 +105,9 @@ export class MissedCallWebhookController {
}
return { received: true, processed: true, callId, leadId: lead?.id ?? null };
- } catch (err) {
- this.logger.error(`Webhook processing failed: ${err}`);
+ } catch (err: any) {
+ const responseData = err?.response?.data ? JSON.stringify(err.response.data) : '';
+ this.logger.error(`Webhook processing failed: ${err.message} ${responseData}`);
return { received: true, processed: false, error: String(err) };
}
}
@@ -123,21 +124,24 @@ export class MissedCallWebhookController {
disposition: string | null;
ucid: string | null;
}, authHeader: string): Promise {
+ const callData: Record = {
+ name: `${data.direction === 'INBOUND' ? 'Inbound' : 'Outbound'} — ${data.callerPhone}`,
+ direction: data.direction,
+ callStatus: data.callStatus,
+ callerNumber: { primaryPhoneNumber: `+91${data.callerPhone}` },
+ agentName: data.agentName,
+ startedAt: data.startTime ? new Date(data.startTime).toISOString() : null,
+ endedAt: data.endTime ? new Date(data.endTime).toISOString() : null,
+ durationSec: data.duration,
+ disposition: this.mapDisposition(data.disposition),
+ };
+ if (data.recordingUrl) {
+ callData.recording = { primaryLinkUrl: data.recordingUrl, primaryLinkLabel: 'Recording' };
+ }
+
const result = await this.platform.queryWithAuth(
`mutation($data: CallCreateInput!) { createCall(data: $data) { id } }`,
- {
- data: {
- name: `${data.direction === 'INBOUND' ? 'Inbound' : 'Outbound'} — ${data.callerPhone}`,
- direction: data.direction,
- callStatus: data.callStatus,
- agentName: data.agentName,
- startedAt: data.startTime ? new Date(data.startTime).toISOString() : null,
- endedAt: data.endTime ? new Date(data.endTime).toISOString() : null,
- durationSec: data.duration,
- disposition: this.mapDisposition(data.disposition),
- recordingUrl: data.recordingUrl ? { primaryLinkUrl: data.recordingUrl, primaryLinkLabel: 'Recording' } : undefined,
- },
- },
+ { data: callData },
authHeader,
);
return result.createCall.id;
@@ -185,7 +189,7 @@ export class MissedCallWebhookController {
occurredAt: new Date().toISOString(),
performedBy: data.performedBy,
channel: data.channel,
- durationSeconds: data.durationSeconds,
+ durationSec: data.durationSeconds,
outcome: data.outcome,
leadId: data.leadId,
},