Expertise · Telephony and contact centre
The telephony layer between your operator and whoever answers — a human or an agent.
We build the phone system: SIP trunks, schedule-based routing rules, IVR menus read from the database, waiting queues, transfer to a human, call recording and analysis. The call becomes a record with transcript and summary, not a memory.
Already builtThree in-house implementations, cited below. (1) The Asterisk layer in the Kallina platform: 5,522 lines of dialplan configuration, AGI scripts and audio bridges in `asterisk/`, plus automatically generated trunk templates per number. (2) `asterisk-manager` — a separate service that manages queues, trunks and RTP flow through the PBX management interface. (3) The middleware for a virtual switchboard of an operator, which ran in production: the reconciliation commit of 14 July 2026 brings into the repository code that had been running on the server and was about 40 days ahead of git. The reserve, stated before you ask: the SIP host through which our test lines pass does not respond — checked today, 06.09.2026, with no reply to ping and no reply over HTTP. We do not present a demo number to call now, because we would not be able to bring it up in front of you.
Between your telephone operator and whoever actually answers — a person or a voice agent — there must be a layer that makes decisions. Who takes the call at 23:40. What happens if nobody answers. Where the call goes when the client asks for “a human”. What remains of the conversation after it has ended. This layer is a central exchange, and we build it so that it belongs to the business, not to the voice supplier: if tomorrow you change the agent engine, the routing rules, queues and history remain with you.
Concretely, what we built: twelve dialplan files — production, IVR, queue, transfer, voicemail, forwarding, outbound calls with agent — four AGI scripts in Python and two audio bridges, 5,522 lines in total. The IVR engine does not have the menus written in the file: it reads them from the database through an AGI script and returns one of six decisions — to an agent, to another menu, to a queue, transfer to an external number, final message, or closure. The queues are named after their identifier and do not have members written in the configuration: they are added and removed from outside, through the management interface, which means that an operator can enter or leave the queue without restarting the exchange.
Routing has real rules, not a single “ring here”: business hours and time zone, overnight schedules such as 22:00 → 06:00, and priority matching — the SIP `Diversion` header, which carries the original number from which the call was forwarded, then the own number, then the fallback rule. Trunks are generated per number, with a name built from the provider and number, by a configuration function — not handwritten for each new line.
What comes out of a call is just as important as the call. For the line tied to an operator’s virtual exchange, we built a Python middleware that queries the recordings list every minute, with a broad reconciliation every six hours so a network outage does not lose anything, and a three-minute scan of missed calls — those have no recording and would otherwise be completely invisible. Each recording is downloaded, transcribed, analysed and mirrored in call analytics, and the missed call becomes an alert. Idempotence is held in a Redis set, so the same recording is not processed twice.