Webhooks envoi / tournée
Pour informer proactivement les systèmes clients des modifications apportées aux ordres de transport/envois et aux tournées, des webhooks peuvent être configurés dans X4Fleet pour ces deux entités.
Pour chaque entité, il existe une liste d'événements pouvant déclencher un webhook.
Les webhooks peuvent être configurés par les utilisateurs administrateurs dans X4Fleet sous Paramètres > Paramètres > "Notifications de statut (Webhooks)".

Pour l'authentification de la requête POST, une paire nom d'utilisateur/mot de passe ou un certificat x509 peut être stocké.
Si une autre méthode est requise ou si X4Fleet ne doit pas envoyer lui-même des requêtes au système client, celles-ci peuvent également être récupérées proactivement (p.ex. toutes les 5 minutes) par le système client. (Activer la case à cocher "Utiliser uniquement le stockage webhook" pour cela.)
Même si le payload est très complet, il est recommandé de considérer le webhook uniquement comme déclencheur pour "Hey, quelque chose s'est passé pour l'envoi X. Va vérifier !" — ce qui signifie qu'un GET Shipment ou GET Tour est ensuite déclenché par le système client pour obtenir plus de détails. En effet, le payload webhook doit devenir plus léger à long terme et les nouvelles structures de données ne seront intégrées que dans les endpoints correspondants.
Endpoints :
GET /x4gateway/v1/export/webhookShipmentMessages
GET /x4gateway/v1/export/webhookTourMessages
Déclencheurs envoi / ordre de transport
- En cours - L'envoi a été créé mais est encore en cours de traitement ou des informations manquent
- Libéré - L'envoi a été manuellement ou via un paramètre libéré pour planification sur une tournée
- Planifié - L'envoi a été assigné à une tournée
- Enlèvement terminé
- Livraison terminée
- Fractionnement quantité - L'envoi a été divisé en N envois supplémentaires car la quantité ne peut pas être traitée en un seul voyage
- Fractionnement itinéraire - L'envoi a été divisé en N envois supplémentaires car les marchandises transitent par N dépôts avant d'atteindre leur destination
- Terminé
- Terminé avec réserve - Des écarts existent entre l'exécution et la planification (p.ex. moins a été chargé que prévu)
- Réouvert - Quelque chose sur l'envoi doit être corrigé
Déclencheurs tournée
- En planification - Un premier envoi a été attribué à la tournée (généralement pas pertinent pour le système client)
- Planifiée - La tournée a été calculée ou optimisée (généralement pas pertinent pour le système client)
- Libérée - La tournée a été libérée pour l'exécution (selon le processus, plusieurs jours peuvent s'écouler entre la libération et l'exécution)
- Re-libérée - La tournée a été rappelée, éventuellement modifiée, et re-libérée pour exécution
- En révision - La tournée était libérée mais a ensuite été rappelée pour correction
- Révisée - Après "En révision" lorsque recalculée/optimisée
- En exécution - Le chauffeur a démarré la tournée libérée
- Complètement terminée - Tous les points de transport et envois complétés selon la disposition
- Incomplètement terminée - Écarts entre l'exécution terminée et la planification du dispatcher
- Non commencée
- En pause - p.ex. pendant la pause du chauffeur ou lorsque le véhicule a été préchargé la veille et la tournée attend la poursuite de l'exécution
- * (A) - Ce suffixe signifie que la tournée était en statut "En exécution" avant de passer au nouveau statut
- * (P) - Ce suffixe signifie que la tournée était en statut "En pause" avant de passer au nouveau statut
Un webhook de tournée n'est envoyé que si le statut de la tournée change effectivement. Si une action est répétée dans le même statut (p.ex. d'autres envois sont attribués à une tournée déjà "En planification"), aucun autre webhook de tournée n'est envoyé. Exception : si le module "Examen de l’envoi" est actif, les webhooks de tournée ne sont envoyés, dès le début de l'exécution (à partir du statut "En exécution"), qu'une fois tous les envois de la tournée examinés ; le webhook de tournée est alors envoyé avec le statut actuel de la tournée, même sans changement de statut, pour autant que le webhook soit configuré pour ce statut. Les changements de statut intermédiaires ne sont pas envoyés a posteriori. Cette restriction ne s'applique pas aux webhooks d'envoi.
Schéma JSON pour les webhooks reçus
{
"title": "WebhookShipmentDto",
"type": "object",
"properties": {
"id": { "type": "integer" },
"groupId": { "type": "integer" },
"refId": { "type": ["string", "null"] },
"source": { "type": ["string", "null"] },
"salesmanCode": { "type": ["string", "null"] },
"orderNumber": { "type": ["string", "null"] },
"note": { "type": ["string", "null"] },
"timeStamp": { "type": "string", "format": "date-time" },
"status": { "type": ["string", "null"] },
"fiscalStatus": { "type": ["string", "null"] },
"shipmentType": { "type": ["string", "null"] },
"customField1": { "type": ["string", "null"] },
"customField2": { "type": ["string", "null"] },
"customField3": { "type": ["string", "null"] },
"customField4": { "type": ["string", "null"] },
"customField5": { "type": ["string", "null"] },
"customField6": { "type": ["string", "null"] },
"customField7": { "type": ["string", "null"] },
"customField8": { "type": ["string", "null"] },
"customField9": { "type": ["string", "null"] },
"customField10": { "type": ["string", "null"] },
"precombinedTourNumber": { "type": ["string", "null"] },
"extSortOrderA": { "type": ["integer", "null"] },
"extSortOrderB": { "type": ["integer", "null"] },
"calculatedSortOrderA": { "type": ["integer", "null"] },
"calculatedSortOrderB": { "type": ["integer", "null"] },
"transportPoints": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookTransportPointDto" }
},
"shipmentReports": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookShipmentReportDto" }
},
"parentShipmentReports": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookShipmentReportDto" },
"description": "Reports stored on the split parent shipment (e.g. the parent delivery note); empty for shipments without a split parent"
},
"splitParentId": { "type": ["integer", "null"] },
"parentShipmentType": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/ShipmentTypeEnum" }
]
},
"splits": { "type": ["integer", "null"] },
"splitSortOrder": { "type": ["integer", "null"] },
"pickupLocationRefId": { "type": ["string", "null"] },
"dropoffLocationRefId": { "type": ["string", "null"] },
"isReadyForCommissioning": { "type": "boolean" }
},
"$defs": {
"WebhookTransportPointDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"dropoffStop": { "type": "boolean" },
"sortOrder": { "type": "integer" },
"precombinedTourNumber": { "type": ["string", "null"] },
"scheduledArrival": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"scheduledDeparture": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"startActual": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"endActual": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"images": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookTransportPointImageDto" }
},
"status": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/StopStatusEnum" }
]
},
"driverTasks": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookDriverTaskDto" }
}
}
},
"WebhookTransportPointImageDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"transportPointId": { "type": "integer" },
"url": { "type": ["string", "null"] },
"lat": { "type": ["number", "null"] },
"lng": { "type": ["number", "null"] }
}
},
"WebhookShipmentReportDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"reportId": { "type": ["integer", "null"] },
"reportUrl": { "type": ["string", "null"] },
"timeStamp": { "type": ["string", "null"], "format": "date-time" },
"reportType": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/ReportTypeEnum" }
]
}
}
},
"WebhookDriverTaskDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"type": { "$ref": "#/$defs/DriverTaskType" },
"status": { "$ref": "#/$defs/DriverTaskStatusEnum" },
"barCode": { "type": ["string", "null"] },
"scannerLat": { "type": ["number", "null"] },
"scannerLng": { "type": ["number", "null"] },
"quantityRequired": { "type": ["number", "null"] },
"quantityActual": { "type": ["number", "null"] },
"quantityEdited": { "type": ["number", "null"] }
}
},
"ShipmentTypeEnum": {
"type": "integer",
"enum": [0, 1, 2, 3, 4],
"description": "0 = InitialLeg, 1 = MainLeg, 2 = FinalLeg, 3 = Direct, 4 = Undefined"
},
"StopStatusEnum": {
"type": "integer",
"enum": [0, 1, 2, 3, 9, 10, 11],
"description": "0 = NotStarted, 1 = Success, 2 = Fail, 3 = Warning, 9 = InProgress, 10 = WaitingAfterArrival, 11 = WaitingBeforeDeparture"
},
"DriverTaskType": {
"type": "integer",
"enum": [1, 2, 3],
"description": "1 = Pickup, 2 = Dropoff, 3 = Flushing"
},
"DriverTaskStatusEnum": {
"type": "integer",
"enum": [0, 1, 2, 3],
"description": "0 = NotStarted, 1 = Completed, 2 = Failed, 3 = InProgress"
},
"ReportTypeEnum": {
"type": "integer",
"enum": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,21,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],
"description": "0=EmptyReport, 1=TourListReport, 2=DeliveryNoteReport, 3=SpecialEventReport, 4=SimulationManipulationReport, 5=ShipmentViewReport, 6=PoiDocumentReport, 7=ContainerLabelReport, 8=KpiTourReport, 9=KpiVehicleReport, 10=KpiDriverReport, 11=KpiQualityReport, 12=TradingOffersReport, 13=TradingDemandsReport, 14=KpiResourcePlanningReport, 15=KpiPositionReport, 16=KpiGroupReport, 17=PickupNoteReport, 18=KpiTsg, 19=KpiExamGroupReport, 21=BarcodeHistory, 26=TourListActualArrivalsReport, 27=TransportPointReport, 28=ShipmentListReport, 29=WeeklyPrecombinedTourReport, 30=InvoicingOrderReport, 31=InvoicingPositionsReport, 32=TourDriverTaskReport, 33=RecurringOrdersReport, 34=WaySplitReport, 35=ScaleNoteReport, 36=PickupQuantityReport, 37=FlushingReport, 38=ProposalReport, 39=LoadUnitReport, 40=ParentShipmentReport"
}
}
}
{
"title": "WebhookTourDto",
"type": "object",
"properties": {
"id": { "type": "integer" },
"groupId": { "type": ["integer", "null"] },
"refId": { "type": ["string", "null"] },
"name": { "type": ["string", "null"] },
"note": { "type": ["string", "null"] },
"version": { "type": ["string", "null"] },
"timeStamp": { "type": ["string", "null"], "format": "date-time" },
"tourStatus": { "type": ["string", "null"] },
"vehicleId": { "type": ["integer", "null"] },
"vehicleRefId": { "type": ["string", "null"] },
"vehicleTSGRefId": { "type": ["string", "null"] },
"trailerid": { "type": ["integer", "null"] },
"trailerRefId": { "type": ["string", "null"] },
"trailerTSGRefId": { "type": ["string", "null"] },
"driverId": { "type": ["integer", "null"] },
"driverRefId": { "type": ["string", "null"] },
"customField1": { "type": ["string", "null"] },
"customField2": { "type": ["string", "null"] },
"customField3": { "type": ["string", "null"] },
"customField4": { "type": ["string", "null"] },
"customField5": { "type": ["string", "null"] },
"customField6": { "type": ["string", "null"] },
"date": { "type": ["string", "null"], "format": "date", "description": "heure LOCALE, sérialisée en date uniquement via WebhookDateConverter" },
"tillDate": { "type": ["string", "null"], "format": "date", "description": "heure LOCALE, sérialisée en date uniquement via WebhookDateConverter" },
"fixStartTime": { "type": ["string", "null"], "pattern": "^-?\\d+\\.?\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$", "description": "TimeSpan sérialisé en chaîne (p.ex. '08:30:00')" },
"scheduledStart": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"scheduledEnd": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"start": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"end": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"distance": { "type": ["integer", "null"] },
"actualDistance": { "type": ["integer", "null"] },
"plannedDrivingTime": { "type": ["integer", "null"] },
"actualDrivingTime": { "type": ["integer", "null"] },
"plannedTourTime": { "type": ["integer", "null"] },
"actualTourTime": { "type": ["integer", "null"] },
"precombinedTourNumber": { "type": ["string", "null"] },
"shipments": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookShipmentDto" }
}
},
"$defs": {
"WebhookShipmentDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"groupId": { "type": "integer" },
"refId": { "type": ["string", "null"] },
"source": { "type": ["string", "null"] },
"salesmanCode": { "type": ["string", "null"] },
"orderNumber": { "type": ["string", "null"] },
"note": { "type": ["string", "null"] },
"timeStamp": { "type": "string", "format": "date-time" },
"status": { "type": ["string", "null"] },
"fiscalStatus": { "type": ["string", "null"] },
"shipmentType": { "type": ["string", "null"] },
"customField1": { "type": ["string", "null"] },
"customField2": { "type": ["string", "null"] },
"customField3": { "type": ["string", "null"] },
"customField4": { "type": ["string", "null"] },
"customField5": { "type": ["string", "null"] },
"customField6": { "type": ["string", "null"] },
"customField7": { "type": ["string", "null"] },
"customField8": { "type": ["string", "null"] },
"customField9": { "type": ["string", "null"] },
"customField10": { "type": ["string", "null"] },
"precombinedTourNumber": { "type": ["string", "null"] },
"extSortOrderA": { "type": ["integer", "null"] },
"extSortOrderB": { "type": ["integer", "null"] },
"calculatedSortOrderA": { "type": ["integer", "null"] },
"calculatedSortOrderB": { "type": ["integer", "null"] },
"transportPoints": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookTransportPointDto" }
},
"shipmentReports": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookShipmentReportDto" }
},
"parentShipmentReports": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookShipmentReportDto" },
"description": "Reports stored on the split parent shipment (e.g. the parent delivery note); empty for shipments without a split parent"
},
"splitParentId": { "type": ["integer", "null"] },
"parentShipmentType": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/ShipmentTypeEnum" }
]
},
"splits": { "type": ["integer", "null"] },
"splitSortOrder": { "type": ["integer", "null"] },
"pickupLocationRefId": { "type": ["string", "null"] },
"dropoffLocationRefId": { "type": ["string", "null"] },
"isReadyForCommissioning": { "type": "boolean" }
}
},
"WebhookTransportPointDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"dropoffStop": { "type": "boolean" },
"sortOrder": { "type": "integer" },
"precombinedTourNumber": { "type": ["string", "null"] },
"scheduledArrival": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"scheduledDeparture": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"startActual": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"endActual": { "type": ["string", "null"], "format": "date-time", "description": "heure UTC" },
"images": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookTransportPointImageDto" }
},
"status": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/StopStatusEnum" }
]
},
"driverTasks": {
"type": ["array", "null"],
"items": { "$ref": "#/$defs/WebhookDriverTaskDto" }
}
}
},
"WebhookTransportPointImageDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"transportPointId": { "type": "integer" },
"url": { "type": ["string", "null"] },
"lat": { "type": ["number", "null"] },
"lng": { "type": ["number", "null"] }
}
},
"WebhookShipmentReportDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"reportId": { "type": ["integer", "null"] },
"reportUrl": { "type": ["string", "null"] },
"timeStamp": { "type": ["string", "null"], "format": "date-time" },
"reportType": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/$defs/ReportTypeEnum" }
]
}
}
},
"WebhookDriverTaskDto": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"type": { "$ref": "#/$defs/DriverTaskType" },
"status": { "$ref": "#/$defs/DriverTaskStatusEnum" },
"barCode": { "type": ["string", "null"] },
"scannerLat": { "type": ["number", "null"] },
"scannerLng": { "type": ["number", "null"] },
"quantityRequired": { "type": ["number", "null"] },
"quantityActual": { "type": ["number", "null"] },
"quantityEdited": { "type": ["number", "null"] }
}
},
"ShipmentTypeEnum": {
"type": "integer",
"enum": [0, 1, 2, 3, 4],
"description": "0=InitialLeg, 1=MainLeg, 2=FinalLeg, 3=Direct, 4=Undefined"
},
"StopStatusEnum": {
"type": "integer",
"enum": [0, 1, 2, 3, 9, 10, 11],
"description": "0=NotStarted, 1=Success, 2=Fail, 3=Warning, 9=InProgress, 10=WaitingAfterArrival, 11=WaitingBeforeDeparture"
},
"DriverTaskType": {
"type": "integer",
"enum": [1, 2, 3],
"description": "1=Pickup, 2=Dropoff, 3=Flushing"
},
"DriverTaskStatusEnum": {
"type": "integer",
"enum": [0, 1, 2, 3],
"description": "0=NotStarted, 1=Completed, 2=Failed, 3=InProgress"
},
"ReportTypeEnum": {
"type": "integer",
"enum": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,21,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],
"description": "0=EmptyReport, 1=TourListReport, 2=DeliveryNoteReport, 3=SpecialEventReport, 4=SimulationManipulationReport, 5=ShipmentViewReport, 6=PoiDocumentReport, 7=ContainerLabelReport, 8=KpiTourReport, 9=KpiVehicleReport, 10=KpiDriverReport, 11=KpiQualityReport, 12=TradingOffersReport, 13=TradingDemandsReport, 14=KpiResourcePlanningReport, 15=KpiPositionReport, 16=KpiGroupReport, 17=PickupNoteReport, 18=KpiTsg, 19=KpiExamGroupReport, 21=BarcodeHistory, 26=TourListActualArrivalsReport, 27=TransportPointReport, 28=ShipmentListReport, 29=WeeklyPrecombinedTourReport, 30=InvoicingOrderReport, 31=InvoicingPositionsReport, 32=TourDriverTaskReport, 33=RecurringOrdersReport, 34=WaySplitReport, 35=ScaleNoteReport, 36=PickupQuantityReport, 37=FlushingReport, 38=ProposalReport, 39=LoadUnitReport, 40=ParentShipmentReport"
}
}
}
Réseau & sécurité
Protocole et port
Les webhooks sont envoyés exclusivement via HTTPS sur le port 443. L'URL du webhook doit toujours commencer par https:// dans la configuration x4fleet. Une redirection HTTP côté destinataire est possible, mais il est recommandé de configurer directement l'URL avec https://.
Adresses IP sources
Les webhooks sont envoyés depuis l'infrastructure API x4fleet (Azure App Service). Les adresses IP sortantes sont gérées par Azure et peuvent changer lors de modifications de l'infrastructure. La production et le staging/testing s'exécutent sur des environnements Azure distincts avec des adresses IP sortantes différentes. Les adresses IP actuelles peuvent être demandées auprès du support x4fleet.
Remarque : gateway.x4fleet.com et gateway-staging.x4fleet.com sont les points d'entrée API pour les clients x4fleet — et non la source des webhooks sortants. Ces noms DNS ne sont donc pas adaptés aux règles de pare-feu côté destinataire.
Recommandations de sécurité
Le filtrage par IP est techniquement possible, mais déconseillé car les IP sortantes Azure ne sont pas exclusives et peuvent changer. X4fleet prend en charge les méthodes d'authentification suivantes, sécurisées de manière cryptographique et indépendantes des adresses IP :
- Basic Auth — Nom d'utilisateur et mot de passe dans l'en-tête
Authorization - Certificat client (mTLS) — Certificat x509 via Azure Key Vault