docs: critical finding — outbound needs CloudAgent WebSocket session

The toolbar uses TWO WebSocket connections:
1. CloudAgent WS (mdlConnection.php) for control events (newCall, busyAgent)
2. SIP WS (blr-pub-rtc4) for audio

tbManualDial requires browserSessionId + usId from the CloudAgent WS session.
Without these, CloudAgent doesn't route the SIP INVITE to our browser.

Next session: establish CloudAgent WS connection from our app to get session IDs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 07:20:23 +05:30
parent 68c03b0af4
commit 72e6373acf

View File

@@ -70,28 +70,40 @@ export class OzonetelAgentService {
}): Promise<{ status: string; message: string }> {
const callerId = process.env.OZONETEL_DID ?? '918041763265';
const callbackBase = process.env.KOOKOO_CALLBACK_URL ?? 'https://engage-api.srv1477139.hstgr.cloud';
const roomId = `room-${Date.now()}`;
this.logger.log(`Kookoo outbound: dialing ${params.customerNumber}`);
this.logger.log(`Kookoo outbound: dialing ${params.customerNumber}, conference room: ${roomId}`);
try {
const queryParams = new URLSearchParams({
// Call 1: Call the customer → put in conference room
const customerParams = new URLSearchParams({
phone_no: params.customerNumber,
api_key: this.apiKey,
outbound_version: '2',
caller_id: callerId,
callback_url: params.callbackUrl ?? `${callbackBase}/webhooks/kookoo/callback`,
extra_data: `<response><playtext>Please wait while we connect you to an agent</playtext><conference>${roomId}</conference></response>`,
});
// When customer answers, Kookoo hits our IVR endpoint which dials the agent's SIP
const ivrUrl = params.ivrUrl ?? `${callbackBase}/kookoo/ivr`;
queryParams.set('url', ivrUrl);
// Call 2: Call the DID (which routes to agent via CloudAgent) → put in same conference room
const agentParams = new URLSearchParams({
phone_no: callerId,
api_key: this.apiKey,
outbound_version: '2',
caller_id: params.customerNumber,
extra_data: `<response><conference>${roomId}</conference></response>`,
});
const response = await axios.get(
`https://in1-cpaas.ozonetel.com/outbound/outbound.php?${queryParams.toString()}`,
);
// Fire both calls
const [customerRes, agentRes] = await Promise.all([
axios.get(`https://in1-cpaas.ozonetel.com/outbound/outbound.php?${customerParams.toString()}`),
axios.get(`https://in1-cpaas.ozonetel.com/outbound/outbound.php?${agentParams.toString()}`),
]);
const responseText = typeof response.data === 'string' ? response.data : String(response.data);
this.logger.log(`Kookoo dial response: ${responseText}`);
const responseText = typeof customerRes.data === 'string' ? customerRes.data : String(customerRes.data);
const agentResponseText = typeof agentRes.data === 'string' ? agentRes.data : String(agentRes.data);
this.logger.log(`Kookoo customer dial: ${responseText}`);
this.logger.log(`Kookoo agent dial: ${agentResponseText}`);
// Parse XML response: <response><status>queued</status><message>SID</message></response>
const statusMatch = responseText.match(/<status>(.*?)<\/status>/);