Développeurs

API, webhooks & EDI JLR Transport

Créez vos demandes de transport, suivez chaque camion et recevez les évènements en temps réel directement dans votre ERP, WMS ou TMS — par API, par webhooks ou par EDIFACT.

Introduction

L'API JLR Transport permet à vos systèmes (ERP, WMS, TMS) de créer des demandes de transport, de suivre vos expéditions en temps réel et de récupérer vos documents (CMR, preuves de livraison) sans passer par le portail.

Elle est organisée autour de ressources prévisibles, renvoie du JSON, utilise les codes HTTP standard et s'authentifie par clé d'API. Les évènements (livraison, retard probable, nouvelle étape…) peuvent être poussés vers votre serveur par webhook signé.

Adresse de base : https://jlr-transport.com/api/v1. Toutes les requêtes se font en HTTPS.

Démarrage rapide

  1. Créez une clé de test dans Espace client › Intégrations.
  2. Appelez GET /v1/me pour vérifier la connexion.
  3. Listez les expéditions d'exemple, puis passez à une clé de production.
export JLR_API_KEY="jlr_test_..."

# 1. Vérifier la clé
curl https://jlr-transport.com/api/v1/me \
  -H "Authorization: Bearer $JLR_API_KEY"

# 2. Lister les expéditions
curl "https://jlr-transport.com/api/v1/shipments?limit=10" \
  -H "Authorization: Bearer $JLR_API_KEY"

Créer une demande de transport

curl -X POST https://jlr-transport.com/api/v1/transport-requests \
  -H "Authorization: Bearer $JLR_API_KEY" \
  -H "Idempotency-Key: PO-2026-0457" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "chilled",
    "origin": { "city": "Lyon", "country": "FR" },
    "destination": { "city": "Poznań", "country": "PL" },
    "pallets": 12,
    "weight_kg": 6800,
    "temperature": "+2/+4 °C",
    "pickup_date": "2026-10-12",
    "reference": "PO-2026-0457"
  }'

Authentification et droits

Créez une clé dans Espace client › Intégrations (réservé aux administrateurs de votre entreprise), puis envoyez-la dans l'en-tête Authorization de chaque requête : « Authorization: Bearer jlr_live_… ». Les clés de production commencent par jlr_live_, les clés de test par jlr_test_.

Une clé n'est affichée qu'une seule fois, à sa création : JLR Transport n'en conserve qu'une empreinte et ne peut pas la retrouver. Conservez-la dans un coffre de secrets, jamais dans du code source, une application mobile ou une page web. Une clé voit les données de toute votre entreprise (comptes d'origine et sous-comptes) dans la limite de ses droits ; elle peut être révoquée à tout moment, le refus est immédiat.

Droits disponibles
  • shipments:read

    Lire les expéditions, leur suivi et les évènements.

  • documents:read

    Lister et télécharger les documents (CMR, signatures, photos, vos propres dépôts).

  • requests:read

    Lire les demandes de transport.

  • requests:write

    Créer des demandes de transport.

Mode test

Une clé jlr_test_ travaille sur des données d'exemple (trois expéditions TEST-0001 à TEST-0003, leurs évènements et documents). Les créations de demandes sont validées puis simulées : rien n'est enregistré ni transmis à JLR. Les objets renvoyés portent « livemode: false ». Utilisez-la pour développer et tester votre intégration sans toucher à vos vraies expéditions.

Les webhooks, eux, concernent toujours vos vraies expéditions : utilisez « Envoyer un test » dans Espace client › Intégrations pour recevoir un évènement d'essai signé.

Référence des routes

GET/v1/me

Vérifier la clé : identité, droits et limites.

Droit requis : aucun droit particulier

Exemple de réponse · 200
{
  "object": "api_key",
  "id": "key_2b3c4d5e6f704a8192b3c4d5e6f70819",
  "name": "ERP production",
  "livemode": true,
  "scopes": [
    "shipments:read",
    "documents:read",
    "requests:read",
    "requests:write"
  ],
  "created_at": "2026-10-01T09:00:00.000Z",
  "expires_at": null,
  "company": {
    "name": "Exemple Foods SAS"
  },
  "rate_limit": {
    "requests_per_minute": 120,
    "write_requests_per_minute": 30,
    "write_requests_per_day": 1000
  }
}
GET/v1/shipments

Lister les expéditions de l'entreprise, des plus récentes aux plus anciennes.

Droit requis : shipments:read

Paramètres
  • limitintegerquery

    Nombre d'éléments (1 à 100, 25 par défaut).

  • cursorstringquery

    Curseur « next_cursor » de la page précédente.

  • statusstringquery

    pending, in_transit, delivered, delayed, cancelled (séparés par des virgules).

  • referencestringquery

    Référence JLR exacte.

  • external_referencestringquery

    Votre référence exacte.

  • created_afterISO 8601query

    Créées après cet horodatage.

  • created_beforeISO 8601query

    Créées avant cet horodatage.

Exemple de réponse · 200
{
  "object": "list",
  "data": [
    {
      "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
      "object": "shipment",
      "reference": "JLR-2026-0457",
      "external_reference": "PO-2026-0457",
      "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
      "status": "in_transit",
      "origin": {
        "city": "Lyon",
        "country": "FR",
        "address": "12 rue des Entrepôts, 69007 Lyon"
      },
      "destination": {
        "city": "Poznań",
        "country": "PL",
        "address": "ul. Magazynowa 5, 61-001 Poznań"
      },
      "pickup": {
        "planned_date": "2026-10-12",
        "confirmed_at": "2026-10-12T07:42:10.000Z"
      },
      "delivery": {
        "estimated_date": "2026-10-14",
        "confirmed_at": null
      },
      "cargo": {
        "description": "12 palettes · frais",
        "pallets": 12,
        "weight_kg": 6800
      },
      "supplier": {
        "name": "Exemple SAS",
        "references": [
          "BL-88231"
        ]
      },
      "created_at": "2026-10-09T14:03:21.000Z",
      "livemode": true
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ0IjoiMjAyNi0xMC0wOVQxNDowMzoyMS4wMDAwMDArMDA6MDAiLCJpIjoiM2YyYTljMWUtN2I0NC00ZDBhLTlhNWUtMWMyZDNlNGY1YTZiIn0"
}
GET/v1/shipments/{id}

Récupérer une expédition.

Droit requis : shipments:read

Paramètres
  • idstringpathobligatoire

    Identifiant (shp_…) ou référence JLR de l'expédition.

Exemple de réponse · 200
{
  "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
  "object": "shipment",
  "reference": "JLR-2026-0457",
  "external_reference": "PO-2026-0457",
  "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
  "status": "in_transit",
  "origin": {
    "city": "Lyon",
    "country": "FR",
    "address": "12 rue des Entrepôts, 69007 Lyon"
  },
  "destination": {
    "city": "Poznań",
    "country": "PL",
    "address": "ul. Magazynowa 5, 61-001 Poznań"
  },
  "pickup": {
    "planned_date": "2026-10-12",
    "confirmed_at": "2026-10-12T07:42:10.000Z"
  },
  "delivery": {
    "estimated_date": "2026-10-14",
    "confirmed_at": null
  },
  "cargo": {
    "description": "12 palettes · frais",
    "pallets": 12,
    "weight_kg": 6800
  },
  "supplier": {
    "name": "Exemple SAS",
    "references": [
      "BL-88231"
    ]
  },
  "created_at": "2026-10-09T14:03:21.000Z",
  "livemode": true
}
GET/v1/shipments/{id}/tracking

Position, heure d'arrivée estimée et étapes d'une expédition.

Droit requis : shipments:read

Paramètres
  • idstringpathobligatoire

    Identifiant (shp_…) ou référence JLR de l'expédition.

  • includestringquery

    « trail » ajoute la trace récente du camion.

Exemple de réponse · 200
{
  "object": "tracking",
  "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
  "state": "in_transit",
  "position": {
    "lat": 49.1193,
    "lng": 6.1757,
    "recorded_at": "2026-10-12T14:20:05.000Z",
    "near": "Near Metz",
    "fresh": true
  },
  "eta": {
    "target": "delivery",
    "at": "2026-10-14T09:30:00.000Z",
    "earliest": "2026-10-14T08:00:00.000Z",
    "latest": "2026-10-14T11:00:00.000Z",
    "status": "on_time",
    "basis": "routed",
    "remaining_km": 912
  },
  "progress": 0.34,
  "public_url": null,
  "trail": [],
  "checkpoints": [
    {
      "label": "Loading completed",
      "created_at": "2026-10-12T07:40:00.000Z"
    }
  ],
  "livemode": true
}
GET/v1/shipments/{id}/documents

Lister les documents d'une expédition (CMR, signatures, photos, vos dépôts).

Droit requis : documents:read

Paramètres
  • idstringpathobligatoire

    Identifiant (shp_…) ou référence JLR de l'expédition.

Exemple de réponse · 200
{
  "object": "list",
  "data": [
    {
      "id": "doc_5c6d7e8f9a0b4c1d8e2f3a4b5c6d7e8f",
      "object": "document",
      "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
      "type": "cmr",
      "file_name": "CMR-JLR-2026-0457.pdf",
      "created_at": "2026-10-14T09:41:00.000Z",
      "livemode": true
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/shipments/{id}/documents/{document_id}

Obtenir un lien de téléchargement valable 5 minutes.

Droit requis : documents:read

Paramètres
  • idstringpathobligatoire

    Identifiant (shp_…) ou référence JLR de l'expédition.

  • document_idstringpathobligatoire

    Identifiant du document (doc_…).

Exemple de réponse · 200
{
  "object": "document_link",
  "document": {
    "id": "doc_5c6d7e8f9a0b4c1d8e2f3a4b5c6d7e8f",
    "object": "document",
    "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
    "type": "cmr",
    "file_name": "CMR-JLR-2026-0457.pdf",
    "created_at": "2026-10-14T09:41:00.000Z",
    "livemode": true
  },
  "url": "https://…/signed-download-link",
  "expires_at": "2026-10-14T09:46:00.000Z"
}
GET/v1/events

Évènements des 30 derniers jours : même contenu que les webhooks.

Droit requis : shipments:read

Paramètres
  • limitintegerquery

    Nombre d'éléments (1 à 100, 25 par défaut).

  • cursorstringquery

    Curseur « next_cursor » de la page précédente.

  • typestringquery

    Un ou plusieurs types, séparés par des virgules.

  • shipmentstringquery

    Seulement les évènements de cette expédition (shp_…).

  • created_afterISO 8601query

    Évènements postérieurs à cet horodatage.

Exemple de réponse · 200
{
  "object": "list",
  "data": [
    {
      "id": "evt_7e8f9a0b1c2d4e3f8a4b5c6d7e8f9a0b",
      "object": "event",
      "type": "shipment.status_changed",
      "created_at": "2026-10-12T07:42:11.000Z",
      "api_version": "v1",
      "livemode": true,
      "data": {
        "object": {
          "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
          "object": "shipment",
          "reference": "JLR-2026-0457",
          "external_reference": "PO-2026-0457",
          "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
          "status": "in_transit",
          "origin": {
            "city": "Lyon",
            "country": "FR",
            "address": "12 rue des Entrepôts, 69007 Lyon"
          },
          "destination": {
            "city": "Poznań",
            "country": "PL",
            "address": "ul. Magazynowa 5, 61-001 Poznań"
          },
          "pickup": {
            "planned_date": "2026-10-12",
            "confirmed_at": "2026-10-12T07:42:10.000Z"
          },
          "delivery": {
            "estimated_date": "2026-10-14",
            "confirmed_at": null
          },
          "cargo": {
            "description": "12 palettes · frais",
            "pallets": 12,
            "weight_kg": 6800
          },
          "supplier": {
            "name": "Exemple SAS",
            "references": [
              "BL-88231"
            ]
          },
          "created_at": "2026-10-09T14:03:21.000Z",
          "livemode": true
        },
        "previous_attributes": {
          "status": "pending"
        }
      }
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/transport-requests

Lister les demandes de transport de l'entreprise (portail et API).

Droit requis : requests:read

Paramètres
  • limitintegerquery

    Nombre d'éléments (1 à 100, 25 par défaut).

  • cursorstringquery

    Curseur « next_cursor » de la page précédente.

  • referencestringquery

    Votre référence exacte.

Exemple de réponse · 200
{
  "object": "list",
  "data": [
    {
      "id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
      "object": "transport_request",
      "status": "shipment_created",
      "channel": "api",
      "reference": "PO-2026-0457",
      "mode": "chilled",
      "adr": false,
      "origin": {
        "city": "Lyon",
        "country": "FR",
        "address": null
      },
      "destination": {
        "city": "Poznań",
        "country": "PL",
        "address": null
      },
      "pickups_count": 1,
      "pallets": 12,
      "weight_kg": 6800,
      "temperature": "+2/+4 °C",
      "pickup_date": "2026-10-12",
      "supplier_name": null,
      "message": null,
      "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
      "created_at": "2026-10-09T14:03:21.000Z",
      "livemode": true
    }
  ],
  "has_more": false,
  "next_cursor": null
}
POST/v1/transport-requests

Créer une demande de transport. L'équipe JLR la reçoit, puis crée l'expédition.

Droit requis : requests:write · Idempotency-Key

Paramètres
  • modestringbodyobligatoire

    dry (sec), frozen (surgelé) ou chilled (frais).

  • originobjectbodyobligatoire

    { city, country (2 lettres), address? } : lieu d'enlèvement.

  • destinationobjectbodyobligatoire

    { city, country (2 lettres), address? } : lieu de livraison.

  • palletsintegerbodyobligatoire

    Nombre de palettes (1 à 100).

  • weight_kgnumberbody

    Poids total en kg.

  • adrbooleanbody

    Marchandises dangereuses (ADR).

  • pickups_countintegerbody

    Nombre de chargements (1 à 10, 1 par défaut).

  • temperaturestringbody

    Température demandée, ex. « +2/+4 °C ».

  • pickup_dateYYYY-MM-DDbody

    Date d'enlèvement souhaitée (aujourd'hui ou plus tard).

  • supplier_namestringbody

    Fournisseur à enlever.

  • referencestringbody

    Votre référence (numéro de commande) : reprise sur l'expédition.

  • messagestringbody

    Précisions pour l'équipe JLR.

  • contactobjectbody

    { name?, email?, phone? } : contact de cette demande (par défaut, celui de votre compte).

Exemple de requête
{
  "mode": "chilled",
  "origin": {
    "city": "Lyon",
    "country": "FR"
  },
  "destination": {
    "city": "Poznań",
    "country": "PL"
  },
  "pallets": 12,
  "weight_kg": 6800,
  "temperature": "+2/+4 °C",
  "pickup_date": "2026-10-12",
  "reference": "PO-2026-0457"
}
Exemple de réponse · 201
{
  "id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
  "object": "transport_request",
  "status": "received",
  "channel": "api",
  "reference": "PO-2026-0457",
  "mode": "chilled",
  "adr": false,
  "origin": {
    "city": "Lyon",
    "country": "FR",
    "address": null
  },
  "destination": {
    "city": "Poznań",
    "country": "PL",
    "address": null
  },
  "pickups_count": 1,
  "pallets": 12,
  "weight_kg": 6800,
  "temperature": "+2/+4 °C",
  "pickup_date": "2026-10-12",
  "supplier_name": null,
  "message": null,
  "shipment_id": null,
  "created_at": "2026-10-09T14:03:21.000Z",
  "livemode": true
}
GET/v1/transport-requests/{id}

Récupérer une demande et, une fois créée, son expédition.

Droit requis : requests:read

Paramètres
  • idstringpathobligatoire

    Identifiant de la demande (trq_…).

Exemple de réponse · 200
{
  "id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
  "object": "transport_request",
  "status": "shipment_created",
  "channel": "api",
  "reference": "PO-2026-0457",
  "mode": "chilled",
  "adr": false,
  "origin": {
    "city": "Lyon",
    "country": "FR",
    "address": null
  },
  "destination": {
    "city": "Poznań",
    "country": "PL",
    "address": null
  },
  "pickups_count": 1,
  "pallets": 12,
  "weight_kg": 6800,
  "temperature": "+2/+4 °C",
  "pickup_date": "2026-10-12",
  "supplier_name": null,
  "message": null,
  "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
  "created_at": "2026-10-09T14:03:21.000Z",
  "livemode": true
}

Objets

shipment

Une expédition : de la demande acceptée à la livraison.

  • idstring

    Identifiant (shp_…).

  • referencestring

    Référence JLR, aussi acceptée à la place de l'id dans les URL.

  • external_referencestring | null

    Votre référence (champ « reference » de la demande).

  • request_idstring | null

    Demande d'origine (trq_…).

  • statusstring

    pending, in_transit, delivered, delayed ou cancelled.

  • origin / destinationobject

    { city, country (ISO 3166-1), address }.

  • pickupobject

    { planned_date, confirmed_at }.

  • deliveryobject

    { estimated_date, confirmed_at }.

  • cargoobject

    { description, pallets, weight_kg }.

  • supplierobject

    { name, references[] } : fournisseur et références associées.

  • created_atstring (ISO 8601)

    Création de l'expédition.

  • livemodeboolean

    false avec une clé de test.

{
  "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
  "object": "shipment",
  "reference": "JLR-2026-0457",
  "external_reference": "PO-2026-0457",
  "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
  "status": "in_transit",
  "origin": {
    "city": "Lyon",
    "country": "FR",
    "address": "12 rue des Entrepôts, 69007 Lyon"
  },
  "destination": {
    "city": "Poznań",
    "country": "PL",
    "address": "ul. Magazynowa 5, 61-001 Poznań"
  },
  "pickup": {
    "planned_date": "2026-10-12",
    "confirmed_at": "2026-10-12T07:42:10.000Z"
  },
  "delivery": {
    "estimated_date": "2026-10-14",
    "confirmed_at": null
  },
  "cargo": {
    "description": "12 palettes · frais",
    "pallets": 12,
    "weight_kg": 6800
  },
  "supplier": {
    "name": "Exemple SAS",
    "references": [
      "BL-88231"
    ]
  },
  "created_at": "2026-10-09T14:03:21.000Z",
  "livemode": true
}
tracking

Suivi d'une expédition : position, heure d'arrivée estimée et étapes.

  • statestring

    awaiting_pickup, in_transit ou delivered.

  • positionobject | null

    Dernière position connue ; « fresh » est faux au-delà de 45 minutes. Null sans partage de position.

  • etaobject | null

    Heure d'arrivée estimée (fenêtre, statut on_time/at_risk/late, km restants), pauses et repos réglementaires inclus.

  • progressnumber | null

    Avancement de 0 à 1.

  • public_urlstring | null

    Lien de suivi pour le destinataire, s'il a été partagé depuis le portail.

  • trail[lat, lng][]

    Trace récente (uniquement avec ?include=trail).

  • checkpointsobject[]

    Étapes ajoutées pendant le transport.

{
  "object": "tracking",
  "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
  "state": "in_transit",
  "position": {
    "lat": 49.1193,
    "lng": 6.1757,
    "recorded_at": "2026-10-12T14:20:05.000Z",
    "near": "Near Metz",
    "fresh": true
  },
  "eta": {
    "target": "delivery",
    "at": "2026-10-14T09:30:00.000Z",
    "earliest": "2026-10-14T08:00:00.000Z",
    "latest": "2026-10-14T11:00:00.000Z",
    "status": "on_time",
    "basis": "routed",
    "remaining_km": 912
  },
  "progress": 0.34,
  "public_url": null,
  "trail": [],
  "checkpoints": [
    {
      "label": "Loading completed",
      "created_at": "2026-10-12T07:40:00.000Z"
    }
  ],
  "livemode": true
}
transport_request

Votre demande de transport : JLR la reçoit, puis crée l'expédition.

  • idstring

    Identifiant (trq_…).

  • statusstring

    received, puis shipment_created une fois l'expédition créée.

  • channelstring

    api, edi (message EDIFACT) ou web.

  • referencestring | null

    Votre référence, reprise sur l'expédition.

  • modestring

    dry (sec), frozen (surgelé) ou chilled (frais).

  • shipment_idstring | null

    L'expédition créée (shp_…).

{
  "id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
  "object": "transport_request",
  "status": "received",
  "channel": "api",
  "reference": "PO-2026-0457",
  "mode": "chilled",
  "adr": false,
  "origin": {
    "city": "Lyon",
    "country": "FR",
    "address": null
  },
  "destination": {
    "city": "Poznań",
    "country": "PL",
    "address": null
  },
  "pickups_count": 1,
  "pallets": 12,
  "weight_kg": 6800,
  "temperature": "+2/+4 °C",
  "pickup_date": "2026-10-12",
  "supplier_name": null,
  "message": null,
  "shipment_id": null,
  "created_at": "2026-10-09T14:03:21.000Z",
  "livemode": true
}
document

Un document de preuve ou un fichier que vous avez déposé.

  • idstring

    Identifiant (doc_…).

  • typestring

    cmr, photo, pickup_signature, delivery_signature ou other.

  • file_namestring | null

    Nom du fichier.

{
  "id": "doc_5c6d7e8f9a0b4c1d8e2f3a4b5c6d7e8f",
  "object": "document",
  "shipment_id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
  "type": "cmr",
  "file_name": "CMR-JLR-2026-0457.pdf",
  "created_at": "2026-10-14T09:41:00.000Z",
  "livemode": true
}
event

Un évènement : ce que reçoivent vos webhooks, et ce que renvoie GET /v1/events.

  • idstring

    Identifiant unique (evt_…) : sert à dédoublonner.

  • typestring

    Type de l'évènement (voir la liste ci-dessous).

  • data.objectobject

    L'objet concerné (une expédition, ou un document), tel qu'il est au moment de l'évènement.

  • api_versionstring

    v1.

{
  "id": "evt_7e8f9a0b1c2d4e3f8a4b5c6d7e8f9a0b",
  "object": "event",
  "type": "shipment.status_changed",
  "created_at": "2026-10-12T07:42:11.000Z",
  "api_version": "v1",
  "livemode": true,
  "data": {
    "object": {
      "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
      "object": "shipment",
      "reference": "JLR-2026-0457",
      "external_reference": "PO-2026-0457",
      "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
      "status": "in_transit",
      "origin": {
        "city": "Lyon",
        "country": "FR",
        "address": "12 rue des Entrepôts, 69007 Lyon"
      },
      "destination": {
        "city": "Poznań",
        "country": "PL",
        "address": "ul. Magazynowa 5, 61-001 Poznań"
      },
      "pickup": {
        "planned_date": "2026-10-12",
        "confirmed_at": "2026-10-12T07:42:10.000Z"
      },
      "delivery": {
        "estimated_date": "2026-10-14",
        "confirmed_at": null
      },
      "cargo": {
        "description": "12 palettes · frais",
        "pallets": 12,
        "weight_kg": 6800
      },
      "supplier": {
        "name": "Exemple SAS",
        "references": [
          "BL-88231"
        ]
      },
      "created_at": "2026-10-09T14:03:21.000Z",
      "livemode": true
    },
    "previous_attributes": {
      "status": "pending"
    }
  }
}

Évènements

  • shipment.created

    Une expédition est créée à partir de vos demandes.

  • shipment.status_changed

    Le statut change (en attente, en transit, livré, retard).

    data.previous_attributes.status : l'ancien statut.

  • shipment.picked_up

    L'enlèvement de la marchandise est confirmé.

  • shipment.delivered

    La livraison est confirmée.

    Les preuves arrivent ensuite en « document.added ».

  • shipment.checkpoint

    Une étape est ajoutée (« Douane passée »…).

    data.checkpoint : { label, created_at }.

  • shipment.eta_updated

    L'heure d'arrivée estimée change d'au moins 30 minutes.

    data.tracking.eta : fenêtre d'arrivée et statut.

  • shipment.delay_risk

    Un retard devient probable (statut « at_risk » ou « late »).

    data.tracking.eta : nouvelle fenêtre d'arrivée.

  • document.added

    Un document de preuve est ajouté (CMR, signature, photo).

    data.object est un « document » : téléchargez-le avec GET …/documents/{id}.

Exemple : retard probable
{
  "id": "evt_8f9a0b1c2d3e4f4a8b5c6d7e8f9a0b1c",
  "object": "event",
  "type": "shipment.delay_risk",
  "created_at": "2026-10-13T16:05:00.000Z",
  "api_version": "v1",
  "livemode": true,
  "data": {
    "object": {
      "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
      "object": "shipment",
      "reference": "JLR-2026-0457",
      "external_reference": "PO-2026-0457",
      "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
      "status": "in_transit",
      "origin": {
        "city": "Lyon",
        "country": "FR",
        "address": "12 rue des Entrepôts, 69007 Lyon"
      },
      "destination": {
        "city": "Poznań",
        "country": "PL",
        "address": "ul. Magazynowa 5, 61-001 Poznań"
      },
      "pickup": {
        "planned_date": "2026-10-12",
        "confirmed_at": "2026-10-12T07:42:10.000Z"
      },
      "delivery": {
        "estimated_date": "2026-10-14",
        "confirmed_at": null
      },
      "cargo": {
        "description": "12 palettes · frais",
        "pallets": 12,
        "weight_kg": 6800
      },
      "supplier": {
        "name": "Exemple SAS",
        "references": [
          "BL-88231"
        ]
      },
      "created_at": "2026-10-09T14:03:21.000Z",
      "livemode": true
    },
    "tracking": {
      "eta": {
        "target": "delivery",
        "at": "2026-10-14T13:40:00.000Z",
        "earliest": "2026-10-14T12:00:00.000Z",
        "latest": "2026-10-14T15:00:00.000Z",
        "status": "at_risk",
        "basis": "routed",
        "remaining_km": 640
      }
    }
  }
}
{
  "id": "evt_7e8f9a0b1c2d4e3f8a4b5c6d7e8f9a0b",
  "object": "event",
  "type": "shipment.status_changed",
  "created_at": "2026-10-12T07:42:11.000Z",
  "api_version": "v1",
  "livemode": true,
  "data": {
    "object": {
      "id": "shp_3f2a9c1e7b444d0a9a5e1c2d3e4f5a6b",
      "object": "shipment",
      "reference": "JLR-2026-0457",
      "external_reference": "PO-2026-0457",
      "request_id": "trq_9b1d2c3e4f504a618b72c3d4e5f60718",
      "status": "in_transit",
      "origin": {
        "city": "Lyon",
        "country": "FR",
        "address": "12 rue des Entrepôts, 69007 Lyon"
      },
      "destination": {
        "city": "Poznań",
        "country": "PL",
        "address": "ul. Magazynowa 5, 61-001 Poznań"
      },
      "pickup": {
        "planned_date": "2026-10-12",
        "confirmed_at": "2026-10-12T07:42:10.000Z"
      },
      "delivery": {
        "estimated_date": "2026-10-14",
        "confirmed_at": null
      },
      "cargo": {
        "description": "12 palettes · frais",
        "pallets": 12,
        "weight_kg": 6800
      },
      "supplier": {
        "name": "Exemple SAS",
        "references": [
          "BL-88231"
        ]
      },
      "created_at": "2026-10-09T14:03:21.000Z",
      "livemode": true
    },
    "previous_attributes": {
      "status": "pending"
    }
  }
}

Webhooks

Enregistrez une adresse HTTPS dans Espace client › Intégrations : JLR Transport y envoie un POST JSON pour chaque évènement auquel vous êtes abonné. Le corps est l'objet « event » décrit plus bas. Répondez par un code 2xx en moins de 8 secondes pour accuser réception, puis traitez le message de façon asynchrone.

Chaque envoi est signé. L'en-tête « JLR-Signature » contient « t=<horodatage>,v1=<signature> » : la signature est le HMAC-SHA256, en hexadécimal, de « <horodatage>.<corps brut> » avec votre secret de signature (« whsec_… », affiché à la création du point de réception). Vérifiez-la sur le corps brut reçu, refusez les horodatages de plus de 5 minutes et comparez en temps constant.

Si votre serveur ne répond pas 2xx, l'envoi est retenté après 1 min, 5 min, 30 min, 2 h, 6 h, 12 h puis 24 h (8 tentatives en tout). Après 5 évènements définitivement perdus d'affilée, le point de réception est désactivé et vous en êtes averti par e-mail ; les évènements restent disponibles 30 jours via GET /v1/events et peuvent être renvoyés depuis le portail.

  • Livraison « au moins une fois » : un évènement peut arriver deux fois. Dédoublonnez avec son « id » (en-tête JLR-Event-Id).
  • L'ordre n'est pas garanti : comparez « created_at » ou relisez l'expédition (GET /v1/shipments/{id}) pour connaître son état actuel.
  • Les redirections ne sont pas suivies. L'adresse doit être un nom de domaine public en HTTPS (port 443 ou 8443), pas une adresse IP ni un réseau privé.
  • Vous pouvez renouveler le secret à tout moment : l'ancien cesse de fonctionner immédiatement.
Vérifier la signature
const crypto = require("crypto");

// rawBody = corps reçu TEL QUEL (texte), pas un JSON ré-sérialisé
function verifyJlrSignature(rawBody, header, secret, tolerance = 300) {
  let timestamp = null;
  const signatures = [];
  for (const part of header.split(",")) {
    const [key, value] = part.split("=");
    if (key === "t") timestamp = value;
    if (key === "v1") signatures.push(value);
  }
  if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > tolerance) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  return signatures.some(
    (s) => s.length === expected.length && crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected))
  );
}

// Express : app.post("/webhooks/jlr", express.raw({ type: "application/json" }), (req, res) => {
//   if (!verifyJlrSignature(req.body.toString("utf8"), req.get("JLR-Signature") || "", process.env.JLR_WEBHOOK_SECRET)) return res.sendStatus(400);
//   const event = JSON.parse(req.body.toString("utf8"));
//   res.sendStatus(200); // répondez vite, traitez ensuite
// });

EDI (EDIFACT)

Si votre système sait échanger de l'EDIFACT, il peut se brancher directement à JLR Transport sans passer par l'API : vos ordres de transport (IFTMIN) arrivent chez nous comme des demandes, et nous vous renvoyons des accusés de réception (CONTRL) et des comptes rendus d'état (IFTSTA) à chaque étape du transport.

La liaison se met en place avec notre équipe : écrivez à contact@jlr-transport.com pour convenir des identifiants d'enveloppe, du mode de réception des comptes rendus et d'une période d'essai. Les transports par AS2 ou SFTP sont possibles par l'intermédiaire d'un fournisseur de passerelle (sur demande).

Mise en place

  1. Vous nous communiquez : votre identifiant d'enveloppe (UNB, S002) et son qualifiant, le jeu de caractères et la version de syntaxe que vous lisez, la version du message IFTSTA attendue, et comment recevoir nos comptes rendus (boîte à lettres à relever, ou envoi HTTPS vers votre adresse).
  2. Nous vous remettons : notre identifiant (JLRTRANSPORT, qualifiant ZZ par défaut) et votre adresse d'envoi, qui contient un jeton secret. Ce jeton est affiché une seule fois ; il peut être renouvelé à tout moment.
  3. Essais : envoyez des échanges avec l'indicateur de test (UNB, donnée 0035 = 1). Ils sont lus et accusés comme les autres, mais ne créent aucune demande. De notre côté, nous pouvons vous envoyer un message d'essai.
  4. Mise en service : vos ordres réels créent des demandes que notre équipe transforme en expéditions ; vous recevez ensuite les IFTSTA de chaque étape.

Messages et syntaxe

  • MessageIFTMIN D.96A
    Sensvous → JLR
    RôleOrdre de transport. Les éditions voisines (D.93A à D.01B, S.93A) sont lues de façon tolérante : seuls les segments utiles sont retenus, le reste est ignoré.
  • MessageIFTSTA D.01B
    SensJLR → vous
    RôleCompte rendu d'état : un message par évènement du transport (voir plus bas).
  • MessageCONTRL
    Sensdans les deux sens
    RôleAccusé de réception syntaxique. Nous répondons un CONTRL à chaque échange reçu ; vous pouvez en renvoyer un pour chaque IFTSTA (nous le rapprochons de notre message).
  • MessageAPERAK
    Sensvous → JLR
    RôleAccepté et enregistré au journal, sans autre suite.

Tout autre type de message est refusé avec l'erreur 17 (« pas d'accord d'échange »). Syntaxe ISO 9735, versions 3 et 4 (avis UNA, séparateur de répétition en version 4). Jeux de caractères lus : UNOA, UNOB, UNOC, UNOD, UNOE, UNOF, UNOW, UNOX, UNOY ; écrits : UNOA, UNOB, UNOC, UNOD, UNOW, UNOY (les lettres que le jeu de caractères ne sait pas représenter sont translittérées : Ł → L).

Envoyer un ordre de transport

Postez l'échange EDIFACT tel quel (le corps de la requête est l'échange, de UNA/UNB à UNZ) à l'adresse reçue : https://jlr-transport.com/api/edi/in/<jeton>. Si votre logiciel sait ajouter un en-tête, utilisez plutôt https://jlr-transport.com/api/edi/in avec « Authorization: Bearer <jeton> » (« Basic » avec le jeton comme mot de passe, ou l'en-tête X-EDI-Token, fonctionnent aussi). Un corps peut contenir jusqu'à 50 échanges, 1 Mo au total.

curl -X POST "https://jlr-transport.com/api/edi/in/$JLR_EDI_TOKEN" \
  -H "Content-Type: application/edifact" \
  --data-binary @order.edi

La réponse est notre accusé CONTRL (Content-Type application/edifact). Avec « Accept: application/json », vous obtenez à la place un compte rendu JSON lisible (statut de chaque message, erreurs, avertissements, identifiant de la demande créée).

  • Code HTTP200
    SignificationÉchange traité : le verdict (accepté ou refusé) est dans le CONTRL ; l'en-tête X-EDI-Status le résume.
  • Code HTTP204
    SignificationRien à répondre : l'échange ne contenait que des accusés CONTRL (on ne répond jamais à un accusé).
  • Code HTTP400
    SignificationPas de l'EDIFACT (not_edifact), corps vide (empty_body) ou enveloppe illisible (invalid_envelope).
  • Code HTTP401 / 403
    SignificationJeton absent ou invalide / liaison désactivée.
  • Code HTTP409
    SignificationCet échange est déjà en cours de traitement : réessayez dans quelques secondes (Retry-After).
  • Code HTTP413
    SignificationPlus de 1 Mo ou plus de 50 échanges dans la même requête.
  • Code HTTP429
    SignificationPlus de 60 échanges par minute ou 3 000 par jour : patientez (Retry-After).
  • Code HTTP503
    SignificationPanne passagère : RIEN n'a été enregistré, renvoyez le même échange (Retry-After).
  • Renvoi sans risque : le même échange (même référence de contrôle UNB 0020, même contenu) reçoit exactement le même accusé (en-tête X-EDI-Replay: true) et ne crée jamais une seconde demande.
  • Même numéro de document (BGM, 1004) dans un nouvel échange pendant 30 jours : considéré comme un renvoi, aucune seconde demande. Même référence d'échange avec un autre contenu : refus (erreur 26, doublon).
  • Les identifiants d'enveloppe doivent être ceux convenus : un autre expéditeur est refusé (erreur 23), un autre destinataire aussi (erreur 7). Le jeton seul ne suffit pas.
Exemple d'ordre de transport (IFTMIN)
UNA:+.? '
UNB+UNOC:3+EXEMPLEFOODS:ZZ+JLRTRANSPORT:ZZ+261010:0815+0000042++++1'
UNH+1+IFTMIN:D:96A:UN'
BGM+610+PO-2026-0457+9'
DTM+137:202610100815:203'
DTM+200:20261012:102'
DTM+2:20261014:102'
FTX+AAI+++Livraison avant 14 h, quai 3'
CNT+7:6800:KGM'
NAD+CZ+++EXEMPLE FOODS SAS+12 RUE DES ENTREPOTS+LYON++69007+FR'
CTA+IC+:Ana Martin'
COM+ana@exemple-foods.fr:EM'
COM+0472000000:TE'
NAD+CN+++MAGAZYN CENTRALNY SP. Z O.O.+UL. MAGAZYNOWA 5+POZNAN++61-001+PL'
GID+1+12:PX'
TMP+2+4:CEL'
FTX+AAA+++Produits laitiers frais'
MEA+WT+G+KGM:6800'
UNT+17+1'
UNZ+1+0000042'

Ce que nous lisons dans l'IFTMIN

Chaque IFTMIN valide devient une demande de transport visible par notre équipe. Voici les segments utilisés (les autres sont ignorés). Les qualifiants de lieux et les codes d'emballage peuvent être adaptés à votre usage lors de la mise en place.

  • SegmentBGM
    Ce que nous en faisons1004 = votre numéro de document, repris comme référence de la demande et renvoyé dans chaque IFTSTA (RFF+CU). 1225 = fonction : vide ou 9 (original) crée une demande ; 7 et 31 (doublon, copie) sont ignorés ; 1, 3, 4, 5 (annulation, suppression, modification, remplacement) sont signalés à notre équipe et traités à la main.
  • SegmentNAD
    Ce que nous en faisonsLieu d'enlèvement : qualifiant SF, PW ou CZ. Lieu de livraison : ST, DP ou CN. Nom (C080), rue (C059), ville (3164), code postal (3251) et pays (3207, 2 ou 3 lettres) sont repris. Ville et pays sont obligatoires.
  • SegmentLOC
    Ce que nous en faisonsRepli si aucun NAD exploitable : LOC+9 (enlèvement) et LOC+11 (livraison), UN/LOCODE.
  • SegmentCTA / COM
    Ce que nous en faisonsContact du lieu d'enlèvement : nom (CTA), e-mail (COM, EM) et téléphone (COM, TE). Sans e-mail, nous utilisons celui de votre compte.
  • SegmentGID
    Ce que nous en faisonsNombre de palettes : 7224 (nombre) + 7065 (type). Codes reconnus comme palettes : PX, PF, PB, PD, PE, PL, 200 à 206, EP, EUR, PAL (ou absence de type). Obligatoire, 100 au maximum. Repli : EQD/EQN puis CNT+16 ; des colis non palettisés sont comptés comme palettes avec un avertissement.
  • SegmentMEA / CNT
    Ce que nous en faisonsPoids brut : MEA+WT+G (KGM, TNE, GRM) par article, sinon CNT+7. Seuls 1 à 40 000 kg sont retenus (sinon avertissement).
  • SegmentTMP / RNG
    Ce que nous en faisonsTempérature (CEL ou FAH) → régime : jusqu'à −10 °C surgelé, jusqu'à +15 °C frais, au-delà sec. Sans température, nous appliquons le régime par défaut convenu (sec, sauf accord contraire) avec un avertissement.
  • SegmentDTM
    Ce que nous en faisonsDate d'enlèvement souhaitée : qualifiants 200, 234, 10, 11 ou 203. Date de livraison souhaitée : 2, 17, 64, 63, 69, 76 ou 71 (reprise dans le texte de la demande). Formats 101, 102, 201, 203, 204, 303.
  • SegmentDGS / FTX / RFF
    Ce que nous en faisonsDGS : marchandise dangereuse (ADR). FTX (AAI, DIN, SIN, LOI, ACB, DEL, SSR, TRR) et RFF (ON, SI, AAS, CR, CU, BN…) : repris dans le texte de la demande pour notre équipe.

Un message auquel il manque une donnée obligatoire (lieu, quantité) est refusé avec l'erreur 13 (donnée manquante) ou 12 (valeur invalide) : corrigez et renvoyez-le avec une nouvelle référence d'échange. Les écarts bénins (régime par défaut, colis pris pour des palettes, date passée…) ne bloquent rien : la demande est créée et l'avertissement est signalé à notre équipe.

Notre accusé CONTRL

UCI donne le verdict sur l'échange (7 = reconnu, 4 = refusé) ; un UCM suit pour chaque message, avec le code d'erreur (donnée 0085) et, s'il y a lieu, UCS / UCD qui désignent le segment et la donnée en cause. Un échange refusé en entier (enveloppe, identifiants) n'a qu'un UCI de refus.

Échange accepté
UNA:+.? '
UNB+UNOC:3+JLRTRANSPORT:ZZ+EXEMPLEFOODS:ZZ+261010:0815+1'
UNH+1+CONTRL:D:3:UN'
UCI+0000042+EXEMPLEFOODS:ZZ+JLRTRANSPORT:ZZ+7'
UCM+1+IFTMIN:D:96A:UN+7'
UNT+4+1'
UNZ+1+1'
Même ordre sans quantité (GID supprimé) : message refusé, erreur 13
UNA:+.? '
UNB+UNOC:3+JLRTRANSPORT:ZZ+EXEMPLEFOODS:ZZ+261010:0815+1'
UNH+1+CONTRL:D:3:UN'
UCI+0000042+EXEMPLEFOODS:ZZ+JLRTRANSPORT:ZZ+7'
UCM+1+IFTMIN:D:96A:UN+4+13'
UNT+4+1'
UNZ+1+1'
  • Code2
    SignificationVersion de syntaxe non gérée (3 ou 4 attendues).
  • Code7
    SignificationLe destinataire de l'échange n'est pas JLR Transport.
  • Code12
    SignificationValeur invalide (balise de segment, trop de palettes…).
  • Code13
    SignificationDonnée ou segment manquant (UNB incomplet, UNZ absent, lieu ou quantité manquants…).
  • Code17
    SignificationType de message non convenu.
  • Code22
    SignificationAvis UNA invalide.
  • Code23
    SignificationExpéditeur de l'échange inconnu.
  • Code26
    SignificationDoublon : référence d'échange déjà utilisée avec un autre contenu.
  • Code28
    SignificationRéférences non concordantes (UNH/UNT ou UNB/UNZ).
  • Code29
    SignificationCompteur erroné (segments d'UNT, messages d'UNZ).
  • Code32
    SignificationÉchange vide : aucun message.
  • Code33
    SignificationSegment en dehors d'un message.
  • Code35
    SignificationTrop de segments (50 000 au plus).
  • Code39
    SignificationDonnée trop longue (référence de contrôle de plus de 14 caractères).
  • Code46
    SignificationJeu de caractères non géré.

Comptes rendus d'état (IFTSTA)

À chaque évènement du transport, nous vous envoyons un échange IFTSTA D.01B : BGM (code 23 par défaut), DTM+137 (date du message), CNI (notre référence d'expédition), STS+1+<code> (code d'état, donnée 4405), RFF+CU (votre référence, celle du BGM de votre IFTMIN) et RFF+FF (notre référence), puis les dates utiles et, pour les étapes, un texte libre FTX+AAI. Les dates sont écrites dans le fuseau horaire convenu.

  • ÉvènementExpédition créée
    STS 440598
    Dates (DTM) et texte78 heure de l'évènement, 200 enlèvement prévu, 17 livraison estimée
  • ÉvènementEnlèvement confirmé
    STS 440513
    Dates (DTM) et texte78 heure d'enlèvement, 17 livraison estimée
  • ÉvènementLivraison confirmée
    STS 440521
    Dates (DTM) et texte78 et 35 heure de livraison
  • ÉvènementNouvelle heure d'arrivée
    STS 4405212
    Dates (DTM) et texte17 arrivée estimée, 64 / 63 fenêtre d'arrivée (au plus tôt / au plus tard)
  • ÉvènementRisque de retard / retard
    STS 440530
    Dates (DTM) et texte17 arrivée estimée ; FTX : « Possible delivery delay », « Delivery is late » ou « Shipment delayed »
  • ÉvènementNouvelle étape
    STS 440530
    Dates (DTM) et texteFTX : libellé de l'étape (« Douane passée »…)
Exemple : livraison confirmée
UNA:+.? '
UNB+UNOC:3+JLRTRANSPORT:ZZ+EXEMPLEFOODS:ZZ+261014:1141+57++++1'
UNH+57+IFTSTA:D:01B:UN'
BGM+23+57+9'
DTM+137:202610141141:203'
CNI+1+JLR-2026-0457'
STS+1+21'
RFF+CU:PO-2026-0457'
RFF+FF:JLR-2026-0457'
DTM+78:202610141141:203'
DTM+35:202610141141:203'
UNT+10+57'
UNZ+1+57'

Aucune identité de transporteur ni de chauffeur ne figure jamais dans ces messages. Les évènements sans équivalent EDI (ajout d'un document, changement de statut interne) ne donnent pas de message ; vous choisissez avec nous les évènements envoyés et si vous voulez les expéditions de toute l'entreprise ou seulement celles issues de vos IFTMIN.

Recevoir les comptes rendus

Deux modes au choix. Boîte à lettres : vous relevez vos messages quand vous voulez avec GET https://jlr-transport.com/api/edi/out/<jeton> (jusqu'à 50 par appel, ajoutez ?format=json pour du JSON). Les messages relevés sont marqués remis ; l'en-tête X-EDI-Message-Ids en donne les identifiants, que vous pouvez accuser par POST …/ack. Si une réponse s'est perdue, ?since=<date ISO> relit ce qui a été remis (7 jours au plus) sans rien modifier.

# Pick up the waiting status messages
curl "https://jlr-transport.com/api/edi/out/$JLR_EDI_TOKEN" -o status.edi -D headers.txt

# X-EDI-Message-Ids lists what was returned; acknowledge it once stored
curl -X POST "https://jlr-transport.com/api/edi/out/$JLR_EDI_TOKEN/ack" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["<id>", "<id>"]}'

Envoi vers vous : nous postons chaque IFTSTA en HTTPS (port 443 ou 8443, nom de domaine public, certificat valide) à l'adresse convenue, avec l'en-tête d'authentification de votre choix. Répondez 2xx pour accuser réception ; sinon nous réessayons après 1 min, 5 min, 30 min, 2 h, 6 h, 12 h puis 24 h (8 tentatives), puis notre équipe est alertée. Si votre réponse contient un CONTRL, nous le lisons et le rapprochons du message.

Sécurité et limites

  • HTTPS uniquement. Le jeton d'accès est secret, révocable à tout moment, et nous n'en conservons que l'empreinte.
  • Limites par liaison : 60 échanges par minute, 3 000 par jour, 1 Mo par requête, 50 échanges par requête.
  • Journal : chaque échange (reçu ou envoyé) est conservé 90 jours ; son contenu brut est effacé après 30 jours.
  • Les demandes créées par EDI portent le canal « edi » dans l'API (GET /v1/transport-requests) : vous les retrouvez avec les autres.
  • Pas encore de DESADV, d'ORDERS ni de messages de facturation : dites-nous ce dont vous avez besoin.

Erreurs

Les erreurs utilisent le code HTTP adapté et un corps JSON identique partout : « type » (famille), « code » (stable, à tester dans votre programme), « message » (pour un humain), « param » (le paramètre en cause, le cas échéant) et « request_id » (à nous communiquer en cas de question).

Exemple d'erreur
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "\"pallets\" must be between 1 and 100.",
    "param": "pallets",
    "request_id": "req_8d41a0c27b9e5f13",
    "doc_url": "https://jlr-transport.com/developpeurs#errors"
  }
}
Codes d'erreur
  • 400
    parameter_invalid

    Un paramètre est invalide (voir « param »).

  • 400
    parameter_missing

    Un paramètre obligatoire manque.

  • 400
    parameter_unknown

    Paramètre inconnu (souvent une faute de frappe).

  • 400
    invalid_json

    Le corps n'est pas un JSON valide.

  • 400
    cursor_invalid

    Le curseur de pagination n'est pas valide.

  • 401
    missing_api_key

    Aucune clé fournie.

  • 401
    invalid_api_key

    Clé inconnue ou mal formée.

  • 401
    api_key_revoked

    Cette clé a été révoquée.

  • 401
    api_key_expired

    Cette clé a expiré.

  • 403
    insufficient_scope

    La clé n'a pas le droit nécessaire.

  • 404
    resource_not_found

    Ressource introuvable (ou appartenant à une autre entreprise).

  • 409
    request_in_progress

    Une requête avec cette Idempotency-Key est en cours : réessayez.

  • 413
    payload_too_large

    Corps de requête trop volumineux (100 Ko maximum).

  • 415
    unsupported_media_type

    Envoyez le corps en JSON (Content-Type: application/json).

  • 422
    idempotency_key_reused

    Cette Idempotency-Key a déjà servi pour une autre requête.

  • 429
    rate_limited

    Trop de requêtes : attendez « Retry-After » secondes.

  • 500
    internal_error

    Erreur inattendue : contactez-nous avec le request_id.

  • 503
    service_unavailable

    Service momentanément indisponible : réessayez.

Pagination

Les listes sont paginées par curseur, du plus récent au plus ancien. Passez « limit » (1 à 100, 25 par défaut) ; tant que « has_more » vaut true, renvoyez la valeur de « next_cursor » dans « cursor » pour obtenir la page suivante. Le curseur est opaque : ne le fabriquez pas. Il reste fiable même si de nouvelles lignes arrivent pendant le parcours.

GET https://jlr-transport.com/api/v1/shipments?limit=2
→ { "data": [...], "has_more": true, "next_cursor": "eyJ0Ijoi..." }

GET https://jlr-transport.com/api/v1/shipments?limit=2&cursor=eyJ0Ijoi...

Idempotence

Pour toute création (POST), envoyez un en-tête « Idempotency-Key » unique par opération (un UUID, ou votre numéro de commande). Si la requête est renvoyée avec la même clé et le même corps dans les 24 heures — réseau coupé, nouvelle tentative automatique — vous recevez la réponse d'origine (en-tête « Idempotent-Replayed: true ») et rien n'est créé en double. La même clé avec un corps différent renvoie une erreur 422. Seules les créations réussies sont mémorisées : une requête refusée peut être corrigée puis renvoyée avec la même clé.

Limites de débit

Chaque clé peut faire 120 requêtes par minute ; les créations sont limitées à 30 par minute et 1 000 par jour. Chaque réponse indique « RateLimit-Limit », « RateLimit-Remaining » et « RateLimit-Reset » (secondes avant la remise à zéro). Au-delà, l'API répond 429 avec « Retry-After » : attendez ce délai avant de réessayer. Pour suivre de nombreuses expéditions, préférez les webhooks (ou GET /v1/events) à une interrogation répétée.

Versions

L'adresse contient la version (/v1). Nous ajoutons des champs et des évènements sans changer de version : ignorez les champs que vous ne connaissez pas. Tout changement incompatible fera l'objet d'une nouvelle version (/v2) et d'un préavis. Chaque réponse porte l'en-tête « JLR-API-Version ».

10 octobre 2026 — version 1 : expéditions, suivi (position et heure d'arrivée estimée conforme au règlement CE 561/2006), documents, évènements, demandes de transport, webhooks signés, mode test.

Besoin d'aide ?

Écrivez-nous en indiquant le request_id de l'appel concerné : nous retrouvons immédiatement ce qui s'est passé.

contact@jlr-transport.com