L’API Transport renvoie des erreurs au format standard RFC 9457 (« Problem Details for HTTP APIs »), avec le Content-Type application/problem+json. Vous obtenez ainsi un objet structuré, prévisible et identique sur toutes les routes — pas de message texte brut à parser.
Branchez toujours votre logique sur le code HTTP (status) et, quand il est présent, sur le code métier (code), jamais sur le texte de detail, qui peut évoluer sans préavis.

Structure d’une réponse d’erreur

Toute erreur partage la même enveloppe. Trois champs sont toujours présents (type, title, status) ; les autres sont contextuels.
string (uri-reference)
requis
URI identifiant le type de problème. Vaut about:blank par défaut (le code HTTP suffit alors à qualifier l’erreur).
string
requis
Libellé court du problème : nom du statut HTTP (ex. Not Found, Unprocessable Entity) ou de l’opération qui a échoué (ex. Create failed, Cancel failed).
integer
requis
Code de statut HTTP, dupliqué dans le corps pour faciliter le logging.
string
Message lisible spécifique à cette occurrence (ex. Transport not found.). À afficher ou logger, mais à ne jamais utiliser comme clé de branchement. En 422, il résume l’erreur ; le détail par champ est dans errors[].
string (uri-reference)
URI identifiant l’occurrence précise du problème, lorsqu’il est pertinent.
string
Code métier stable qui qualifie certaines erreurs (ex. leg_invoiced, no_return_leg, already_cancelled, not_cancellable, relay_timeout, request_in_progress, billing_entity_required). C’est ce champ, avec status, que votre intégration doit tester.
string
Présent sur les erreurs 500 : identifiant unique de l’incident. Une 500 ne contient aucun détail technique ; communiquez cette référence au support pour qu’il retrouve l’incident.
array
Détail des erreurs de validation, champ par champ. Présent surtout sur les réponses 422.

Codes HTTP

L’API utilise un ensemble restreint de codes, chacun avec une sémantique métier claire.

Réponses de succès

Un 207 n’est pas une erreur globale : inspectez data[] pour connaître le sort de chaque document (success, error).

Réponses d’erreur

Une mission qui n’appartient pas à votre groupe renvoie 404, jamais 403. C’est un choix de sécurité (mitigation BOLA) : l’API ne révèle pas l’existence d’une mission hors de votre périmètre. Un 404 ne signifie donc pas forcément que la mission n’existe pas — il peut s’agir d’une mission d’un autre groupe.

Cas particuliers à connaître

Votre clé API est liée à un groupe (flotte, concession, loueur). Toute mission dont le group_id ne correspond pas à celui de votre clé est traitée comme introuvable. Vérifiez que vous utilisez le bon id (alias offer_id) et la clé du bon groupe.
Un 409 couvre plusieurs situations. Le code, quand il est présent, précise laquelle :
  • Idempotence divergente (code: idempotency_key_reused) : la même Idempotency-Key réutilisée pour une requête différente — autre corps, autre route ou autre mission.
  • Requête encore en cours (code: request_in_progress) : une requête identique (même clé, même corps) est encore en traitement. Rien n’est recréé : attendez la durée de l’en-tête Retry-After, puis réessayez avec la même clé.
  • Issue inconnue (outcome_unknown: true, sans Retry-After, aussi possible dès le premier 502) : l’écriture a peut-être déjà été appliquée. Vérifiez la mission (GET /transports) ou contactez le support avant de la renvoyer.
  • Jambe déjà facturée ou payée (code: leg_invoiced) : refacturation ou option sur une jambe déjà payée ou facturée.
  • Mission non annulable : déjà annulée (code: already_cancelled), déjà partie ou terminée (code: not_cancellable).
Rejouer la même Idempotency-Key avec le même corps n’est jamais une erreur : la réponse mise en cache est renvoyée à l’identique, avec l’en-tête Idempotent-Replayed: true.
Le corps est syntaxiquement correct mais ne passe pas la validation métier (champ requis absent, valeur invalide). detail résume l’erreur en clair ; le tableau errors[] détaille chaque champ fautif — utilisez-le pour afficher des messages précis à l’utilisateur.Quelques code à connaître : documents_failed (aucun document déposé, résultat par document dans data, nouvel essai possible avec la même clé), no_return_leg (une jambe retour visée sur une mission sans trajet retour), billing_entity_required / billing_entity_not_allowed (entité de facturation introuvable ou non autorisée). Un paramètre since invalide renvoie aussi un 422, avec errors: [{ "field": "since", "error": "invalid" }].
Le débit est limité par IP, par clé et globalement. Un dépassement renvoie un 429 accompagné de l’en-tête Retry-After (en secondes). Mettez en place un back-off et patientez la durée indiquée avant de réessayer.
Une 500 ne contient aucun détail technique. Elle porte un champ reference : notez-le et communiquez-le au support (api@myexpressdriver.com), il permet de retrouver l’incident.
Les écritures (création, annulation, modifications, documents) passent par un service MED en aval. Le code précise la cause :
  • relay_timeout : le service n’a pas répondu à temps, mais l’opération a pu aboutir. Pour une création, réessayez avec la même Idempotency-Key et le même corps : si la mission a été créée, elle vous est renvoyée (201 identique) au lieu d’être créée une seconde fois. Sans clé, un nouvel essai peut créer un doublon : vérifiez d’abord vos missions (GET /transports).
  • relay_unavailable : service indisponible ou connexion coupée. Le plus souvent rien n’a été fait, mais une connexion coupée en cours de route a pu laisser l’écriture aboutir : relisez la mission (GET /transports/{id}) avant de rejouer une annulation ou une modification de dates ; pour une création, réessayez avec la même Idempotency-Key.
  • billing_lookup_unavailable (création) : l’entité de facturation n’a pas pu être résolue, les entités de facturation étant momentanément inaccessibles. Rien n’a été créé : rejouez la même requête (même Idempotency-Key).
  • billing_unavailable (refacturation, options) : l’état de facturation de la jambe n’a pas pu être vérifié. Rien n’a été modifié : réessayez dans un instant. Si cette vérification échoue dès l’entrée de l’API, la réponse est une 500 avec reference (sans code) : même conduite.
  • not_confirmed (dates, duplication et son aperçu) : l’écriture n’a pas été confirmée. Vérifiez la mission (ou vos missions, pour une duplication) avant de réessayer.
outcome_unknown: true peut accompagner un 502 dès la première réponse, quand l’écriture avait déjà commencé : ne rejouez pas automatiquement, vérifiez d’abord. Il n’accompagne jamais un relay_timeout.

Exemple complet

Une requête POST /transports à laquelle il manque l’objet billing :

Gestion côté client (Node.js)

L’exemple ci-dessous montre un wrapper minimal qui branche sa logique sur le code HTTP et le code métier, respecte Retry-After sur les 429 et les 409 request_in_progress, et ne retente un 502 que si une Idempotency-Key est posée (sans clé, un nouvel essai de création peut créer un doublon).
Points clés d’une intégration robuste : brancher sur status et code, lire errors[] sur les 422, respecter Retry-After sur les 429, conserver la reference des 500, et poser une Idempotency-Key sur chaque création pour pouvoir la rejouer sans doublon en cas de 502.

Étapes suivantes

Idempotence

Posez une Idempotency-Key pour rejouer les mutations sans effet de bord double, notamment après un 429 ou un 502.

Authentification

Comprenez les clés X-Api-Key, les scopes et l’isolation par groupe — la source des 401, 403 et 404.

Démarrage rapide

Créez votre première mission de bout en bout et observez les réponses de succès et d’erreur en conditions réelles.