Voice agents
Twilio, Vapi of LiveKit voor een Nederlandse voice agent
Een Bossche tandartsgroep met 26 mensen wilde een Nederlandse voice agent voor 2.100 telefoontjes per week. We testten Twilio, Vapi en self-hosted LiveKit. Dit ging stuk.

Dinsdagochtend, 09:14. De receptie van een tandartsgroep met 26 mensen in Den Bosch heeft drie patiënten in de wacht en een vierde lijn die rinkelt. Twee van de drie gesprekken zijn een variant op "ik wil mijn afspraak verzetten". De receptioniste heeft het geprinte weekoverzicht in haar ene hand en een half-bevroren praktijksysteem in haar andere. Ze heeft dit verzet-dansje deze maand ruwweg vierhonderd keer gedaan. De praktijkeigenaar vroeg ons wat er nodig was om een voice agent vóór de gesprekken te zetten die duidelijk alleen een bestaande afspraak willen verplaatsen, en de balie te laten doen wat de balie wél moet doen.
De praktijk draait op 088-nummers, boekt afspraken via een Nederlands tandheelkundig PMS over een ongedocumenteerde REST-laag, en verwerkt zo'n 2.100 inkomende telefoontjes per week verspreid over twee locaties. We zetten drie telefonie-stacks op een shortlist voor de spraaklaag en draaiden elk twee weken tegen een stuk echt verkeer, in de avonden wanneer de praktijk dicht was. De scorecategorieën waren: kosten per minuut bij dat volume, barge-in latency op een rumoerige balielijn, en wie eigenlijk eigenaar is van de SIP-trunk wanneer KPN weer eens een geplande reroute uitvoert.
De drie opties op tafel
Twilio Voice. Inkomende 088-gesprekken via SIP doorgezet vanaf een Nederlandse wholesale carrier naar Twilio, Twilio Media Streams pompt ruwe audio naar onze orchestrator, Deepgram voor Nederlandse STT, Cartesia voor Nederlandse TTS, GPT-4o-mini voor intent-parsing, onze eigen conversatie-state machine op een kleine VPS.
Vapi. De alles-in-één. Vapi regelt de telefonie, de STT, de TTS en de turn-taking, biedt een function-calling-laag voor de PMS-adapter, en levert een gehost dashboard. We brachten ons eigen nummer mee via SIP en wezen de herboek-logica naar dezelfde PMS-adapter.
Self-hosted LiveKit Agents. Het livekit-agents Python-framework op één VM in eu-west, een RoutIT SIP-trunk gericht op de SIP-ingress van LiveKit, Deepgram nova-2 voor Nederlandse STT, Cartesia voor TTS, GPT-4o-mini voor intent, dezelfde PMS-adapter. Twee engineers op de on-call rota.
Hoe we de bake-off draaiden
De praktijk ging akkoord met een avondpilot van twee weken. Gesprekken buiten kantooruren kwamen uit op een IVR met één prompt: "We zijn gesloten. Druk 1 voor een terugbelafspraak of blijf aan de lijn voor een proef van onze nieuwe afsprakenassistent." Patiënten die aan de lijn bleven werden om beurten doorgerouteerd naar één van de drie stacks, gewogen zodat de meest risicovolle gesprekken (oudere bellers, terugkerende patiënten met complexe historie) naar de sterkste kandidaat gingen naarmate de pilot vorderde.
We kozen avonden boven kantooruren om één reden: een gecontroleerde faalmodus. Een patiënt die om 19:30 belt om een donderdag-slot te verzetten heeft tijd om terug te bellen als de agent stuntelt. Een patiënt die om 08:45 belt over een afspraak van 09:00 niet. De agent moest zich pas in de wachtrij overdag bewijzen nadat de avondcijfers in beide praktijken standhielden, en zelfs toen draaide de eerste daadwerkelijke dagweek in shadow-mode achter een mens.
We logden elk gesprek met een mono-opname op 16 kHz (toestemmingsprompt aan het begin), een transcript-paar, VAD-events en de uiteindelijke PMS-write of doorverbinding. Drie metrics telden: completion rate (werd de patiënt daadwerkelijk verzet), barge-in latency op het 90e percentiel, en handoff rate naar de balie (hoe vaak we het over de schutting gooiden). Totaal pilotvolume: 612 gesprekken over twee weken, ruwweg gelijk verdeeld over de drie stacks. De projecties voor kosten per minuut gebruikten het volledige dagvolume dat de productiestack uiteindelijk zou dragen.
Kosten per minuut bij 2.100 gesprekken per week
De gemiddelde gesprekslengte bij herboekingen is korter dan mensen denken: 1m 38s, nadat je mislukte oppakacties en same-day annuleringen eruit haalt. Dat geeft de praktijk ongeveer 3.400 voice-minuten per week, noem het 14.500 per maand. Op dat volume bijt de kostprijs per minuut.
Ruwe maandelijkse kosten medio 2026 op vendor list prices, omgerekend naar euro's, alles inbegrepen (telefonie, STT, TTS, intent-LLM):
- Twilio-pad: rond de €0,09 per minuut, ongeveer €1.300 per maand. Het meeste daarvan is Twilio voice plus het Deepgram- en Cartesia-paar, omdat we de SIP-trunk bij een Nederlandse carrier hielden en Twilio alleen voor media gebruikten.
- Vapi: rond de €0,14 per minuut op de volumetier waar wij voor in aanmerking kwamen, ongeveer €2.030 per maand vóór een minuten-bundelkorting die we niet konden afdwingen.
- Self-hosted LiveKit: rond de €0,06 per minuut plus een vaste €80 voor de VM en €30 voor het SIP-trunk-abonnement, ongeveer €980 per maand.
Wat de tabel niet laat zien: de kosten van operationeel eigenaarschap. Self-hosted LiveKit kostte de eerste maand zo'n vier engineering-uren per week voor tuning, monitoring en een paar Deepgram-hikjes upstream. Dat zakt na week zes naar minder dan een uur per week. Tegen Europese blended rates komt daar in het eerste kwartaal nog €200 tot €400 per maand bij, daarna ruis.
Het kostenverschil is geen principekwestie. Op dit volume zit self-hosted ongeveer 25% onder Twilio en 50% onder Vapi. Onder de 4.000 minuten per maand wordt het gat klein genoeg dat de operationele overhead van LiveKit zichzelf niet meer terugverdient.
Barge-in latency op een rumoerige balielijn
Barge-in is het moment waarop een patiënt de agent midden in een zin onderbreekt. De agent moet binnen zo'n 300 milliseconden stoppen met praten, anders begint het gesprek te voelen als ruziemaken met een kiosk. Tandartsbalies zijn rumoerig: kinderen in de wachtkamer, een autoclaaf die zijn cyclus draait, de espressomachine. Achtergrondgeluid duwt elke VAD-drempel harder.
Twilio Media Streams levert audio frames van 20 ms, maar de bridge zelf voegt zo'n 180 ms one-way toe tussen Frankfurt en Amsterdam. Barge-in onder de 500 ms in totaal is haalbaar, maar het kost werk: je eigen VAD draaien op ruwe RTP en het Media Streams control-channel gebruiken om de TTS-playback te clearen op het moment dat de patiënt begint te praten.
Vapi biedt een redelijke turn-taking-config met een instelbare interruption threshold. We maten 540 tot 720 ms barge-in op rustige avonden, oplopend naar 1,0 tot 1,3 seconden wanneer de baliegeluiden piekten. De VAD tunet goed in een gecontroleerd gesprek, minder goed op een drukke balie.
LiveKit Agents met Silero VAD gaf ons een mediane interrupt-tijd van 280 ms op rustige gesprekken en 480 ms op rumoerige. We moesten de Silero min_speech_duration verlagen naar 150 ms om te voorkomen dat de agent over korte Nederlandse antwoorden als "ja", "nee" en "oké" heen ging praten.
Ruisonderdrukking telt zwaarder dan vendorkeuze. Een ruisonderdrukkende headset van €40 aan de balie deed meer voor de barge-in stabiliteit dan welke platforminstelling we ook probeerden.
Wie de SIP-trunk bezit als KPN een 088-nummer reroutet
Dit is het stuk waar niemand over praat, tot de dag dat KPN onderhoud uitvoert en de voice agent veertig minuten stilvalt.
Nederlandse 088-nummers zijn niet-geografische bedrijfsnummers, gereguleerd door ACM en routeerbaar over carriers heen. Het nummer staat in het ACM-nummerregister. De fysieke signalering loopt door de wholesale carrier waarmee jij contracteert, en KPN zit in het midden van de meeste van die paden omdat het grote stukken van de nationale PSTN-backbone bezit.
Als KPN een 088-range reroutet voor gepland werk (en dat doen ze meerdere keren per jaar), regelen de carriers die met ze peeren de failover. RoutIT, Voiceworks/Destiny en Belcentrale hebben allemaal directe KPN-peering en failoveren binnen seconden. Twilio's SIP-interconnect voor Nederlandse nummers heeft niet dezelfde peering-stamboom. Als jouw 088 SIP-trunk in Twilio's Dublin- of Frankfurt-POP woont, zit je één hop verder weg van het reroute-event en kan de SIP REGISTER achterlopen.
Tijdens de pilot vingen we zo'n reroute-event op: een onderhoudsgedreven path switch op een doordeweekse ochtend, die de 088-range van de tandartsgroep kort raakte. Het Vapi-been begon binnen negentig seconden SIP 503 terug te geven en bleef ongeveer veertig minuten gedegradeerd. Het RoutIT-LiveKit-been bleef het hele venster door rinkelen, omdat de failover binnen RoutIT's eigen peering-relatie met KPN gebeurde. In dat venster van veertig minuten verwerkte LiveKit drieëntwintig gesprekken. Vapi nul.
Houd voor Nederlandse 088-voice agents de SIP-trunk bij een KPN-gepeerde wholesaler. Behandel de AI-leverancier (Twilio, Vapi, LiveKit) als media-endpoint, nooit als carrier.
De stack die we live brachten
LiveKit Agents op één 4-core VM in eu-west, het Deepgram nova-2 Nederlandse model, Cartesia Sonic Nederlandse vrouwenstem, GPT-4o-mini voor intent en slot-filling, een kleine Python REST-adapter naar de tandarts-PMS, RoutIT SIP-trunk. De agent neemt op, bevestigt dat de patiënt een bestaande afspraak wil verplaatsen, zoekt hem op via caller ID, leest het huidige slot terug, biedt de eerstvolgende drie slots op dezelfde weekdag aan, en schrijft de wijziging terug. Alles wat off-script gaat verbindt door naar de balie met de gevangen context in één regel ("Patient Hassink wil afspraak van donderdag 18 juni 14:30 verplaatsen, voorkeur begin volgende week").
Voice assistant-config, Python:
# agent.py - barge-in tuned for a Dutch dental reception
from livekit.agents import VoiceAssistant
from livekit.plugins import deepgram, cartesia, silero, openai
assistant = VoiceAssistant(
vad=silero.VAD.load(
min_speech_duration=0.15, # catch short Dutch "ja"/"nee"
min_silence_duration=0.35,
),
stt=deepgram.STT(language="nl", model="nova-2-general"),
llm=openai.LLM(model="gpt-4o-mini", temperature=0.1),
tts=cartesia.TTS(voice="nl-NL-female-1", language="nl", speed=0.92),
interrupt_speech_duration=0.2,
interrupt_min_words=1,
chat_ctx=load_system_prompt("rebook_dental_nl.md"),
)
SIP-trunk-config die RoutIT inbound naar de LiveKit-gateway wijst:
# livekit-sip-trunk.yaml
trunks:
- name: routit-088-inbound
kind: inbound
numbers:
- "+31889999999"
auth_username: dental-rebook-eu1
allowed_addresses:
- sip.routit.net
- sip2.routit.net
media_encryption: prefer
Wat we verkeerd deden
Drie dingen, allemaal op te lossen.
We begonnen met de standaard Nederlandse stem van Cartesia op de standaard spreeksnelheid. Te snel voor de oudere patiëntdemografie van deze praktijk. We zetten de snelheid op 92% en de completion rate sprong in week twee van 71% naar 84%. Geen enkele latency-tuning had hetzelfde verschil gemaakt.
We onderschatten hoeveel de system prompt uitmaakte ten opzichte van de latency-tuning. De eerste prompt vroeg de agent om alles voor de zekerheid twee keer te bevestigen. Oudere patiënten gingen zich herhalen, de agent bevestigde opnieuw, en een gesprek van 90 seconden zwol op tot drie minuten. De prompt aanscherpen tot één read-back aan het einde van de boeking sneed 47 seconden van het gemiddelde gesprek af, zonder meetbaar verlies aan nauwkeurigheid. De les: optimaliseer het script voordat je de milliseconden optimaliseert.
We zetten voor de pilot eerst Vapi's BYO-number-setup in om integratietijd te besparen. Het reroute-incident hierboven gaf de doorslag richting volledig self-hosten van het mediapad. De les was niet dat Vapi slecht is. De les was dat elke vendor wiens default trunk buiten Nederland staat een peering-risico erft waar je je niet uit kunt tunen.
Wanneer self-hosted wint en wanneer niet
LiveKit zelf hosten heeft zin als je een voorspelbaar volume hebt boven ongeveer 10.000 minuten per maand, je een engineer beschikbaar kunt houden voor on-call, en je opereert in een taal en nummerrange waar carrier-eigenaarschap er toe doet. Voor een kleine praktijk met 500 minuten per week is Vapi of Twilio met een Nederlandse BYO-trunk de juiste keuze. De rekensom kantelt pas voorbij een bepaald volume, en de operationele overhead is reëel.
Toen we de herboek-voice agent voor deze Bossche tandartsgroep bouwden, was wat ons het meest verbaasde hoe zwaar carrier-peering meetelde ten opzichte van de AI-stack erbovenop. Als je voor een Nederlandse mkb'er dezelfde afweging maakt: onze AI-agents kiezen standaard voor het self-hosted mediapad bij elke klant boven 10.000 minuten per maand.
De audit van vijf minuten die je vandaag kunt doen: pak je laatste telefoonrekening, zoek welke carrier achter je 088-nummer zit, en check of die SIP-trunking direct naar een third-party media-endpoint ondersteunt. Zo ja, dan kun je de AI-stack erbovenop altijd nog wisselen. Zo nee, dan is dat het gesprek dat je moet starten vóór het voice-agent-project, niet erna.
Kern
Voor een Nederlandse voice agent boven 10.000 minuten per maand: houd de SIP-trunk bij een KPN-gepeerde wholesaler en behandel de AI-leverancier als media-endpoint.
FAQ
Kan Twilio Nederlandse 088-nummers native aan?
Niet als primaire carrier. Je hebt een Nederlandse wholesale carrier nodig (RoutIT, Voiceworks/Destiny, Belcentrale) om de trunk te dragen en via SIP door te zetten. Twilio zit erbovenop als media-endpoint.
Welke barge-in latency haalt LiveKit op Nederlandse gesprekken?
Mediaan 280 ms op een rustige lijn met Silero VAD op 150 ms minimum speech duration. Reken op 450 tot 550 ms op een rumoerige balielijn.
Werkt Vapi out of the box in het Nederlands?
Ja, via de meegeleverde Deepgram- en Cartesia-config, maar de standaard spreeksnelheid is te hoog voor oudere Nederlandse demografieën. Zet 'm 5 tot 10% trager voor klinieken en zorginstellingen.
Wat kost een self-hosted voice agent bij 14.500 minuten per maand?
Ongeveer €980 per maand alles inbegrepen op de list prices van medio 2026: ruwweg €0,06 per minuut media plus een vast VM- en SIP-trunk-abonnement.
Waarom is KPN-peering belangrijk voor een voice agent?
KPN reroutet 088-ranges meerdere keren per jaar. Carriers die direct met KPN peeren failoveren in seconden. Trunks die buiten Nederland staan kunnen gesprekken verliezen voor de duur van het onderhoudsvenster.