IVR pour “FreeSWITCH + STT/TTS cloud + orchestration n8n”,

Si votre contrainte centrale est “FreeSWITCH + STT/TTS cloud + orchestration n8n”, la solution open source la plus “naturellement compatible” (au sens programmable, API/webhooks-first, orientée Voice AI) est généralement :



Recommandation principale : jambonz (IVR programmable / CPaaS open source)


Pourquoi c’est le meilleur fit dans votre cas

  • Modèle “applications” via webhooks / API : vous pilotez la logique d’appel comme un workflow, ce qui s’assemble très bien avec n8n (HTTP nodes, queues, webhooks). jambonz.org+1
  • Conçu pour intégrer des briques STT/TTS (providers externes) sans vous enfermer dans un dialplan monolithique. jambonz.org
  • S’appuie sur FreeSWITCH côté media (MRF) dans son architecture (déploiements Helm/K8s disponibles). artifacthub.io

Cas d’usage typique

  • IVR “conversationnel” (DTMF + voix), collecte d’intentions, appels à vos API (Odoo/Supabase), notifications sortantes (SMS/RCS/WhatsApp), transfert warm/cold, enregistrement, etc.
  • n8n devient votre orchestrateur : “IVR event → n8n → décision → jambonz → next step”.

IVR conversationnel (voix → STT → logique → TTS → voix) au-dessus de FreeSWITCH,

Pour un IVR conversationnel (voix → STT → logique → TTS → voix) au-dessus de FreeSWITCH, avec orchestration par n8n, l’option open source la plus cohérente et “native Voice AI” est :



Choix recommandé : jambonz (plateforme voix programmable open source, basée sur FreeSWITCH)


Pourquoi c’est le meilleur fit pour votre cas (FreeSWITCH + STT/TTS + n8n)

  • Architecture “programmable” : l’appel est piloté par des webhooks / API (plutôt qu’un dialplan statique). Cela se marie très bien avec n8n (Webhooks + HTTP Request + files/queues + orchestration). docs.jambonz.org+1
  • FreeSWITCH comme media server dans l’écosystème jambonz (déploiements et composants dédiés “MRF”). docs.jambonz.org+1
  • Écosystème Voice AI : modules et intégrations orientés streaming / transcription / TTS (ex. modules FS “pour jambonz”, intégrations STT/TTS côté partenaires). GitHub+1



Pattern d’intégration (concret) avec n8n

  • FreeSWITCH/jambonz gère la session temps réel (SIP/RTP, enregistrement, bridge/transfert).
  • jambonz émet des événements vers vos webhooks n8n (début d’appel, tour de parole, résultat STT, intention, etc.). docs.jambonz.org
  • n8n exécute la state machine “métier” (règles, RAG, appels Odoo/Supabase, scheduling, notifications) et renvoie à jambonz la prochaine action (prompt TTS, question suivante, confirmation, transfert agent, etc.).





Schéma d’architecture cible et une state machine JSON directement exploitable dans n8n.

En 100% voix, votre critère de succès n’est pas “un IVR qui marche”, mais un IVR qui reste fluide (latence maîtrisée), robuste (reprises / erreurs), et pilotable (state machine, observabilité, itérations rapides) tout en s’intégrant proprement à FreeSWITCH et à n8n.

Recommandation : jambonz + FreeSWITCH (IVR conversationnel programmable)

Pourquoi c’est le meilleur choix open source pour 100% voix

  • Conçu pour des applications voix pilotées par webhooks / websocket : jambonz expose un modèle d’application “programmable” (événements d’appel → votre logique → réponse d’actions), ce qui colle parfaitement à n8n (webhook trigger + HTTP response). docs.jambonz.org+1
  • S’appuie sur FreeSWITCH côté media (MRF / media server), donc vous gardez votre socle télécom tout en “externalisant” la logique conversationnelle. docs.jambonz.org+1
  • Déploiement industrialisable (Helm/Kubernetes disponible) si vous devez passer du PoC à la prod sans tout re-architecturer. GitHub
  • Point clé : vous évitez de “cimenter” votre logique dans le dialplan FreeSWITCH. Vous la mettez dans une state machine (n8n), ce qui accélère énormément les itérations.

Pourquoi pas “FreeSWITCH pur” directement pour le conversationnel

C’est faisable, mais vous allez ré-implémenter vous-mêmes :

  • la couche “programmable IVR” (webhooks, retries, versioning),
  • des patterns d’observabilité,
  • une partie de la gestion d’état conversationnel.
    C’est pertinent si vous avez déjà une équipe VoIP très senior et du temps. Sinon, jambonz vous fait gagner un cycle complet.

Où se place MRCP (mod_unimrcp) dans votre stratégie

FreeSWITCH supporte ASR/TTS via mod_unimrcp (MRCP v1/v2). developer.signalwire.com+1

C’est une bonne option si vous standardisez tout via un serveur MRCP (on-prem ou vendor). Mais si vous consommez des STT/TTS “modernes” (streaming, webhooks, événements) et que vous voulez que n8n orchestre tout, jambonz est généralement plus naturel.

Architecture cible 100% voix (simple et efficace)

Composants

  • FreeSWITCH : media + SIP/WebRTC + enregistrements + transferts.
  • jambonz : “voice application runtime” (call control + events).
  • n8n : state machine conversationnelle + intégrations (Odoo/Supabase/RAG/notifications).
  • STT/TTS : vos providers externes (via jambonz et/ou via un microservice VoiceAI).
  • (Optionnel mais recommandé) Redis pour l’état court terme, S3/Supabase Storage pour les médias.

Boucle conversationnelle (tour de parole)

  1. TTS (prompt) : “Bonjour… dites-moi ce que vous souhaitez…”
  2. Barge-in + VAD : l’utilisateur coupe, on stoppe le TTS immédiatement.
  3. STT streaming : transcription partielle + finale.
  4. n8n décide : intention + slots + prochaine question / action.
  5. TTS de la réponse + éventuellement action side-effect (création RDV, SMS récap, etc.)
  6. Répéter jusqu’à clôture/transfert.

Ce que vous devez absolument cadrer en 100% voix

1) Latence “perçue”

Objectif pratique : < 700–900 ms entre fin de phrase utilisateur et début de la réponse TTS.

Mesures pour y arriver :

  • Streaming STT (partiels) + décision anticipée quand confiance suffisante.
  • TTS en chunks + pré-génération des phrases fréquentes (caching).
  • n8n : éviter les branches qui appellent 6 APIs en série ; préférer parallélisation / timeouts.

2) Barge-in (indispensable)

Sans barge-in, l’expérience est pénible. Il faut :

  • pouvoir interrompre un TTS en cours,
  • rebasculer immédiatement en écoute STT.

3) Gestion d’échec (le vrai sujet)

Prévoyez nativement :

  • No speech (silence) : reprompt progressif (x2), puis escalade.
  • Low confidence : reformulation (“J’ai compris … est-ce correct ?”).
  • Ambiguïté : question de désambiguïsation courte.
  • Fallback : transfert agent / rappel / SMS avec lien.


Implémentation n8n : “state machine” minimale (pattern recommandé)

Webhook d’entrée (trigger)

  • POST /ivr/event
  • Payload : {call_id, from, to, event_type, stt_final, confidence, language, ...}

Stockage état (court terme)

  • Redis / Postgres : call_id -> {state, context, retries, last_intent, …}

Réponse n8n (commande)

n8n renvoie à jambonz une “action” :

  • say (TTS)
  • listen (STT config)
  • call_api (side effects)
  • transfer (warm transfer)
  • hangup

Ce pattern s’aligne bien avec le modèle “app programmable” côté jambonz (webhook/websocket). docs.jambonz.org+1


Décision recommandée (sans ambiguïté)

Pour un IVR 100% conversationnel avec FreeSWITCH et une orchestration métier via n8n :

  1. jambonz + FreeSWITCH comme socle “voix programmable”. docs.jambonz.org+1
  2. n8n comme moteur de state machine + intégrations. n8n Docs+1
  3. MRCP/mod_unimrcp uniquement si vous avez une contrainte forte “tout passe par MRCP”. developer.signalwire.com+1

Proposition d'archi  pour des prises RDV + rappel + replanification + transfert agent”

Voici une proposition directement exploitable pour votre scénario 100% voix :

  • Prise de RDV
  • Rappel
  • Replanification
  • Transfert agent (warm/cold)

Hypothèse d’architecture (cohérente avec ce que vous visez) : FreeSWITCH (media) + jambonz (call-control programmable) + n8n (state machine métier) + STT/TTS provider.

1) Contrat d’intégration : événements → n8n → actions

Endpoint unique n8n

  • POST https://<votre-n8n>/webhook/ivr

Payload d’événement (minimal, stable)

{ "call_id": "uuid", "direction": "inbound", "from": "+33...", "to": "+33...", "event": "call_start|stt_partial|stt_final|no_speech|dtmf_disabled|hangup|transfer_result", "timestamp": "2026-01-02T18:55:12.123Z", "stt": { "text": "je voudrais déplacer mon rendez-vous", "is_final": true, "confidence": 0.86, "language": "fr-FR" }, "meta": { "channel": "pstn|webrtc", "recording": true } }

Réponse attendue par jambonz (action)

{ "actions": [ { "type": "say", "text": "Très bien. Pour quel jour souhaitez-vous le rendez-vous ?" }, { "type": "listen", "mode": "stt", "timeout_ms": 6000, "barge_in": true } ], "state": { "name": "SLOT_DATE", "context": { "intent": "RESCHEDULE" } } }

Règle d’or : n8n renvoie toujours une liste d’actions + un state sérialisé.

2) State machine IVR (JSON) – MVP robuste

États et transitions (version “production-minded”)

{ "machine": "synergia_ivr_rdv_v1", "initial": "START", "states": { "START": { "on": { "call_start": "CONSENT_AND_LANGUAGE" } }, "CONSENT_AND_LANGUAGE": { "prompt": "Bonjour. Pour gérer votre rendez-vous, nous allons traiter votre demande vocalement. Dites simplement : prendre rendez-vous, déplacer, ou annuler.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final": "INTENT_ROUTER", "no_speech": "REPROMPT_1" } }, "REPROMPT_1": { "prompt": "Je n'ai pas entendu. Dites : prendre rendez-vous, déplacer, annuler, ou parler à un agent.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final": "INTENT_ROUTER", "no_speech": "REPROMPT_2" } }, "REPROMPT_2": { "prompt": "Toujours pas de réponse. Je peux vous mettre en relation avec un agent.", "actions": [ { "type": "say" }, { "type": "transfer", "mode": "warm", "target": "AGENT_QUEUE", "timeout_ms": 25000 } ], "on": { "transfer_result": "END" } }, "INTENT_ROUTER": { "logic": "nlp_intent_extract", "on_intent": { "BOOK": "IDENTIFY_PATIENT", "RESCHEDULE": "IDENTIFY_PATIENT", "CANCEL": "IDENTIFY_PATIENT", "AGENT": "TRANSFER_AGENT", "UNKNOWN": "CLARIFY_INTENT" } }, "CLARIFY_INTENT": { "prompt": "Je peux prendre un rendez-vous, le déplacer, l'annuler, ou vous passer un agent. Que souhaitez-vous ?", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final": "INTENT_ROUTER", "no_speech": "REPROMPT_2" } }, "IDENTIFY_PATIENT": { "prompt": "Dites votre nom et votre prénom.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final": "PATIENT_LOOKUP", "no_speech": "REPROMPT_1" } }, "PATIENT_LOOKUP": { "logic": "crm_lookup", "on_result": { "FOUND_ONE": "CONFIRM_IDENTITY", "FOUND_MULTI": "DISAMBIGUATE_IDENTITY", "NOT_FOUND": "ASK_PHONE_CONFIRM" } }, "CONFIRM_IDENTITY": { "prompt_template": "Je vous ai identifié comme {full_name}. Confirmez-vous ?", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 6000 } ], "on": { "stt_final_yes": "FLOW_ROUTER", "stt_final_no": "DISAMBIGUATE_IDENTITY", "no_speech": "REPROMPT_1" } }, "DISAMBIGUATE_IDENTITY": { "prompt": "Je trouve plusieurs dossiers. Dites votre date de naissance.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final": "PATIENT_LOOKUP_REFINE", "no_speech": "REPROMPT_1" } }, "ASK_PHONE_CONFIRM": { "prompt": "Je ne trouve pas votre dossier. Dites votre numéro de téléphone, chiffre par chiffre.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 9000 } ], "on": { "stt_final": "PATIENT_CREATE_OR_ESCALATE", "no_speech": "REPROMPT_2" } }, "FLOW_ROUTER": { "logic": "route_by_intent", "on_intent": { "BOOK": "BOOK_COLLECT_CONSTRAINTS", "RESCHEDULE": "GET_EXISTING_APPT", "CANCEL": "GET_EXISTING_APPT", "AGENT": "TRANSFER_AGENT" } }, "GET_EXISTING_APPT": { "logic": "fetch_next_appointment", "on_result": { "FOUND": "EXISTING_APPT_SUMMARY", "NONE": "NO_APPT_FALLBACK" } }, "EXISTING_APPT_SUMMARY": { "prompt_template": "Votre prochain rendez-vous est le {date} à {time}. Souhaitez-vous le déplacer, l'annuler, ou le conserver ?", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 8000 } ], "on": { "stt_final": "RESCHEDULE_OR_CANCEL_ROUTER", "no_speech": "REPROMPT_1" } }, "RESCHEDULE_OR_CANCEL_ROUTER": { "logic": "intent_extract_reschedule_cancel_keep", "on_intent": { "RESCHEDULE": "RESCHEDULE_PROPOSE_SLOTS", "CANCEL": "CANCEL_CONFIRM", "KEEP": "REMINDER_OPTIN", "AGENT": "TRANSFER_AGENT", "UNKNOWN": "CLARIFY_INTENT" } }, "BOOK_COLLECT_CONSTRAINTS": { "prompt": "Très bien. Dites vos préférences : par exemple, mardi matin ou jeudi après-midi.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 9000 } ], "on": { "stt_final": "BOOK_PROPOSE_SLOTS", "no_speech": "REPROMPT_1" } }, "BOOK_PROPOSE_SLOTS": { "logic": "compute_3_slots", "on_result": { "SLOTS": "SLOTS_READOUT", "NO_SLOTS": "NO_SLOTS_FALLBACK" } }, "RESCHEDULE_PROPOSE_SLOTS": { "logic": "compute_3_slots", "on_result": { "SLOTS": "SLOTS_READOUT", "NO_SLOTS": "NO_SLOTS_FALLBACK" } }, "SLOTS_READOUT": { "prompt_template": "Je peux vous proposer : 1) {s1}, 2) {s2}, 3) {s3}. Dites option 1, 2, 3, ou agent.", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 9000 } ], "on": { "stt_final": "SLOT_PICKER", "no_speech": "REPROMPT_1" } }, "SLOT_PICKER": { "logic": "pick_slot", "on_result": { "PICKED": "BOOKING_COMMIT", "UNKNOWN": "SLOTS_READOUT" } }, "BOOKING_COMMIT": { "logic": "create_or_update_appointment", "on_result": { "OK": "REMINDER_OPTIN", "FAIL": "FAILOVER_AGENT" } }, "CANCEL_CONFIRM": { "prompt": "Confirmez-vous l'annulation ?", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 6000 } ], "on": { "stt_final_yes": "CANCEL_COMMIT", "stt_final_no": "REMINDER_OPTIN", "no_speech": "REPROMPT_1" } }, "CANCEL_COMMIT": { "logic": "cancel_appointment", "on_result": { "OK": "END_CONFIRMATION", "FAIL": "FAILOVER_AGENT" } }, "REMINDER_OPTIN": { "prompt": "Souhaitez-vous recevoir un rappel la veille et une heure avant ?", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final_yes": "SCHEDULE_REMINDERS", "stt_final_no": "END_CONFIRMATION", "no_speech": "END_CONFIRMATION" } }, "SCHEDULE_REMINDERS": { "logic": "create_reminder_jobs", "on_result": { "OK": "END_CONFIRMATION", "FAIL": "END_CONFIRMATION" } }, "TRANSFER_AGENT": { "actions": [ { "type": "say", "text": "Je vous mets en relation avec un agent." }, { "type": "transfer", "mode": "warm", "target": "AGENT_QUEUE", "timeout_ms": 30000 } ], "on": { "transfer_result": "END" } }, "NO_APPT_FALLBACK": { "prompt": "Je ne vois pas de rendez-vous à venir. Souhaitez-vous en prendre un ?", "actions": [ { "type": "say" }, { "type": "listen", "mode": "stt", "barge_in": true, "timeout_ms": 7000 } ], "on": { "stt_final_yes": "BOOK_COLLECT_CONSTRAINTS", "stt_final_no": "TRANSFER_AGENT", "no_speech": "TRANSFER_AGENT" } }, "NO_SLOTS_FALLBACK": { "prompt": "Je n'ai pas de créneau compatible. Je peux vous proposer un agent ou vous envoyer un lien pour choisir.", "actions": [ { "type": "say" }, { "type": "transfer", "mode": "warm", "target": "AGENT_QUEUE", "timeout_ms": 30000 } ], "on": { "transfer_result": "END" } }, "FAILOVER_AGENT": { "prompt": "Je rencontre une difficulté. Je vous passe un agent.", "actions": [ { "type": "say" }, { "type": "transfer", "mode": "warm", "target": "AGENT_QUEUE", "timeout_ms": 30000 } ], "on": { "transfer_result": "END" } }, "END_CONFIRMATION": { "prompt": "C'est noté. Merci et bonne journée.", "actions": [ { "type": "say" }, { "type": "hangup" } ] }, "END": { "type": "final" } } }


3) Workflow n8n (découpage en nœuds)

Webhook → Router → State → Actions

  1. Webhook Trigger /webhook/ivr
  2. Function (Normalize Event)
    • normalise event, call_id, stt.text, confidence
  3. Data Store (Get State)
    • clé : call_id (Redis/Postgres)
  4. Switch (event type)
    • call_start
    • stt_final
    • no_speech
    • transfer_result
  5. Sub-workflow “Intent Extract” (si stt_final)
    • peut être un appel HTTP à votre service NLP (ou LLM)
    • sort : {intent, slots, confidence}
  6. Sub-workflow “CRM Lookup” (si état IDENTIFY)
    • appel Odoo/CRM (ou autre)
  7. Sub-workflow “Compute 3 slots”
    • appelle votre moteur de disponibilité (calendar)
    • sort 3 créneaux “lisibles”
  8. Sub-workflow “Commit appointment”
    • create/update/cancel
  9. Sub-workflow “Schedule reminders”
    • crée 2 jobs (J-1, H-1) dans votre scheduler (n8n cron + DB, ou queue)
  10. Data Store (Set State)
  11. Respond to Webhook
  • renvoie actions[] + state

4) Rappels : modèle d’exécution recommandé

Jobs rappel

  • J-1 : message vocal sortant (ou SMS/WhatsApp en parallèle si vous le souhaitez)
  • H-1 : idem + option “dire REPLANIFIER” pour revenir dans le flow

Important : même en “100% voix”, il est utile d’avoir un fallback lien (si l’utilisateur est inaudible), mais vous pouvez garder cela optionnel.

5) Transfert agent : warm transfer “propre” (expérience utilisateur)

  • Warm : vous annoncez à l’agent un bref contexte (ID patient + intention + créneau choisi) via :
    • un “screen-pop” (URL) envoyé à votre outil agent,
    • ou un message interne (queue/CRM),
    • ou un court TTS “résumé” pour l’agent (si vous avez un canal).
  • Cold : transfert direct si l’agent n’a pas besoin de contexte.

6) Paramètres “100% voix” à figer dès le MVP

  • barge_in = true partout (sinon UX mauvaise)
  • no_speech : 2 tentatives puis transfert agent
  • low_confidence : confirmation (“J’ai compris X, confirmez-vous ?”)
  • timeouts réalistes :
    • écoute : 6–9s
    • transfert : 25–30s
  • journalisation structurée (par call_id) :
    • état courant, intention, slots, erreurs, durée STT/TTS

Prochaine étape (sans vous poser de questions supplémentaires)

Je peux vous fournir immédiatement, dans le même format “copiable”, les 3 éléments suivants :

  1. Schéma de données context (patient_id, appointment_id, slots[], retry_count, etc.)
  2. Exemples de réponses actions[] pour chaque grand état (SLOTS_READOUT, CONFIRM_IDENTITY, TRANSFER_AGENT…)
  3. Template n8n (pseudo-export) : liste exacte des nœuds et variables, prête à reconstituer rapidement.

Dites-moi simplement si votre backend RDV est plutôt : Odoo Calendar / Google Calendar / un agenda OBS / autre ; je mapperai les appels “compute slots / commit appointment” sur l’API correspondante.

votre backend RDV  Avec Odoo Calendar,

Très bien. Avec Odoo Calendar, vous avez un socle propre pour gérer la prise / modification / annulation sous forme d’objets standards (calendar.event) et vous pouvez piloter tout cela depuis n8n via JSON-RPC Odoo.

Ci-dessous : (1) le mapping Odoo, (2) les appels JSON-RPC, (3) une stratégie “3 créneaux” réaliste, (4) la gestion des rappels, (5) le transfert agent avec “screen-pop”.


Découvrir plus

votre backend RDV est Google Calendar

Ci-dessous, en format copiable, les 3 éléments + l’adaptation si le backend RDV est Google Calendar (au lieu d’Odoo Calendar). Je conserve un design où n8n porte la state machine et où le “backend agenda” est interchangeable via 3 fonctions : lookup_patient, compute_slots, commit_event.


Découvrir plus

ANNEXE : les alternative à JAMBOMZ ? 

Alternatives solides (selon votre “style” d’implémentation)



Option A — FusionPBX (open source) : excellent si vous voulez du “PBX + IVR classique” avec UI

  • FusionPBX fournit une interface web complète au-dessus de FreeSWITCH (routage, IVR DTMF, extensions, etc.). docs.fusionpbx.com
  • Adapté si votre IVR reste principalement DTMF, avec quelques appels HTTP vers n8n/Odoo (mais l’UX “Voice AI” est moins native que jambonz).



Option B — FreeSWITCH “pur” + scripts (Lua/JS/Python) + n8n

  • Approche la plus “bas niveau”, très flexible, mais vous construisez davantage vous-même (state machine, tests, observabilité, versioning).
  • Très viable si vous avez une équipe dev VoIP solide et que vous voulez un contrôle total.



Option C — UniMRCP / mod_unimrcp : pertinent si vous voulez STT/TTS via MRCP

  • FreeSWITCH supporte l’intégration ASR/TTS via mod_unimrcp (SSML, speak/playback, etc.). developer.signalwire.com+1
  • Très bien si vous passez par un serveur MRCP (on-prem ou vendor). En revanche, pour des APIs STT/TTS “modernes” type webhooks/streaming, vous aurez souvent plus de travail d’adaptation.



Option D — 2600Hz Kazoo : “carrier-grade”, mais plus lourd

  • Plateforme API-based bâtie autour de FreeSWITCH, très puissante, mais c’est une stack opérateur (complexité d’exploitation plus élevée). 2600hz.org+1

Tableau de choix (copiable)

Option“Fit” STT/TTS + n8nTime-to-marketComplexité opsIdéal pour
jambonzTrès élevéRapideMoyenneIVR programmable, Voice AI, webhooks, workflows
FusionPBXMoyenRapideFaible→MoyennePBX/IVR DTMF avec UI admin
FreeSWITCH pur + scriptsÉlevéMoyen→LentMoyenneContrôle total, logique sur-mesure
mod_unimrcp (MRCP)Variable (selon MRCP)MoyenMoyenneASR/TTS via MRCP, environnements téléphonie “classiques”
Kazoo (2600Hz)ÉlevéLentÉlevéePlateforme opérateur multi-tenant à grande échelle