Recevoir les webhooks
Recevez une requête HTTPS dès qu’un événement se produit : demande d’intervention d’une personne, création d’un ticket ou fin d’indexation. Disponible avec Growth et Pro.
Configurer un point de terminaison
Ajoutez un point de terminaison dans Paramètres → Développeurs → Webhooks, ou via l’API des points de terminaison webhook. Choisissez les événements souhaités et copiez le secret de signature (whsec_…).
Votre point de terminaison doit :
- accepter des requêtes
POSTavec un corps JSON via HTTPS ; - répondre avec un statut
2xxdans un délai de 10 secondes ; - vérifier la signature avant d’exploiter les données reçues.
Effectuez les opérations longues, comme l’envoi d’e-mails ou les appels à d’autres API, après avoir répondu, par exemple à l’aide d’une file d’attente.
Données de l’événement
Tous les événements partagent la même structure. data.object contient l’objet complet tel qu’il était au moment de l’événement, au même format que celui renvoyé par l’API REST.
{
"id": "evt_9fK2qLm0",
"object": "event",
"type": "ticket.created",
"created_at": "2026-10-03T09:15:31Z",
"livemode": true,
"data": {
"object": {
"id": "tkt_7Hc1vQ",
"object": "ticket",
"number": 4821,
"subject": "Tasse ébréchée dans la commande n° 1187",
"description": "L’une des deux tasses est arrivée avec un éclat sur le bord.",
"status": "open",
"priority": "high",
"contact_id": "ctc_4mR8tYw2",
"conversation_id": "conv_8HqL2v9K",
"assignee_id": null,
"tags": [
"damaged",
"shipping"
],
"metadata": {
"order_id": "1187"
},
"created_at": "2026-10-03T09:15:30Z",
"updated_at": "2026-10-03T09:15:30Z",
"solved_at": null
}
}
}Types d’événements
| Événement | data.object | Déclenchement |
|---|---|---|
conversation.created | conversation | Un visiteur a envoyé son premier message. |
conversation.handed_off | conversation | Un visiteur a demandé à parler à une personne, ou vous avez appelé le point de terminaison de transfert. |
conversation.closed | conversation | Une conversation a été fermée. |
message.created | message | Tout nouveau message, quel qu’en soit l’auteur. |
ticket.created | ticket | Un ticket a été créé depuis le widget ou l’API. |
ticket.updated | ticket | Le statut, la priorité, l’attribution ou les étiquettes d’un ticket ont changé. |
ticket.solved | ticket | Un ticket a été marqué comme résolu. |
contact.created | contact | Un nouveau contact a été créé. |
knowledge_source.ready | knowledge_source | L’indexation d’une source de connaissances est terminée. |
knowledge_source.failed | knowledge_source | L’indexation a échoué. Vérifiez l’URL de la source. |
Abonnez-vous à * pour recevoir tous les événements, y compris les types ajoutés à l’avenir. Un événement ping est envoyé lorsque vous utilisez le bouton ou le point de terminaison de test.
Vérifier les signatures
Chaque requête contient un en-tête Yuno-Signature :
Yuno-Signature: t=1791025531,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdt est un horodatage Unix et v1 est le HMAC-SHA256 de la chaîne {t}.{raw request body}, calculé avec votre secret de signature. Pour vérifier :
- Lisez le corps brut sous forme d’octets avant toute analyse JSON.
- Calculez le HMAC de
t + "." + bodyet comparez-le àv1en temps constant. - Rejetez la requête si
tdate de plus de cinq minutes, afin d’éviter les attaques par rejeu.
import crypto from "node:crypto"
import express from "express"
const app = express()
app.post("/hooks/yuno", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("Yuno-Signature") ?? ""
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")))
const expected = crypto
.createHmac("sha256", process.env.YUNO_WEBHOOK_SECRET)
.update(`${t}.${req.body}`)
.digest("hex")
const valid =
v1 && v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300
if (!valid || !fresh) return res.status(400).send("Signature invalide")
const event = JSON.parse(req.body)
res.sendStatus(200)
if (event.type === "conversation.handed_off") {
notifyOnCall(event.data.object)
}
})Nouvelles tentatives
Si votre point de terminaison ne répond pas avec un statut 2xx à temps, nous réessayons en espaçant les tentatives : après 1 minute, 5 minutes, 30 minutes, 2 heures, puis toutes les 6 heures pendant 3 jours au maximum. Vous pouvez consulter chaque tentative et renvoyer des événements dans le tableau de bord.
Un point de terminaison en échec pendant 3 jours est désactivé et vous recevez un e-mail. Réactivez-le dans le tableau de bord ou avec PATCH /webhook-endpoints/{id}.
Bonnes pratiques
- Gérez les doublons. Un même événement peut arriver plusieurs fois. Conservez les valeurs
iddéjà traitées et ignorez les événements correspondants. - Ne vous fiez pas à l’ordre de réception. Un événement
ticket.updatedpeut arriver avantticket.created. Comparezupdated_at, ou récupérez la dernière version depuis l’API. - Vérifiez
livemode. Les événements du mode test ontlivemode: false.