Comment utiliser les webhooks ?

Sojan

Sojan

Dernière mise à jour le Jul 31, 2026

Les webhooks sont des callbacks HTTP configurés pour chaque compte. Ceux-ci sont déclenchés lorsque des actions telles que la création d'un message se produisent dans Chatwoot. Plusieurs webhooks peuvent être créés pour un seul compte.

Comment ajouter un webhook ?

Étape 1. Allez dans Paramètres → Intégrations → Webhooks. Cliquez sur le bouton "Configurer".

Étape 2. Cliquez sur le bouton "Ajouter un nouveau webhook". Une fenêtre modale s'ouvrira. Ici, saisissez l’URL vers laquelle la requête POST doit être envoyée. Ensuite, vous devez sélectionner les événements auxquels vous souhaitez vous abonner. Cette option vous permettra d’écouter uniquement les événements pertinents dans Chatwoot.

Chatwoot enverra une requête POST avec la charge utile suivante vers les URL configurées pour diverses mises à jour de votre compte.

Exemple de payload de webhook

{

  "event": "message_created", // Le nom de l'événement
  "id": "1", // ID du message
  "content": "Hi", // Contenu du message
  "created_at": "2020-03-03 13:05:57 UTC", // Heure à laquelle le message a été envoyé
  "message_type": "incoming", // Ce champ peut valoir incoming, outgoing ou template. L'utilisateur du widget envoie des messages entrants, et l'agent envoie des messages sortants à l'utilisateur.
  "content_type": "enum", // Ceci est un enum, il peut être input_select, cards, form ou text. Le type de message sera template si content_type est l'une de celles-ci. La valeur par défaut est text
  "content_attributes": {} // Ce sera un objet, différentes valeurs sont définies ci-dessous
  "source_id": "", // Ceci serait l’ID externe si la boîte de réception est une intégration Twitter ou Facebook.
  "sender": { // Ceci fournit les détails de l’agent ayant envoyé ce message
    "id": "1",
    "name": "Agent",
    "email": "agent@example.com"
  },
  "contact": { // Ceci fournit les détails de l'utilisateur ayant envoyé ce message
    "id": "1",
    "name": "contact-name"
  },
  "conversation": { // Ceci fournit les détails de la conversation
    "display_id": "1", // Il s'agit de l'ID de la conversation visible dans le tableau de bord.
    "additional_attributes": {
      "browser": {
        "device_name": "Macbook",
        "browser_name": "Chrome",
        "platform_name": "Macintosh",
        "browser_version": "80.0.3987.122",
        "platform_version": "10.15.2"
      },
      "referer": "<http://www.chatwoot.com>",
      "initiated_at": "Tue Mar 03 2020 18:37:38 GMT-0700 (Mountain Standard Time)"
    }
  },
  "account": { // Ceci fournit les détails du compte
    "id": "1",
    "name": "Chatwoot",
  }
}

Événements webhook pris en charge dans Chatwoot

Chatwoot publie divers événements sur les endpoints webhook configurés. Si vous souhaitez configurer un webhook, consultez le guide ici.

Chaque événement a sa structure de payload selon le type de modèle sur lequel il s’applique. La section suivante décrit les principaux objets que nous utilisons dans Chatwoot et leurs attributs.

Objets

Une charge utile d’événement peut inclure l’un des objets suivants. Les différents types d’objets pris en charge par Chatwoot sont listés ci-dessous.

Compte

{
  "id": "integer",
  "name": "string"
}

Boîte de réception

{
"id": "integer",
"name": "string"
}

Contact

{
  "id": "integer",
  "name": "string",
  "avatar": "string",
  "type": "contact",
  "account": {
    // <...Objet Compte>
  }
}

Utilisateur

{
  "id": "integer",
  "name": "string",
  "email": "string",
  "type": "user"
}

Conversation

{
  "additional_attributes": {
    "browser": {
      "device_name": "string",
      "browser_name": "string",
      "platform_name": "string",
      "browser_version": "string",
      "platform_version": "string"
    },
    "referer": "string",
    "initiated_at": {
      "timestamp": "iso-datetime"
    }
  },
  "can_reply": "boolean",
  "channel": "string",
  "id": "integer",
  "inbox_id": "integer",
  "contact_inbox": {
    "id": "integer",
    "contact_id": "integer",
    "inbox_id": "integer",
    "source_id": "string",
    "created_at": "datetime",
    "updated_at": "datetime",
    "hmac_verified": "boolean"
  },
  "messages": ["Array of message objects"],
  "meta": {
    "sender": {
      // Objet Contact
    },
    "assignee": {
      // Objet Utilisateur
    }
  },
  "status": "string",
  "unread_count": "integer",
  "agent_last_seen_at": "unix-timestamp",
  "contact_last_seen_at": "unix-timestamp",
  "timestamp": "unix-timestamp",
  "account_id": "integer"
}

Message

{
  "id": "integer",
  "content": "string",
  "message_type": "integer",
  "created_at": "unix-timestamp",
  "private": "boolean",
  "source_id": "string / null",
  "content_type": "string",
  "content_attributes": "object",
  "sender": {
    "type": "string - contact/user"
    // Objet Utilisateur ou Contact
  },
  "account": {
    // Objet Compte
  },
  "conversation": {
    // Objet Conversation
  },
  "inbox": {
    // Objet Boîte de réception
  }
}

Exemple de payload de webhook

{
  "event": "event_name"
  // Attributs liés à l'événement
}

Événements Webhook

Chatwoot prend en charge les événements webhook suivants. Vous pouvez vous y abonner lors de la configuration d’un webhook dans le tableau de bord ou via l’API.

conversation_created

Cet événement est déclenché lorsqu'une nouvelle conversation est créée dans le compte. Le payload de l'événement est le suivant.

{
  "event": "conversation_created"
  // <...Attributs Conversation>
}

conversation_updated

Cet événement est déclenché lorsqu’une modification est apportée à l’un des attributs de la conversation.

{
  "event": "conversation_updated",
  "changed_attributes": [
    {
      "<attribute_name>": {
        "current_value": "",
        "previous_value": ""
      }
    }
  ]
  // <...Attributs Conversation>
}

conversation_status_changed

Cet événement est déclenché lorsque le statut de la conversation est modifié.

Note : Si vous utilisez les APIs agent bot au lieu des webhooks, cet événement n’est pas encore pris en charge.

{
  "event": "conversation_status_changed"
  // <...Attributs Conversation>
}

message_created

Cet événement est déclenché lorsqu'un message est créé dans une conversation. Le payload de l'événement est le suivant.

{
  "event": "message_created"
  // <...Attributs Message>
}

message_updated

Cet événement est déclenché lorsqu’un message est mis à jour dans une conversation. Le payload de l'événement est le suivant.

{
  "event": "message_updated"
  // <...Attributs Message>
}

webwidget_triggered

Cet événement est déclenché lorsque l’utilisateur final ouvre le widget de chat en direct.

{
  "event": "webwidget_triggered",
  "id": "",
  "contact": {
    // <...Objet Contact>
  },
  "inbox": {
    // <...Objet Boîte de réception>
  },
  "account": {
    // <...Objet Compte>
  },
  "current_conversation": {
    // <...Objet Conversation>
  },
  "source_id": "string",
  "event_info": {
    "initiated_at": {
      "timestamp": "date-string"
    },
    "referer": "string",
    "widget_language": "string",
    "browser_language": "string",
    "browser": {
      "browser_name": "string",
      "browser_version": "string",
      "device_name": "string",
      "platform_name": "string",
      "platform_version": "string"
    }
  }
}

conversation_typing_on

Cet événement est déclenché lorsqu’un agent commence à écrire dans une conversation. Cela peut être soit une note privée, soit un message au client. Vous pouvez utiliser le flag is_private pour distinguer les deux cas.

{
  "event": "conversation_typing_on",
  "conversation": { ...<Objet Conversation> },
  "user": { ... <Objet User / AgentBot / Captain> },
  "is_private": true
}

conversation_typing_off

Cet événement est déclenché lorsqu’un agent arrête d’écrire ou quitte la fenêtre de la conversation.

{
  "event": "conversation_typing_off",
  "conversation": { ...<Objet Conversation> },
  "user": { ... <Objet User / AgentBot / Captain> },
  "is_private": true
}

Vérification des webhooks

Chatwoot signe chaque requête webhook sortante afin que votre serveur puisse vérifier que la charge utile a bien été envoyée par Chatwoot et qu’elle n’a pas été altérée. Le secret vous est montré une fois le webhook créé, et vous pouvez le consulter à nouveau dans le formulaire de modification du webhook.

Chaque requête webhook envoie les en-têtes suivants, qui peuvent être utilisés pour calculer la signature HMAC de la charge utile :

  • X-Chatwoot-Signature: Signature HMAC-SHA256 préfixée par sha256=

  • X-Chatwoot-Timestamp: Timestamp Unix (en secondes) lorsque la requête a été signée

  • X-Chatwoot-Delivery: ID de livraison unique pour l’événement webhook (si disponible)

La signature est calculée comme suit :

sha256=HMAC-SHA256(webhook_secret, "{timestamp}.{raw_body}")

Où :

  • webhook_secret est le secret associé au webhook

  • timestamp est la valeur de l'en-tête X-Chatwoot-Timestamp

  • raw_body est le corps brut de la requête JSON (non parsé/résérialisé)

Étapes de vérification

  1. Extraire X-Chatwoot-Signature et X-Chatwoot-Timestamp des en-têtes de la requête

  2. Lire le corps brut de la requête en octets (ne pas parser ni résérialiser)

  3. Calculer la signature attendue : sha256=HMAC-SHA256(secret, "{timestamp}.{raw_body}")

  4. Comparer la signature calculée avec la signature reçue en utilisant une comparaison en temps constant

  5. Rejeter éventuellement les requêtes dont le timestamp est trop ancien afin de prévenir les attaques par rejeu

Exemples

Ruby

def verify_signature(raw_body, timestamp, received_signature, secret)
  expected = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{raw_body}")}"
  ActiveSupport::SecurityUtils.secure_compare(expected, received_signature)
end

Python

import hmac
import hashlib

def verify_signature(raw_body: bytes, timestamp: str, received_signature: str, secret: str) -> bool:
    message = f"{timestamp}.".encode() + raw_body
    expected = "sha256=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_signature)

Node.js


const crypto = require("crypto");

function verifySignature(rawBody, timestamp, receivedSignature, secret) {
  const message = `${timestamp}.${rawBody}`;
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(message).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(receivedSignature)
  );
}

Go

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func verifySignature(rawBody []byte, timestamp, receivedSignature, secret string) bool {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(fmt.Sprintf("%s.%s", timestamp, rawBody)))
	expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(expected), []byte(receivedSignature))
}

Notes importantes

  • Utilisez toujours le corps brut de la requête pour la vérification. Parser le JSON puis le résérialiser risque de modifier l’ordre des clés, les espaces ou l'encodage unicode, ce qui produira une signature différente.

  • Utilisez toujours une comparaison en temps constant (par exemple hmac.compare_digest, crypto.timingSafeEqual, ActiveSupport::SecurityUtils.secure_compare) pour éviter les attaques temporelles.

  • Envisagez de rejeter les requêtes avec des timestamps plus anciens que 5 minutes afin de limiter les attaques par rejeu.