Erreurs et idempotence
Format des erreurs
Toute erreur a la même forme, quel que soit l'endpoint :
| Champ | Contrat |
|---|---|
code | stable. Branchez votre logique dessus. |
message | français, destiné à l'affichage. Peut changer sans préavis — ne le parsez jamais. |
details | présent sur les erreurs de validation : liste de { field, messages[] }. |
Une validation échouée détaille chaque champ fautif, et tous les motifs pour chacun :
Une gestion correcte, côté client :
Code
Code
Codes HTTP
| Code | Signification |
|---|---|
400 | requête malformée ou en-tête obligatoire manquant |
401 | clé absente, invalide, expirée ou révoquée |
403 | authentifié, mais hors périmètre, hors droits, ou IP non autorisée |
404 | ressource inexistante, ou fonctionnalité non activée pour vous |
409 | conflit — unicité violée, ou rejeu avec un corps différent |
422 | règle métier violée : plafond dépassé, rôle hors liste blanche, validation échouée |
429 | limite de débit dépassée |
5xx | erreur serveur |
La distinction 400 / 422 est utile : 400 signifie « votre requête est mal formée », 422 signifie « votre requête est correcte mais l'opération est refusée ». Un plafond dépassé est un 422 — inutile de retenter à l'identique.
Idempotence
Toute mutation exige l'en-tête X-Idempotency-Key : un UUID que vous générez.
Ce que fait un second appel
Le premier appel enregistre la demande :
Le même appel rejoué avec la même clé et le même corps ne crée pas de seconde demande : il renvoie la réponse mémorisée, à l'identique — même approvalId, même requestedAt — accompagnée de l'en-tête X-Idempotent-Replay: true.
La même clé avec un corps différent est en revanche un bug côté appelant, et l'API refuse de trancher à votre place :
Et sans l'en-tête du tout :
La clé est conservée 24 h.
Une clé par opération métier, pas par tentative
Générez la clé au moment où l'opération est décidée — pas au moment de l'envoi — et réutilisez-la pour toutes les tentatives réseau de cette opération.
Générer une nouvelle clé à chaque retry annule toute la protection : chaque tentative devient une opération distincte, et un versement de 500 000 FCFA déclaré trois fois donne trois demandes.
Code
Code
Deuxième couche, côté wallet
Indépendamment de l'en-tête HTTP, chaque écriture au grand livre porte une clé dérivée du wallet et de la référence du versement, sous contrainte d'unicité en base. Une même clientReference sur un même wallet ne peut donc pas produire deux écritures, même si l'en-tête a été mal géré.
Cette référence est préfixée côté serveur par votre identifiant d'apporteur : deux partenaires utilisant la même référence sur un même wallet n'entrent pas en collision.
Retenter, ou pas
| Code | Retenter ? |
|---|---|
429, 5xx | oui — backoff exponentiel, même clé d'idempotence |
408, coupure réseau, délai dépassé | oui — même clé : c'est précisément le cas qu'elle couvre |
401 | non, sauf si vous venez de faire tourner la clé |
400, 403, 404, 409, 422 | non — un rejeu à l'identique produira le même résultat |
Le silence n'est pas un échec
Si la connexion tombe avant la réponse, l'opération a peut-être abouti. Ne la considérez ni comme réussie ni comme échouée : rejouez-la avec la même clé d'idempotence. Vous récupérerez la réponse mémorisée si elle avait abouti, et l'opération sera exécutée sinon.
403 ou 404 ?
Les deux existent et ne veulent pas dire la même chose :
403— la ressource est identifiée et se trouve hors de votre périmètre.404— la ressource n'existe pas, ou le périmètre est appliqué directement dans la requête, ce qui rend les deux cas indiscernables.
Un 404 sur un identifiant que vous croyez valide signifie donc en général qu'il appartient à quelqu'un d'autre. Voir Périmètre.
Fonctionnalités non activées
Un module non ouvert pour votre compte répond 404 FEATURE_DISABLED, pas 403. Tant qu'une fonctionnalité n'est pas activée, elle est invisible : vous ne pouvez pas déduire son existence en sondant l'API.
Si une opération documentée ici renvoie ce code, c'est qu'elle doit être activée côté Instafuel pour votre compte.
