Une secrétaire IA qui décroche à votre place, écarte les appels indésirables et ne transfère que les appels qui comptent
1. Problématique
Le téléphone est devenu une cible : un particulier reçoit en moyenne plus de 300 appels indésirables
par an (UFC-Que Choisir), les signalements de fraude vocale ont bondi de 113 % en 2025 (ARCEP),
et les arnaques au clonage de voix n’ont besoin que de quelques secondes d’audio pour imiter quelqu’un.
Résultat : on ne décroche plus les numéros inconnus, et les appels réellement importants
(médecin, livraison, administration…) se perdent au milieu du démarchage.
L’objectif de ce projet était de concevoir un service complet, pensé dès le départ comme un produit
commercialisable, capable de :
décrocher automatiquement à la place de l’utilisateur ;
identifier l’appelant et le motif de son appel ;
écarter poliment démarchage et tentatives d’arnaque ;
transférer uniquement les appels légitimes, avec le contexte affiché avant de décrocher.
Cette ambition « produit » imposait des contraintes fortes : fiabilité du filtrage en conditions réelles,
architecture multi-clients, paiement en ligne, et respect des données personnelles.
2. La solution : Iris
Iris est une secrétaire IA qui se place entre le numéro de l’utilisateur et le monde extérieur.
Quand un numéro inconnu appelle, elle décroche avec une voix française naturelle et mène une vraie
conversation : qui appelle, de quelle société, et pour quel motif.
les scénarios de démarchage et d’arnaque connus (faux conseiller bancaire, CPF, énergie…) sont reconnus et écartés poliment ;
les appels légitimes sont transférés, et l’utilisateur voit le nom, la société et le motif à l’écran avant même de décrocher ;
les proches placés en liste blanche sonnent directement, sans questions ;
les numéros en liste noire sont rejetés immédiatement ;
la voix de l’utilisateur n’est jamais exposée à un inconnu — un rempart contre le clonage vocal ;
tout est tracé : historique, transcriptions et notifications, y compris pour les appels écartés ou manqués.
Deux modes d’intégration sont proposés : soit l’utilisateur communique son numéro Iris (son vrai numéro
reste privé), soit il garde son numéro habituel et active un simple renvoi d’appel — les appels filtrés
lui parviennent alors directement dans l’application.
3. Un produit complet
Au-delà du filtrage lui-même, le projet couvre l’ensemble de l’expérience client :
Application mobile
Tableau de bord avec l’état du service et les statistiques, historique des appels avec transcriptions,
gestion des listes blanche et noire (avec import du répertoire du téléphone), écran d’appel dédié
affichant le contexte transmis par Iris, et réglages guidés pour l’activation du service.
Site web
Site vitrine bilingue (FR/EN) présentant le service avec des statistiques sourcées et des schémas animés,
et un espace client pour consulter son historique, gérer son abonnement et son compte.
Abonnement
Souscription en ligne avec période d’essai, portail de gestion (moyen de paiement, factures, résiliation)
et suppression de compte en un clic, conforme au RGPD.
4. Résultat
Le système a été validé de bout en bout en conditions réelles : un appel entrant réel est décroché
par Iris, filtré par la conversation, puis transféré — l’application sonne, le contexte s’affiche,
et la conversation est stable jusqu’au raccrochage.
filtrage complet : listes blanche et noire, dialogue IA, transfert avec contexte ;
historique exhaustif, y compris les appels écartés sans dialogue et les appels manqués notifiés ;
cycle client complet : inscription, essai, abonnement, gestion et suppression de compte.
Ce projet est le plus complet que j’aie mené : il combine téléphonie temps réel, IA conversationnelle,
backend, base de données multi-clients, application mobile, site web, paiements et déploiement
auto-hébergé. La partie téléphonie a demandé trois architectures successives avant d’aboutir à un
transfert d’appel fiable — un vrai travail d’ingénierie détaillé dans la solution technique.
1. Architecture générale
Le système s’articule autour d’un backend central qui orchestre trois surfaces clientes
(application mobile, site web, notifications push) et des services de téléphonie et d’IA.
Architecture globale : le backend orchestre la téléphonie, l’agent vocal IA, la base de données et les surfaces clientes.
Le principe directeur du projet : le backend est le chef d’orchestre. L’IA conversationnelle dialogue
avec l’appelant, mais toutes les décisions d’aiguillage téléphonique (rejet, transfert, destination)
sont exécutées par le backend — jamais déléguées à un service tiers. Cette règle, issue de la leçon
la plus coûteuse du projet (cf. section 4), garantit un comportement fiable et maîtrisé.
L’architecture est multi-clients dès la conception : chaque client dispose de son propre numéro,
et le backend résout le compte concerné à partir du numéro appelé sur chaque webhook entrant.
2. Stack technique
Téléphonie — Twilio Programmable Voice : numéros, webhooks TwiML, pont des appels et SDK VoIP.
Agent vocal — Vapi.ai pour l’orchestration temps réel STT → LLM → TTS, avec GPT-4.1 pour le dialogue, Speechmatics pour la transcription du français et Cartesia pour la voix de synthèse.
Base de données — Supabase (Postgres hébergé en Europe) : authentification et isolation des données par utilisateur (RLS).
Application mobile — Expo / React Native (TypeScript), SDK VoIP Twilio pour recevoir les appels dans l’app, mises à jour OTA via EAS Update.
Site web — Next.js (App Router, SSR), bilingue FR/EN.
Paiements — Stripe : Checkout, Customer Portal et webhooks.
Notifications — push Expo / FCM pour chaque appel filtré.
Hébergement — serveur personnel sous TrueNAS Scale (Docker Compose), exposé via un tunnel HTTPS sortant.
3. Le parcours d’un appel
Entrée et tri immédiat
Chaque appel entrant déclenche un webhook vers le backend, qui identifie le client concerné puis trie :
un numéro en liste noire est rejeté immédiatement ; un numéro en liste blanche est transféré directement,
sans IA ; un numéro inconnu est envoyé vers l’assistante vocale. Dans ce dernier cas, le backend mémorise
l’identifiant du tronçon d’appel parent — la clé de voûte du transfert (cf. section 4).
Le dialogue de filtrage
L’assistante collecte le nom, la société et le motif, puis appelle un outil d’évaluation côté backend.
Trois verdicts sont possibles : appel légitime (transfert annoncé puis exécuté), appel à bloquer
(l’assistante éconduit poliment puis raccroche), ou cas ambigu — l’assistante tranche alors elle-même
selon ses règles, avec une philosophie assumée : en cas de doute, transférer. Mieux vaut un appel
de trop qu’un appel familial rejeté.
Fin d’appel et traçabilité
Si le destinataire ne répond pas, l’appelant reçoit un message poli et l’utilisateur une notification
« appel manqué » — l’appelant ayant déjà donné son nom et son motif, la messagerie devient inutile.
Le rapport de fin d’appel (transcription, durée, raison de fin) complète ensuite l’historique.
Des filets de sécurité en cascade garantissent qu’aucun appel n’est orphelin : chaque appel existe
dans l’historique, même si l’appelant raccroche avant la fin du filtrage.
4. Téléphonie : garder la propriété de l’appel
La partie la plus difficile du projet : il a fallu trois architectures successives pour aboutir
à un transfert d’appel fiable.
Première impasse — déléguer le numéro
La première version confiait le numéro directement à la plateforme d’agent vocal. Simple, mais l’appel
« appartient » alors au service tiers : impossible de le rediriger soi-même, et le protocole de transfert
SIP (REFER) est refusé sur une jambe d’appel classique (PSTN). Toute stratégie de reprise de l’appel
était bloquée par conception.
Deuxième impasse — le transfert « warm » du fournisseur
Le mécanisme de transfert proposé par la plateforme d’agent vocal s’est révélé défaillant : la destination
décrochait en moins d’une seconde, mais la jambe était systématiquement coupée environ trois secondes
plus tard, sans que l’appelant soit mis en relation. Le problème a été prouvé par la lecture des
journaux d’appels de l’opérateur, reproduit sur tous les chemins possibles, et s’est avéré interne
au fournisseur — donc insoluble de notre côté.
L’architecture retenue
Le numéro reste géré par notre téléphonie : c’est notre TwiML qui compose une jambe SIP authentifiée
(authentification digest) vers l’agent vocal. Deux conséquences décisives : la jambe vers l’IA est
bien une jambe SIP, et le tronçon parent reste un appel qui nous appartient — donc librement redirigeable.
Le transfert suit alors une stratégie de reprise d’appel (pattern officiel de « call screening ») :
à l’entrée, le backend mémorise l’identifiant de l’appel parent dans un registre en mémoire à durée de vie courte ;
l’assistante filtre — sa jambe SIP n’est qu’une enfant de notre appel ;
au verdict de transfert, le backend reprend l’appel parent via l’API et lui injecte un nouveau scénario <Dial> vers la destination ;
la jambe IA est raccrochée et l’appelant est ponté avec son destinataire : le pont est 100 % téléphonie, sans dépendance à un mécanisme expérimental tiers.
Le diagnostic de cette partie a demandé une lecture fine des traces SIP (en-têtes Via,
codes d’erreur, challenges d’authentification) — un vrai travail d’investigation réseau, validé
au final par des appels réels de bout en bout.
5. Backend et base de données
Le backend Express expose les webhooks de téléphonie (appel entrant, fin de mise en relation),
les webhooks d’outils appelés par l’assistante (évaluation, mise en relation, rapport de fin d’appel),
la délivrance de jetons VoIP aux utilisateurs authentifiés et une sonde de santé utilisée par
l’application et le site.
Les services internes sont découpés par responsabilité :
classification des appels (mots-clés démarchage/arnaque, avec un classifieur LLM optionnel) ;
registre en mémoire des appels en cours de filtrage — le cœur de la stratégie de reprise ;
notifications structurées et push ;
façade de stockage multi-clients.
Côté robustesse : tous les appels vers les services externes sont protégés avec dégradation
non bloquante, les webhooks valident leurs entrées, et les verdicts disposent de filets en cascade
(verdict immédiat → rapport de fin d’appel → création de ligne orpheline → notification).
La base Postgres (Supabase) stocke les profils, les listes blanche et noire et l’historique des appels
(verdict, source de la décision, transcription, durée). La sécurité d’accès repose sur le Row Level
Security : un utilisateur authentifié ne lit et n’écrit que ses propres lignes ; la clé serveur qui
contourne ces règles n’est jamais exposée côté client ; l’historique est en lecture seule pour
les clients. Le schéma a évolué par migrations SQL versionnées, dont un déclencheur qui crée
automatiquement le profil métier à l’inscription.
6. Application mobile
L’application (Expo / React Native, TypeScript) propose tableau de bord, historique avec transcriptions,
gestion des listes avec import du répertoire (normalisation des numéros au format international),
écran d’appel dédié et réglages — avec thème clair/sombre.
Recevoir un appel dans l’app : la chaîne VoIP
En mode « service complet », le vrai numéro de l’utilisateur est renvoyé vers Iris : un transfert GSM
classique créerait une boucle infinie. Les appels légitimes sont donc livrés directement dans
l’application via le SDK VoIP :
le backend délivre un jeton d’accès temporaire à l’utilisateur authentifié, et l’app s’enregistre automatiquement au lancement — zéro configuration après l’installation ;
app fermée, une notification push silencieuse réveille le module natif qui fait sonner le téléphone plein écran, même verrouillé ;
le contexte de l’appel (nom, société, motif) est transmis avec l’appel et affiché sur l’écran de sonnerie.
Parmi les pièges rencontrés : une permission micro déclarée mais non accordée à l’exécution produit
un appel qui se connecte… en silence total, et une invitation d’appel arrivée avant le démarrage
du JavaScript doit être récupérée explicitement au lancement.
Les mises à jour sont déployées en OTA (EAS Update) : toute évolution JavaScript est poussée en
une minute environ, sans réinstallation ni passage par un store — la recompilation native reste
réservée aux changements de modules natifs.
7. Site web et paiements
Le site Next.js (App Router, rendu serveur) est entièrement bilingue FR/EN. La vitrine présente
le service avec des statistiques réelles sourcées et des schémas SVG animés du parcours d’appel ;
l’espace client (sessions par cookies) donne accès au tableau de bord, à l’historique
et à la gestion du compte.
Le cycle de paiement repose sur Stripe avec quelques choix structurants :
souscription via Checkout avec période d’essai, gestion (factures, résiliation) via le Customer Portal ;
le webhook signé synchronise l’état en temps réel, mais la source de vérité est une réconciliation directe avec Stripe à chaque affichage de la page abonnement — le système est ainsi immunisé contre les webhooks manqués ;
tout l’affichage « service actif » (site et app) est conditionné à l’état réel de l’abonnement ;
l’app ouvre le navigateur déjà connecté grâce à un lien de connexion à usage unique généré par le backend ;
la suppression de compte (RGPD) annule l’abonnement, efface les données en cascade et supprime l’identité d’authentification, en un clic.
8. Déploiement, sécurité et données personnelles
Le backend et le site sont déployés en conteneurs (Docker Compose) sur mon serveur personnel TrueNAS —
le même que celui du projet NAS. L’exposition publique passe par un tunnel HTTPS sortant :
aucun port n’est ouvert sur le réseau domestique, et les certificats sont gérés automatiquement.
conteneurs non-root avec sondes de santé ;
secrets uniquement en variables d’environnement, jamais dans le code ni le dépôt ;
comptes et historique hébergés en Europe ;
rétention courte des transcriptions et aucun audio brut stocké ;
isolation stricte des données par utilisateur (RLS), jetons VoIP nominatifs et liens de connexion à usage unique ;
export et suppression des données intégrés au produit.
L’ensemble fonctionne aujourd’hui en continu et sert de base aux évolutions prévues : enrichissement
de l’écran d’appel (vérification d’entreprise, résumé du motif), transcription du filtrage en direct
dans l’app, et portage iOS.