Trame · Documentation

Webhooks#

Trame peut notifier une URL HTTPS externe à chaque événement métier, avec une requête POST signée en HMAC-SHA256.

Gestion des souscriptions — pas via l'API à clé#

Les souscriptions webhook (créer, lister, supprimer, consulter l'historique de livraison) se gèrent depuis les réglages de votre espace Trame (authentifié par votre compte, réservé à l'administrateur du tenant) — ce n'est pas une route de l'API /v1/... à clé X-Api-Key documentée ailleurs dans cet espace Developers. Une souscription est composée de :

  • une URL cible (https obligatoire — refusée à la création si non-https, trop longue, ou si elle pointe vers une adresse privée/interne/loopback/metadata cloud — protection anti-SSRF) ;
  • une liste d'événements à recevoir (vide = tous les types ci-dessous) ;
  • un secret HMAC, montré une seule fois à la création (comme une clé API).

Plafond : 20 souscriptions par espace.

Signature et vérification#

Chaque livraison porte un en-tête X-EPB-Signature = HMAC-SHA256 du corps JSON brut avec votre secret de souscription. Vérifiez la signature avant de traiter la charge, et rejetez toute requête qui ne correspond pas (protège contre un tiers qui devinerait l'URL de votre endpoint).

Livraison et retries#

Chaque événement est mis en file d'attente durable (pas d'envoi synchrone bloquant) et retenté jusqu'à 4 tentatives en cas d'échec (timeout, erreur réseau, code HTTP non 2xx côté votre serveur). Chaque POST est borné à 8 secondes. L'historique de livraison (succès, tentatives, dernier code HTTP) est consultable depuis les réglages de l'espace.

Les événements canoniques#

Le bus refuse d'émettre tout type hors de cette liste (anti-typo, anti-événement fantôme).

Messagerie conversation.received, conversation.assigned, conversation.labeled, conversation.commented (alias historique : comment.added), message.sent.

Contacts contact.created, contact.updated, contact.deleted, contact.merged.

Agenda calendar_event.created, calendar_event.updated, calendar_event.deleted, calendar_event.rsvp_changed.

Tâches task.created, task.updated, task.completed, task.deleted.

Divers integration.test (émis par GET /v1/webhooks/test).

Helpdesk omnicanal (module distinct, sans scope ni route /v1/... dédiée dans cette documentation aujourd'hui) channel.message.received, channel.message.sent, channel.note.recorded, channel.delivery.updated, ticket.created, ticket.assigned, ticket.fields_changed, ticket.status_changed, ticket.sla_breached, ticket.escalated, knowledge.article.published, csat.response.created.

Statut : les événements « Helpdesk omnicanal » existent bien dans le bus d'événements et peuvent être souscrits, mais le module qui les produit (tickets, canaux, base de connaissance, CSAT) n'a pas de scope ni de route dans les 11 scopes / l'API REST publique documentés dans cet espace — disponibilité pour une intégration externe à confirmer avant de les utiliser en production.

Ce qui est effectivement câblé aujourd'hui#

  • Messagerie/contacts/agenda/tâches : chaque action réelle (assigner, commenter, poser un label, envoyer un message, créer/modifier/supprimer un contact/événement/tâche) déclenche l'émission de son événement — que l'action vienne de l'application, de l'API REST, ou d'un outil MCP.
  • conversation.received : émis par le pipeline d'automatisation entrant sur un nouveau message reçu (sous réserve que l'automatisation entrante soit active pour votre boîte).
  • Endpoint de test : GET /v1/webhooks/test (émet integration.test, utile pour valider une souscription et sa signature de bout en bout).

Un envoi réussi peut ne produire aucun message.sent#

C'est le seul cas où l'événement est volontairement abandonné, et il faut le connaître avant de bâtir une réconciliation dessus.

Sur un envoi via POST /v1/send ou POST /v1/conversations/{id}/send, le message part avant que message.sent ne soit mis en file. Si cette mise en file échoue, l'envoi n'est ni annulé ni transformé en erreur : la réponse reste 200 et porte un drapeau explicite.

json
{ "id": "...", "accepted": true, "dlpStatus": "ok", "webhookDeferred": true }

"webhookDeferred": true signifie donc : le message est bien parti, l'événement message.sent correspondant n'arrivera jamais. Ce choix est délibéré — remonter l'échec en erreur ferait repartir l'intégration avec une nouvelle clé d'idempotence et enverrait le message une seconde fois. Une notification perdue est moins grave qu'un doublon.

Conséquence pratique : si votre intégration s'appuie sur message.sent pour marquer un envoi comme confirmé, traitez webhookDeferred dans la réponse HTTP comme équivalent à la réception de l'événement. Le rejeu idempotent d'un envoi (X-EPB-Idempotency: hit / hit-mem) ne ré-émet pas l'événement non plus : il n'est produit qu'une fois, sur le chemin d'envoi réel.

Les autres actions n'ont pas ce comportement — et trois actions n'émettent aucun événement : archiver, remettre en boîte de réception et reporter une conversation.