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.
id
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.
type
string
Type d’événement (ex. transport.created). Voir le catalogue ci-dessous.
created_at
string
Horodatage de création de l’événement, au format ISO 8601 (date-time).
data
object
Charge utile publique propre au type d’événement. Ne contient jamais de secret ni de champ interne (rémunération convoyeur brute, pénalités, etc.).
Enveloppe (exemple transport.created)
{
  "id": "8f2c1a9e-0b6d-4f2a-9c1e-2a3b4c5d6e7f",
  "type": "transport.created",
  "created_at": "2026-06-16T10:32:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "transport_id": "M-54321",
    "status": "scheduled",
    "price": { "amount_ht": 180.5, "currency": "EUR" }
  }
}

En-têtes de livraison

Chaque POST sortant vers votre endpoint porte quatre en-têtes X-MED-*.
X-MED-Event-Type
string
Type de l’événement transporté (ex. transport.created). Pratique pour router sans désérialiser le corps.
X-MED-Delivery-Id
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.
X-MED-Timestamp
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.
X-MED-Signature
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

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.
ÉvénementDéclencheurdata (résumé)
transport.createdCréation d’une mission (POST /transports){ id, transport_id, status, price }
transport.status_changedChangement de statut public{ id, from, to }
transport.assignedConvoyeur attribué{ id, driver }
transport.collectedVéhicule enlevé (passage in_transit){ id, collected_at }
transport.completedMission terminée{ id, completed_at }
transport.cancelledAnnulation{ id, reason }
transport.incidentAnomalie signalée{ id }
transport.dates_updatedModification de dates{ id, updated_legs }
transport.price_adjustedRefacturation{ id, leg, old_price_ht, new_price_ht, motif }
transport.price_adjustedAjout d’option{ id, option, new_price }
transport.price_adjustedRetrait d’option{ id, removed_option, new_price }
document.addedDocument ajouté{ id, document_key, url }
document.acceptedDocument accepté{ id, document_key, status }
document.rejectedDocument refusé{ id, document_key, status }
webhook.testTest depuis l’UI « API & Intégrations »{ … }

Événements de cycle de vie

Ces événements suivent la progression d’une mission. La transition typique est :
scheduled → assigned → in_transit [→ in_transit_return] → awaiting_documents → under_review → completed
incident et cancelled peuvent survenir à tout moment.
Émis lorsqu’une mission est créée avec succès via POST /transports. La mission est alors au statut public scheduled.
{
  "id": "8f2c1a9e-0b6d-4f2a-9c1e-2a3b4c5d6e7f",
  "type": "transport.created",
  "created_at": "2026-06-16T10:32:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "transport_id": "M-54321",
    "status": "scheduled",
    "price": { "amount_ht": 180.5, "currency": "EUR" }
  }
}
Émis à chaque transition de statut public. Les champs from et to utilisent le vocabulaire public stable (jamais les statuts internes).
{
  "id": "c3a7d210-4e9f-4b6a-9f1d-77e0c2b8a1f4",
  "type": "transport.status_changed",
  "created_at": "2026-06-16T11:05:12Z",
  "data": {
    "id": "-O9xAbCdEf",
    "from": "assigned",
    "to": "in_transit"
  }
}
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.
{
  "id": "1b9e44c8-2f0a-4d3e-8c6b-9a0d1e2f3a4b",
  "type": "transport.assigned",
  "created_at": "2026-06-16T11:04:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "driver": {
      "name": "Lucas D."
    }
  }
}
Émis lorsque le véhicule est enlevé (passage au statut public in_transit).
{
  "id": "5d2c8e91-7a3b-4c1d-9e0f-6b5a4c3d2e1f",
  "type": "transport.collected",
  "created_at": "2026-06-16T12:18:45Z",
  "data": {
    "id": "-O9xAbCdEf",
    "collected_at": "2026-06-16T12:18:30Z"
  }
}
Émis lorsque la mission est terminée (statut public completed).
{
  "id": "9f0a1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c",
  "type": "transport.completed",
  "created_at": "2026-06-16T18:42:10Z",
  "data": {
    "id": "-O9xAbCdEf",
    "completed_at": "2026-06-16T18:41:55Z"
  }
}
Émis lorsque la mission est annulée (via POST /transports/{id}/cancel ou côté plateforme). Le motif est inclus s’il a été fourni.
{
  "id": "2e4f6a8c-0b1d-4e3f-8a5c-7b9d0e1f2a3c",
  "type": "transport.cancelled",
  "created_at": "2026-06-16T09:15:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "reason": "Demande client"
  }
}
Émis lorsqu’une anomalie est signalée sur la mission (statut public incident).
{
  "id": "7c1d3e5f-9a2b-4c6d-8e0f-1a3b5c7d9e0f",
  "type": "transport.incident",
  "created_at": "2026-06-16T14:27:33Z",
  "data": {
    "id": "-O9xAbCdEf"
  }
}

É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.
{
  "id": "4a6c8e0f-2b1d-4e3f-9a5c-6b8d0e1f3a5c",
  "type": "transport.dates_updated",
  "created_at": "2026-06-17T08:00:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "updated_legs": ["enlevement-aller"]
  }
}
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 :
{
  "id": "6b8d0f2a-4c3e-4f5a-8b7d-9e1f3a5c7b9d",
  "type": "transport.price_adjusted",
  "created_at": "2026-06-17T09:30:12Z",
  "data": {
    "id": "-O9xAbCdEf",
    "leg": "aller",
    "old_price_ht": 180.5,
    "new_price_ht": 210.0,
    "motif": "Distance revue à la hausse"
  }
}
Ajout d’une option :
{
  "id": "7c9e1a3b-5d4f-4a6c-9c8e-0f2a4b6c8d0e",
  "type": "transport.price_adjusted",
  "created_at": "2026-06-17T09:31:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "option": "nettoyage_interieur",
    "new_price": 205.5
  }
}
Retrait d’une option :
{
  "id": "8d0f2b4c-6e5a-4b7d-8d9f-1a3b5c7d9e0f",
  "type": "transport.price_adjusted",
  "created_at": "2026-06-17T09:32:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "removed_option": "nettoyage_interieur",
    "new_price": 180.5
  }
}

É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).
{
  "id": "0c2e4a6c-8b0d-4e1f-9a3c-5b7d9e0f1a3c",
  "type": "document.added",
  "created_at": "2026-06-16T16:20:00Z",
  "data": {
    "id": "-O9xAbCdEf",
    "document_key": "BON_CONVOYAGE_1",
    "url": "https://files.myexpressdriver.com/docs/bon-convoyage-1.pdf"
  }
}
Émis lorsqu’un document passe au statut accepted.
{
  "id": "3d5f7a9c-1b2e-4d6f-8a0c-2b4d6e8f0a2c",
  "type": "document.accepted",
  "created_at": "2026-06-16T17:05:40Z",
  "data": {
    "id": "-O9xAbCdEf",
    "document_key": "BON_CONVOYAGE_1",
    "status": "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.
{
  "id": "5f7a9c1e-3d4f-4a6c-8b0d-1e3f5a7c9b0d",
  "type": "document.rejected",
  "created_at": "2026-06-16T17:12:08Z",
  "data": {
    "id": "-O9xAbCdEf",
    "document_key": "BON_CONVOYAGE_1",
    "status": "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.
{
  "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "type": "webhook.test",
  "created_at": "2026-06-17T10:00:00Z",
  "data": {
    "message": "Ceci est un événement de test."
  }
}

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.