Les webhooks vous notifient en temps réel de chaque changement sur vos missions : création, attribution d’un convoyeur, enlèvement, documents, refacturation, annulation… Plutôt que d’interroger l’API en boucle, vous recevez un POST HTTPS signé dès qu’un événement se produit. Cette page liste tous les types d’événements émis par l’API, leur déclencheur et un exemple complet de payload. Pour configurer un endpoint et valider l’authenticité d’une livraison, voir la page Vérification de signature.
Tous les événements partagent la même enveloppe JSON : { id, type, created_at, data }. Seul le contenu de data change selon le type. Branchez votre routage sur le champ type (ou l’en-tête X-MED-Event-Type), jamais sur la structure de data.

L’enveloppe d’événement

Chaque livraison transporte une enveloppe identique. C’est le contrat stable sur lequel construire votre traitement.
string
Identifiant unique de l’événement (UUID). Stable, mais pas une clé d’idempotence de livraison — pour la déduplication, utilisez l’en-tête X-MED-Delivery-Id.
string
Type d’événement (ex. transport.created). Voir le catalogue ci-dessous.
string
Horodatage de création de l’événement, au format ISO 8601 (date-time).
object
Charge utile publique propre au type d’événement. Ne contient jamais de secret ni de champ interne (rémunération convoyeur, pénalités convoyeur, etc.).
Enveloppe (exemple transport.created)

En-têtes de livraison

Chaque POST sortant vers votre endpoint porte quatre en-têtes X-MED-*.
string
Type de l’événement transporté (ex. transport.created). Pratique pour router sans désérialiser le corps.
string
Identifiant stable de la livraison, conservé entre les tentatives de réessai. C’est votre clé d’idempotence côté consommateur : dédupliquez dessus.
string
Epoch en secondes au moment de l’envoi. Sert à l’anti-rejeu : rejetez une livraison dont l’horodatage s’écarte de plus de 5 minutes de l’heure courante.
string
Signature HMAC-SHA256 au format sha256=<hex>, calculée sur "{X-MED-Timestamp}.{rawBody}" avec votre signing secret. Voir Vérification de signature.
La livraison est at-least-once : un même événement peut arriver plusieurs fois. Dédupliquez systématiquement sur X-MED-Delivery-Id et répondez 2xx rapidement pour acquitter.

Vue d’ensemble du catalogue

Cycle de vie de la mission

transport.created, transport.status_changed, transport.assigned, transport.collected, transport.completed, transport.cancelled, transport.incident, transport.duplicated

Modifications

transport.dates_updated, transport.price_adjusted

Documents / PV

document.added, document.accepted, document.rejected

Diagnostic

webhook.test
Un endpoint peut s’abonner à tous les événements (*) ou à une liste explicite de types. Le tableau ci-dessous résume chaque type ; les sections suivantes détaillent le payload.
Les événements liés au PV / inspection d’une mission (inspection.report_ready, inspection.completed, phases…) sont décrits sur leur propre page : Webhooks — Inspection / PV.

Événements de cycle de vie

Ces événements suivent la progression d’une mission. La transition typique est :
incident peut survenir à tout moment ; cancelled, tant que le véhicule n’est pas parti (scheduled, ou assigned une fois l’attribution du convoyeur confirmée).
Émis lorsqu’une mission est créée avec succès via POST /transports. La mission est alors au statut public scheduled.
  • price est le prix HT de l’aller, en chaîne décimale (ex. "407.44") — convertissez-le en nombre ;
  • transport_id vaut null : la référence métier (M-xxxxx) est attribuée juste après la création. Lisez-la via GET /transports/{id}.
Une création rejouée (même Idempotency-Key après un timeout) n’émet pas de second transport.created pour la même mission.Quand la mission est née d’une duplication, data.duplicated_from référence la source : { "id": "-O9xSourceKey", "transport_id": "M-54300" }. Dans ce cas, price est un objet { "amount_ht": 412 } (prix HT de l’aller, en nombre) et non une chaîne : testez le type de price avant de le lire.
Émis sur la mission source lorsqu’elle sert de base à POST /transports/{id}/duplicate. Une source ne se duplique qu’une fois : cet événement n’est donc émis qu’une seule fois par mission. La mission créée émet, elle, un transport.created classique portant duplicated_from.
Émis à chaque transition de statut public. Les champs from et to utilisent le vocabulaire public stable (jamais les statuts internes).
Valeurs possibles : scheduled, assigned, in_transit, in_transit_return, awaiting_documents, under_review, completed, incident, cancelled.
Émis lorsqu’un convoyeur est attribué à la mission. L’identité du convoyeur est partielle (prénom/nom, téléphone et email masqués) conformément au RGPD.
Émis lorsque le véhicule est enlevé (passage au statut public in_transit).
Émis lorsque la mission est terminée (statut public completed).
Émis une seule fois, peu après l’annulation (de façon asynchrone), quelle qu’en soit l’origine : POST /transports/{id}/cancel, votre espace client ou les équipes MED. La mission reste lisible au statut public cancelled ; un transport.status_changed vers cancelled l’accompagne, et une annulation n’émet jamais transport.completed.La charge se limite à { id, transport_id, cancelled_at }. Elle ne contient ni le motif ni les frais d’annulation. Les frais figurent dans la réponse 200 de POST /transports/{id}/cancel (penalty, return_included) — conservez-la ; l’état de la mission se lit via GET /transports/{id}. Le motif n’est renvoyé nulle part : gardez-le de votre côté.
Émis lorsqu’une anomalie est signalée sur la mission (statut public incident).

Événements de modification

Émis lorsqu’une ou plusieurs jambes voient leurs dates modifiées (via PATCH /transports/{id}/dates). Le champ updated_legs liste uniquement les jambes réellement modifiées.
Valeurs possibles de updated_legs : enlevement-aller, livraison-aller, enlevement-retour, livraison-retour.
Émis après une refacturation (POST /transports/{id}/price-adjustments) ou un ajout/retrait d’option (POST/DELETE /transports/{id}/options/...). Le contenu de data dépend de l’origine de l’ajustement — seul le prix HT facturé au client est exposé, jamais la rémunération convoyeur.Refacturation d’une jambe :
Ajout d’une option :
Retrait d’une option :

Événements de documents

Ces événements suivent le cycle de vie des documents et PV de la mission. Le champ document_key identifie le document concerné.
Émis lorsqu’un document est ajouté à la mission (via POST /transports/{id}/documents ou en interne).
Émis lorsqu’un document passe au statut accepted.
Émis lorsqu’un document est refusé. Le commentaire interne de rejet n’est jamais exposé : le client voit seulement que le document est passé au statut refused.
Le statut d’un document peut valoir waiting, accepted ou refused.

Événement de diagnostic

Émis quand vous cliquez sur « Tester » dans l’onglet « API & Intégrations » de votre profil client. Utilisez-le pour valider que votre endpoint reçoit, vérifie la signature et répond 2xx correctement. Le contenu de data est libre et ne reflète aucune mission réelle.

Souscription et réconciliation

1

Choisir les types à recevoir

À la configuration d’un endpoint, abonnez-vous à * (tous les événements) ou à une liste explicite de types (ex. transport.created, transport.completed).
2

Vérifier chaque livraison

Validez la signature X-MED-Signature et l’horodatage X-MED-Timestamp avant de traiter le corps. Voir Vérification de signature.
3

Dédupliquer et acquitter

Dédupliquez sur X-MED-Delivery-Id, puis répondez 2xx rapidement. Effectuez le traitement lourd en asynchrone.
4

Combler les trous

En cas d’endpoint indisponible, rejouez les événements manqués via GET /events?since=<ISO8601> et inspectez l’état des livraisons avec GET /webhooks/deliveries?status=failed.
Conservez le dernier created_at traité et appelez périodiquement GET /events?since=<dernier> pour garantir qu’aucun événement n’est perdu, même si un webhook a échoué.

Étapes suivantes

Vérification de signature

Recalculez le HMAC-SHA256, comparez à temps constant et bloquez les rejeux.

Réconciliation des événements

Rattrapez les événements manqués via le journal GET /events.