Aller au contenu principal
Leçon 15 sur 16 · Voice Agent API : conversations temps réel

Téléphonie : SIP, LiveKit et G.711

Leçon 15 sur 16 Mis à jour le 3 novembre 2026 Développeur
Sur cette page
  1. Intégrer l’API Voice Agent à la téléphonie
  2. Chemin 1 : le SIP direct chez xAI
  3. Chemin 2 : un pont par votre serveur
  4. Chemin 3 : un framework temps réel
  5. En production
  6. Points clés à retenir

Leçon réécrite le 3 novembre 2026 d’après le guide SIP Phone Calls de xAI, la page Speech to Speech et l’exemple Phone Voice Agent du cookbook : xAI peut répondre lui-même sur votre numéro (SIP direct, webhook signé, transfert, raccrochage, touches DTMF), à côté du pont par votre propre serveur. L’ancienne version proposait un réglage du VAD (threshold, silence_duration_ms, prefix_padding_ms) que xAI ne documente pas, et des chiffres de latence sans source. Sources : SIP Phone Calls · Speech to Speech · Phone Voice Agent.

Intégrer l’API Voice Agent à la téléphonie

L’API Voice Agent sait recevoir des appels téléphoniques : vos clients composent un numéro classique et conversent avec votre agent Grok. Trois chemins y mènent, du plus intégré au plus libre.

Chemin 1 : le SIP direct chez xAI

Dans ce mode, xAI décroche lui-même : l’appel arrive sur l’infrastructure SIP de xAI, qui vous prévient par webhook ; votre application rejoint l’appel par WebSocket et pilote l’agent.

  1. Enregistrer le numéro. Vous déclarez un numéro « Direct SIP » auprès de xAI, avec l’adresse du webhook qui recevra les appels entrants. Pour un numéro qui vous appartient, utilisez origin: "byo_trunk" : xAI ne fournit pas de numéros par l’API. La réponse contient un secret de signature, renvoyé une seule fois : conservez-le dans votre coffre de secrets.
  2. Router l’opérateur. Configurez votre opérateur ou votre PBX pour envoyer les appels vers sip:{numéro}@sip.voice.x.ai. xAI accepte la signalisation SIP en TLS sur le port 5061, en TCP ou UDP sur le port 5060 ; préférez ;transport=tls quand l’opérateur le permet.
  3. Recevoir le webhook. À chaque appel, xAI envoie un webhook signé realtime.call.incoming. Vérifiez les en-têtes webhook-id, webhook-timestamp et webhook-signature avec le secret de signature, puis lisez data.call_id.
  4. Rejoindre l’appel. Ouvrez wss://api.x.ai/v1/realtime?call_id={call_id} avec votre clé d’API — les jetons éphémères ne sont pas acceptés pour une session SIP —, envoyez session.update, puis response.create quand l’agent doit prendre la parole.
import json
import os

import websockets


async def rejoindre_appel(call_id):
    async with websockets.connect(
        f"wss://api.x.ai/v1/realtime?call_id={call_id}",
        additional_headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
    ) as ws:
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "voice": "rex",
                "instructions": "Vous êtes l'agent du service client. Répondez en français, brièvement.",
                "turn_detection": {"type": "server_vad"},
            },
        }))
        await ws.send(json.dumps({"type": "response.create"}))  # l'agent salue l'appelant

        async for message in ws:
            evenement = json.loads(message)
            # même traitement que pour une session WebSocket ordinaire

Une fois connectée, la session se comporte comme n’importe quelle session vocale : xAI relaie lui-même l’audio de l’appelant et lui fait entendre l’agent, sans que votre serveur transporte le son.

Transférer ou raccrocher

Deux appels REST pilotent la ligne. POST /v1/realtime/calls/{call_id}/refer, avec un target_uri en tel: ou en sip:, transfère l’appelant ; la requête attend l’issue du transfert. Un 200 avec un corps vide signifie que la destination a décroché ; un 502 porte le code SIP du refus, un 504 signale un délai dépassé. Après un échec, la session reste ouverte et l’agent peut continuer à parler : c’est à vous de lire la réponse et, au besoin, de lui faire expliquer le contretemps. POST /v1/realtime/calls/{call_id}/hangup met fin à l’appel.

Les touches du clavier (DTMF)

Sur une session SIP, les touches pressées par l’appelant sont mises en mémoire puis transmises au modèle sous forme de texte, dès que l’appelant presse #, après 2,5 secondes sans nouvelle touche, ou dès qu’il se met à parler. Votre client reçoit un événement input_audio_buffer.dtmf_event_received par touche, pour garder une trace. Pour que l’agent compose lui-même des touches, par exemple dans le menu vocal d’un autre service, ajoutez {"type": "function", "name": "send_dtmf"} à ses outils, sans paramètres : l’API fournit le schéma et envoie les tonalités, et vous ne devez pas répondre à ces appels de fonction. Ces événements n’existent que sur une session SIP.

Opérateurs documentés

Le guide de xAI détaille la configuration de quatre opérateurs — Twilio (Elastic SIP Trunking, avec le transfert d’appel à activer sur le trunk si vous transférez), Bandwidth, Telnyx et Plivo — et celle de tout opérateur, centre de contacts ou PBX capable d’ouvrir une route SIP vers l’adresse de votre numéro. Côté codecs, l’exemple Telnyx demande d’activer au moins le G.711 μ-law, le G.711 A-law ou le G.722.

Chemin 2 : un pont par votre serveur

Si vous gardez la main sur le flux audio, votre serveur reçoit l’audio de l’appel et le relaie vers une session WebSocket ordinaire. C’est l’architecture de l’exemple Phone Voice Agent du cookbook de xAI : Twilio diffuse l’audio de l’appel en μ-law par ses Media Streams, et le serveur le transmet tel quel à l’API, sans conversion, dans les deux sens.

# Appel téléphonique → opérateur → votre serveur → WebSocket xAI
import base64
import json
import os

import websockets


async def relayer_appel(flux_audio_appel):
    ws = await websockets.connect(
        "wss://api.x.ai/v1/realtime?model=grok-voice-latest",
        additional_headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
    )

    # G.711 μ-law à 8000 Hz, le format du réseau téléphonique nord-américain
    await ws.send(json.dumps({
        "type": "session.update",
        "session": {
            "voice": "rex",
            "instructions": "Agent du service client.",
            "turn_detection": {"type": "server_vad"},
            "audio": {
                "input": {"format": {"type": "audio/pcmu", "rate": 8000}},
                "output": {"format": {"type": "audio/pcmu", "rate": 8000}},
            },
        },
    }))

    async for morceau in flux_audio_appel:
        await ws.send(json.dumps({
            "type": "input_audio_buffer.append",
            "audio": base64.b64encode(morceau).decode(),
        }))

L’API accepte nativement les deux variantes du G.711, ce qui évite tout transcodage : audio/pcmu (μ-law, la norme nord-américaine et japonaise) et audio/pcma (A-law, la norme européenne et de la plupart des autres pays), toutes deux à 8000 Hz.

{
  "audio": {
    "input": {"format": {"type": "audio/pcma", "rate": 8000}},
    "output": {"format": {"type": "audio/pcma", "rate": 8000}}
  }
}

Les touches DTMF et les appels REST de transfert et de raccrochage appartiennent au chemin SIP direct : dans un pont, c’est votre opérateur qui les gère.

Chemin 3 : un framework temps réel

La documentation de xAI cite deux intégrations prêtes à l’emploi : LiveKit Agents et Pipecat. Elles prennent en charge le transport temps réel — WebRTC, salles, routage des appels — et branchent l’agent vocal de Grok au bon endroit, ce qui épargne la couche téléphonie à une équipe qui ne veut pas la bâtir.

En production

Gérez les silences. Au téléphone, un appelant qui se tait laisse une ligne muette. turn_detection.idle_timeout_ms fait relancer l’appelant par l’agent après un délai de silence choisi (leçon 6).

Reprenez une conversation sur un nouvel appel. La reprise de session fonctionne aussi en SIP : activez resumption.enabled au premier appel et conservez son call_id ; au rappel, ouvrez wss://api.x.ai/v1/realtime?call_id={nouveau_call_id}&conversation_id={ancien_call_id} et renvoyez le même session.update. L’historique expire après 30 minutes d’inactivité.

Respectez les règles. Informez l’appelant qu’il parle à une IA et, si vous enregistrez les appels, dites-le dès le début. Pour les données de santé, xAI indique qu’un BAA (Business Associate Agreement) peut être conclu, sur questionnaire.

Mesurez votre latence. Le délai perçu cumule le réseau téléphonique, votre éventuel pont et la génération de la réponse : mesurez-le de bout en bout sur de vrais appels, et gardez rapides les fonctions que l’agent appelle pendant la conversation.

Points clés à retenir

  • En SIP direct, xAI répond sur votre numéro, vous prévient par un webhook signé realtime.call.incoming, et vous rejoignez l’appel par wss://api.x.ai/v1/realtime?call_id=…
  • refer transfère l’appel (le résultat arrive dans la réponse HTTP), hangup raccroche
  • Les touches DTMF sont transmises au modèle et tracées par input_audio_buffer.dtmf_event_received ; l’outil send_dtmf permet à l’agent d’en composer
  • Dans un pont par votre serveur, le G.711 natif (audio/pcmu, audio/pcma) évite le transcodage
  • LiveKit Agents et Pipecat sont les intégrations citées par xAI