For AI assistants: this page as plain markdown is at https://docs.voicelogica.ai/raw/telephony/live-call-analysis.md, and https://docs.voicelogica.ai/llms.txt lists the docs by topic, one file each, small enough to read whole. Read those rather than this HTML page.
Live call analysis by a partner
Ce contenu n’est pas encore disponible dans votre langue.
A partner service, for example fraud detection, can follow the calls on one of your lines (SIP trunks) while they happen and tell the AI on the call what to do.
- Call events. When a call on the line is answered and when it ends, we POST a short JSON message to the partner.
- Audio. For every call we open a WebSocket to the partner and stream both sides of the call.
- Instructions back to the AI. The partner sends a message for a live call, for example “Warn the caller: asking for an OTP while posing as the bank is a known scam.” Only the AI sees it. The AI acts on it right away, in its own words and in the language of the call. The message never shows in the transcript.
Turn it on for a line
Section titled “Turn it on for a line”- Open Telephony, then the line (SIP trunk) whose calls you want analysed.
- In Live call analysis, fill in the partner’s addresses and token (see the table below).
- Click Save live call analysis. From the next call on, every call on that line goes to the partner.
To stop, empty both addresses and save. You can also set it from your AI assistant with the set_trunk_call_stream tool. It is not available on Voice Logica numbers: use your own line.
| Field | Example |
|---|---|
| Audio address (WebSocket) | wss://partner.example.com:8443/voicelogica/stream |
| Events address (HTTP) | https://partner.example.com:8443/voicelogica/events |
| Token | A long random string. We send it as Authorization: Bearer <token> on both. |
Each address is a full URL with the port and path, for example wss://150.140.10.20:8443/stream. Use wss:// and https://: the audio and the phone numbers then travel encrypted. Plain ws:// and http:// are only for tests.
The partner can also accept connections only from our servers’ IP addresses. Ask us for the list.
Call events
Section titled “Call events”For every call on the line, whether the AI answers it or it is connected to a person, we send a short JSON message to the events address as things happen: the call is answered, a transfer starts, works or fails, the call ends.
Event names
Section titled “Event names”Each event’s event field is written subject.action: what it is about, then what happened. Every event today is about the call, so they all start with call.. Later we may add other subjects on the same address (for example a recording or an analysis), so route by the full name and ignore names you don’t know.
| Event | When it is sent |
|---|---|
call.started | The call is answered. |
call.transfer_started | A transfer begins: the AI or a person is passing the call on. |
call.transferred | The transfer worked: the caller is now with the new destination. |
call.transfer_failed | The transfer did not happen: the recipient declined, was busy, did not answer, or the number failed. |
call.transfer_cancelled | The transfer was dropped and the caller stays where they were. |
call.ended | The call is over. |
A call can have several transfers, one after the other. Each attempt sends its own transfer events.
Fields in every event
Section titled “Fields in every event”| Field | |
|---|---|
event | The event name, from the table above. |
callId | Our id for the call. Use it to send instructions to the AI on this call, and to match events and the audio stream of the same call. |
sipCallId | The SIP Call-ID of the call on your line, when there is one. |
sipTrunkId | The line (SIP trunk) the call is on. |
direction | inbound (someone called the line) or outbound (the line called someone). |
from, to | The caller’s and the called number. |
startedAt | When the call started (ISO 8601, UTC). |
Extra fields per event
Section titled “Extra fields per event”| Event | Extra fields |
|---|---|
call.transfer_started, call.transferred, call.transfer_cancelled | transfer.destination (the number or address), transfer.destinationType (e.g. external_number, user, user_group), transfer.mode (blind, attended or warm), transfer.reason (why the AI transferred, when it said). |
call.transfer_failed | The same, plus transfer.failedReason: what happened, as recorded on the call, e.g. “Destination declined”, “busy”, “No answer after 30 seconds of ringing”. |
call.ended | endedAt, durationSeconds, status (e.g. answered, transferred, missed), reason (why it ended, e.g. caller_hungup). |
A field we have no value for is left out rather than sent empty.
How delivery works
Section titled “How delivery works”- Request:
POSTto the events address, bodyapplication/json, headerAuthorization: Bearer <token>. Check the token on every request and refuse anything else. - Your reply: any
2xxwithin 5 seconds. The body is ignored. - Retries: if there is no
2xx(or no answer in 5 seconds) we try once more right away. If that fails too, the event is dropped and the failure is logged on the call. We never hold the call while we wait. - Once only: each event is sent once per call (once per attempt for transfer events), even when several of our servers handle the same call.
- Order: events go out as the call changes, from whichever of our servers handled that change, so two events close together (for example
call.transferredandcall.ended) can arrive in either order. Go by the event name, not by arrival order. - New events and fields: we may add event names and fields without notice. Ignore what you don’t know. Removing or renaming one would be announced first.
Examples
Section titled “Examples”{ "event": "call.started", "callId": "6adf55e278e0d0de8ce090b2", "sipCallId": "a84c3c7f-77c8@10.0.0.5", "sipTrunkId": "69dcacb29e637bf355a3b834", "direction": "inbound", "from": "+306900000000", "to": "+302100000000", "startedAt": "2026-10-26T13:30:11.970Z"}{ "event": "call.ended", "callId": "6adf55e278e0d0de8ce090b2", "sipTrunkId": "69dcacb29e637bf355a3b834", "direction": "inbound", "from": "+306900000000", "to": "+302100000000", "startedAt": "2026-10-26T13:30:11.970Z", "endedAt": "2026-10-26T13:31:31.242Z", "durationSeconds": 79, "status": "answered", "reason": "caller_hungup"}Transfer events carry a transfer object:
{ "event": "call.transfer_failed", "callId": "6adf55e278e0d0de8ce090b2", "sipTrunkId": "69dcacb29e637bf355a3b834", "direction": "inbound", "from": "+306900000000", "to": "+302100000000", "startedAt": "2026-10-26T13:30:11.970Z", "transfer": { "destination": "+302109999999", "destinationType": "external_number", "mode": "warm", "reason": "Caller asked for the accounts department", "failedReason": "Destination declined" }}callId is the id to use when you send instructions for this call.
Audio stream
Section titled “Audio stream”One WebSocket per call, opened as soon as the call reaches us, with Authorization: Bearer <token>. Every call on the line is streamed, whether the AI answers it or it is connected to a person.
1. First message (text, JSON): the call and the audio format.
{ "event": "start", "callId": "6adf55e278e0d0de8ce090b2", "sipTrunkId": "69dcacb29e637bf355a3b834", "direction": "inbound", "from": "+306900000000", "to": "+302100000000", "startedAt": "2026-10-26T13:30:11.970Z", "version": 1, "bidirectional": false, "audio": { "encoding": "pcm_s16le", "sampleRate": 8000, "channels": 2, "frameMs": 20, "layout": ["caller", "callee"], "framing": "channel_prefixed" }}version is the protocol version, 1 today. bidirectional is false: we only send audio, and anything the partner sends back on the socket is ignored. Lines where the partner runs the whole call and its audio is played to the caller will use the same protocol with bidirectional: true, so check both fields.
2. Audio (binary): each message is one side of the call, 20 ms long.
| Byte | Content |
|---|---|
| 0 | Side: 0 = the caller (the person on your line), 1 = the other side (the AI, or the person the call is connected to) |
| 1 onward | 20 ms of PCM 16-bit little-endian, mono. At 8 kHz that is 320 bytes, so each message is 321 bytes. |
The two sides come as separate messages, so you get each one whole, with no mixing and no silence added to line them up.
3. Last message (text, JSON) when the call ends. Then we close the connection. The reason the call ended comes in the call.ended event.
{ "event": "stop", "callId": "6adf55e278e0d0de8ce090b2" }If the partner reads too slowly and more than about 1 MB is waiting to be sent, we drop audio frames until it catches up, so a slow partner never slows the call down.
Send an instruction to the AI on a call
Section titled “Send an instruction to the AI on a call”curl -X POST https://api.voicelogica.ai/api/v1/phones/calls/6adf55e278e0d0de8ce090b2/instructions \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"message": "Warn the caller: asking for an OTP while posing as the bank is a known scam.", "type": "possible_scam", "severity": "high", "confidence": 0.87}'| Field | |
|---|---|
message | Required, up to 2,000 characters. What the AI should do or say now. |
type | Optional, e.g. possible_scam. |
severity | Optional, e.g. warning, high, critical. |
confidence | Optional, 0 to 1. Kept in the call log only. |
The API key needs the call.send_call_instruction permission. Create it on Team and access - API Keys in the company that owns the line (API Keys). From an AI assistant connected to Voice Logica (MCP), the same is the send_call_instruction tool.
Answer: 200 with { "callId": "...", "delivered": true } once the AI has the instruction. 400 when the call has ended, is not answered yet, or has no AI on it. 404 when the call is not one of this company’s calls.
Every instruction is kept in the call’s log with its type, severity and confidence.
Does the caller hear the instruction?
No. Only the AI sees it. The caller hears what the AI then says, in its own words.
Which calls are sent to the partner?
Every call on that line, audio and events, whether the AI answers it or it is connected to a person. To send only some calls, put them on a separate line.
Can the partner send several alerts on one call?
Yes. Each instruction is acted on when it arrives.
