Webhooks

Les webhooks informent votre backend quand un appel démarre, se termine ou échoue, ce qui vous évite d'effectuer du polling. Utilisez-les pour mettre à jour un enregistrement CRM, stocker les transcriptions dans votre data warehouse, déclencher des alertes en cas d'échec ou suivre la durée des appels pour la facturation. Les webhooks constituent la trace côté serveur de chaque appel. Pour réagir pendant que la conversation est en cours, utilisez plutôt les événements de session.

Fonctionnement de la livraison

Fonctionnement de la livraison

Vous enregistrez l'URL d'un endpoint, et Mistral lui envoie un payload JSON signé chaque fois qu'un événement se produit. Chaque livraison est une requête HTTP POST avec content-type: application/json, et la même enveloppe :

{
  "event": "<event_type>",
  "created_at": "2026-09-08T14:32:10Z",
  "data": { ... }
}
ChampTypeDescription
eventstringLe type d'événement, voir Types d'événements et payloads.
created_atstringQuand l'événement a été émis, sous forme d'horodatage ISO 8601 UTC.
dataobjectLe payload de l'événement.
Configurer un endpoint et un abonnement

Configurer un endpoint et un abonnement

Vous configurez les webhooks par agent, dans la console Mistral ou via l'API :

  1. Créez un endpoint : indiquez l'URL qui reçoit les événements et un nom. La plateforme génère un secret de signature.
  2. Créez un abonnement : associez l'endpoint à un agent et choisissez les types d'événements à recevoir.

Un endpoint peut servir plusieurs agents, et un agent peut avoir plusieurs abonnements.

Avertissement

Conservez le secret de signature en lieu sûr : vous en avez besoin pour vérifier les signatures. Il est généré une seule fois et ne peut pas être renouvelé. S'il fuit, supprimez l'endpoint, créez-en un nouveau et mettez à jour votre code de vérification.

Vérifier les signatures et gérer les livraisons

Vérifier les signatures et gérer les livraisons

Chaque livraison comporte un en-tête x-mistral-signature : un condensat HMAC-SHA256 du corps brut de la requête, calculé avec le secret de votre endpoint.

x-mistral-signature: hmac-sha256=<hex_digest>

Pour vérifier une livraison :

  1. Lisez le corps brut de la requête, avant toute analyse du JSON.
  2. Calculez HMAC-SHA256(secret, raw_body).
  3. Comparez votre condensat avec la valeur qui suit hmac-sha256=, à l'aide d'une comparaison en temps constant.
import hashlib
import hmac

def verify_signature(raw_body: bytes, secret: str, header: str) -> bool:
    expected = hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    received = header.removeprefix("hmac-sha256=")
    return hmac.compare_digest(expected, received)
Avertissement

Vérifiez toujours les octets bruts du corps de la requête, et non une chaîne JSON que vous avez resérialisée : cela peut modifier les espaces ou l'ordre des clés, et le condensat ne correspond plus.

Un récepteur fiable vérifie la signature, renvoie immédiatement un code 200, puis traite l'événement. Ce serveur minimal répartit les traitements selon le type d'événement :

import hashlib
import hmac
import os

from fastapi import FastAPI, Header, HTTPException, Request

app = FastAPI()
SECRET = os.environ["WEBHOOK_SECRET"]

def verify_signature(raw_body: bytes, header: str) -> None:
    expected = hmac.new(
        SECRET.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    received = header.removeprefix("hmac-sha256=")
    if not hmac.compare_digest(expected, received):
        raise HTTPException(status_code=401, detail="Invalid signature")

@app.post("/webhooks")
async def webhook_handler(
    request: Request,
    x_mistral_signature: str = Header(...),
):
    raw_body = await request.body()
    verify_signature(raw_body, x_mistral_signature)

    event = await request.json()

    match event["event"]:
        case "call_started":
            print(f"Call started: {event['data']['session_id']}")
        case "call_ended":
            print(f"Call ended: {event['data']['session_id']}")
        case "call_failed":
            print(f"Call failed: {event['data']['session_id']}")

    return {"status": "ok"}

Comme les livraisons peuvent être retentées, un même événement peut arriver plus d'une fois : utilisez session_id et event ensemble pour ignorer les doublons.

Types d'événements et payloads

Types d'événements et payloads

ÉvénementEnvoyé quand
call_startedUne session est créée et le pipeline vocal est prêt.
call_endedUne session se termine normalement.
call_failedUne session ne démarre pas ou rencontre une erreur fatale pendant l'appel.
call_started

call_started

{
  "event": "call_started",
  "created_at": "2026-09-08T14:32:10Z",
  "data": {
    "session_id": "RM_a1b2c3d4e5f6",
    "agent_id": "ag_01234567-89ab-cdef-0123-456789abcdef",
    "model": "mistral-small-latest",
    "started_at": 1725802330
  }
}
ChampTypeDescription
session_idstringIdentifiant unique de la session.
agent_idstringAgent qui a géré l'appel.
modelstringModèle utilisé pour les réponses.
started_atintegerQuand la session a été créée, sous forme d'horodatage Unix en secondes.
call_ended

call_ended

Envoyé quand une session se termine normalement. Il ajoute la durée et, si disponible, la transcription complète.

{
  "event": "call_ended",
  "created_at": "2026-09-08T14:35:42Z",
  "data": {
    "session_id": "RM_a1b2c3d4e5f6",
    "agent_id": "ag_01234567-89ab-cdef-0123-456789abcdef",
    "model": "mistral-small-latest",
    "duration_seconds": 212,
    "started_at": 1725802330,
    "ended_at": 1725802542,
    "transcript": [
      { "role": "assistant", "content": "Hello! How can I help you today?" },
      { "role": "user", "content": "I'd like to check my account balance." },
      { "role": "assistant", "content": "Sure, let me look that up for you." }
    ]
  }
}
ChampTypeDescription
session_idstringIdentifiant unique de la session.
agent_idstringAgent qui a géré l'appel.
modelstringModèle utilisé pour les réponses.
duration_secondsintegerDurée de l'appel, en secondes.
started_atintegerQuand la session a démarré, sous forme d'horodatage Unix en secondes.
ended_atintegerQuand la session s'est terminée, sous forme d'horodatage Unix en secondes.
transcriptarray ou nullLes tours de parole de la conversation, dans l'ordre. Chaque tour comporte un role (user ou assistant) et un content (texte, ou null si vide).
Note

La transcription est fournie au mieux : si elle n'est pas prête à temps pour la livraison, transcript vaut null. Le reste du payload est complet dans tous les cas.

call_failed

call_failed

Envoyé quand une session ne démarre pas, par exemple à cause d'une erreur d'infrastructure, ou quand elle rencontre une erreur fatale pendant l'appel.

{
  "event": "call_failed",
  "created_at": "2026-09-08T14:32:11Z",
  "data": {
    "session_id": "RM_a1b2c3d4e5f6",
    "agent_id": "ag_01234567-89ab-cdef-0123-456789abcdef",
    "model": "mistral-small-latest",
    "error": "livekit_error"
  }
}
ChampTypeDescription
session_idstringIdentifiant unique de la session.
agent_idstringAgent qui a géré l'appel.
modelstringModèle utilisé pour les réponses.
errorstringRaison lisible par une machine, par exemple token_error, db_error, livekit_error, ou un motif de déconnexion pour les échecs pendant l'appel.
Livraison et nouvelles tentatives

Livraison et nouvelles tentatives

ComportementDétail
TimeoutVotre endpoint doit répondre en moins de 10 secondes.
SuccèsTout code de statut 2xx est considéré comme une livraison réussie.
Nouvelles tentativesEn cas de réponses 5xx ou 429, Mistral retente jusqu'à 3 fois au total, avec un backoff exponentiel (2 s, puis 4 s).
Pas de nouvelle tentativeRéponses 4xx autres que 429. Vérifiez la logique de votre endpoint.
Erreurs de transportLes échecs réseau (DNS, TLS, réinitialisation de la connexion) sont retentés selon le même planning.

Suivez ces bonnes pratiques :

  • Renvoyez immédiatement un code 200. Accusez réception de la livraison, puis traitez-la de façon asynchrone. Un gestionnaire lent risque des timeouts et des livraisons en double.
  • Rendez votre gestionnaire idempotent. Les nouvelles tentatives peuvent livrer deux fois le même événement : dédupliquez avec session_id et event.
  • Utilisez HTTPS. L'API accepte les URL en HTTP simple, mais HTTPS protège le payload et votre secret en transit.
  • Protégez le secret. Il ne peut pas être renouvelé : s'il fuit, remplacez l'endpoint.
Tester en local

Tester en local

Pour recevoir des webhooks sur votre machine pendant le développement, exposez votre serveur local avec un outil de tunnel HTTP, et enregistrez l'URL qu'il vous fournit en tant qu'endpoint.

Aller plus loin

Aller plus loin