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
Cas particuliers à connaître
404 — ressource d'un autre groupe ou inexistante
404 — ressource d'un autre groupe ou inexistante
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.409 — conflit d'idempotence ou conflit métier
409 — conflit d'idempotence ou conflit métier
Un
409 couvre plusieurs situations. Le code, quand il est présent, précise laquelle :- Idempotence divergente (
code: idempotency_key_reused) : la mêmeIdempotency-Keyré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êteRetry-After, puis réessayez avec la même clé. - Issue inconnue (
outcome_unknown: true, sansRetry-After, aussi possible dès le premier502) : 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).
422 — validation du corps de requête
422 — validation du corps de requête
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" }].429 — limite de débit (rate-limit)
429 — limite de débit (rate-limit)
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.500 — erreur interne
500 — erreur interne
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.502 — échec d'un service en aval
502 — échec d'un service en aval
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êmeIdempotency-Keyet le même corps : si la mission a été créée, elle vous est renvoyée (201identique) 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êmeIdempotency-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êmeIdempotency-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 une500avecreference(sanscode) : 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êtePOST /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 lecode 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.
