Gestion des erreurs
Forme de la réponse d'erreur, codes courants et stratégie de retry.
Chaque réponse d'erreur retournée par l'API suit la même forme. Les codes sont stables et machine-readable ; les messages sont human-readable et peuvent changer de formulation entre versions.
Forme de la réponse
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed.",
"details": [
{
"path": ["amount"],
"message": "Number must be greater than 0"
}
]
},
"meta": {
"timestamp": "2026-05-03T10:23:11.000Z"
}
}| Champ | Type | Notes |
|---|---|---|
success | false | Toujours false sur les erreurs. |
error.code | string | Enum stable — basez votre logique client sur ce champ. |
error.message | string | Human-readable. Adapté aux logs, pas aux end-users. |
error.details | unknown | Contexte libre. Pour VALIDATION_FAILED, un tableau d'issues Zod. |
meta.timestamp | string | ISO-8601 UTC. Utile pour les tickets support. |
Le code HTTP miroir la catégorie d'échec (4xx pour erreurs client, 5xx pour erreurs serveur). error.code affine davantage.
Codes courants
| HTTP | error.code | Quand vous le verrez |
|---|---|---|
| 400 | VALIDATION_FAILED | Le body ou les query params ne matchent pas le schema. details liste les problèmes par champ. |
| 400 | INVALID_REQUEST | Body unparseable, ou shape de requête fondamentalement mauvaise (content-type manquant, etc.). |
| 401 | UNAUTHORIZED | Clé API manquante, mal formée ou révoquée. |
| 403 | PERMISSION_DENIED | Authentifié mais pas autorisé (mauvais compte, marchand verrouillé, abonnement expiré). |
| 404 | NOT_FOUND | L'id de ressource n'existe pas ou n'appartient pas à votre compte. |
| 409 | ALREADY_EXISTS | Création dupliquée (ex. un affilié avec le même email existe déjà dans cette campagne). |
| 429 | RATE_LIMIT_EXCEEDED | Limite par IP ou par compte atteinte. Respectez Retry-After. |
| 429 | LIMIT_EXCEEDED | Quota du tier d'abonnement atteint (ex. cap mensuel de conversions). Upgrade ou attente du reset. |
| 500 | SERVER_ERROR | Échec upstream générique. Retry avec backoff. |
| 503 | SERVICE_UNAVAILABLE | Un service downstream (provider de paiement, queue) est dégradé. Retry. |
La liste complète est dans la spec OpenAPI sous components.schemas.ApiErrorResponse.properties.error.code.enum.
Stratégie de retry
Pas de retry sur les 4xx
Les réponses 4xx (sauf 429) signifient que la requête elle-même est mauvaise. Retry sans changer le payload échouera à nouveau et grignote votre quota de rate limit. Corrigez l'appel, pas la boucle de retry.
Un client robuste distingue trois cas :
- Erreur client terminale (
4xxsauf429) — votre payload est mauvais. NE PAS retry. Logguez, corrigez le call site. - Erreur serveur transitoire (
5xx) — RefCampaign upstream est en difficulté. Retry avec backoff exponentiel (1s, 2s, 4s, 8s) jusqu'à environ 5 tentatives. - Rate limited (
429) — backoff selon le headerRetry-After. Le serveur vous dit quand revenir.
Pseudocode :
async function callWithRetry(fetcher, maxAttempts = 5) {
let attempt = 0
while (attempt < maxAttempts) {
const res = await fetcher()
if (res.ok) return res
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get('retry-after') ?? '5', 10)
await sleep(retryAfter * 1000)
attempt++
continue
}
if (res.status >= 500) {
await sleep(Math.min(2 ** attempt * 1000, 30_000))
attempt++
continue
}
throw await errorFromResponse(res) // 4xx — terminal
}
throw new Error('Exhausted retries')
}Le SDK officiel @refcampaign/sdk gère tout cela pour vous.
Erreurs de validation
VALIDATION_FAILED renvoie error.details comme un tableau d'issues Zod :
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Validation failed.",
"details": [
{ "path": ["currency"], "code": "invalid_string", "message": "currency must be a 3-letter ISO code" },
{ "path": ["amount"], "code": "too_small", "message": "Number must be greater than 0" }
]
}
}Le tableau path est le chemin pointé dans votre body de requête ou query params. Affichez ces données dans votre UI / logs pour pointer l'utilisateur sur le champ exact.
Idempotence et gestion des erreurs de duplication
Certains endpoints sont idempotents sur une clé (POST /api/v1/conversions/track sur sessionId dans une fenêtre de cinq minutes — voir postback). Si votre logique de retry déclenche un duplicate, vous obtenez la même réponse de succès, pas un 409. 409 ALREADY_EXISTS est réservé aux vraies tentatives de création dupliquées (ex. inscrire le même email à la même campagne deux fois).
Quand alerter
Considérez ces situations comme dignes d'astreinte :
- Tout taux de
5xxsoutenu au-dessus de ~1% sur 5 min — problème côté RefCampaign. - Tout
429 RATE_LIMIT_EXCEEDEDinattendu — la forme de votre trafic a changé ; à investiguer. 403 PERMISSION_DENIEDen dehors des paths attendus — le statut de votre abonnement a peut-être changé.
401 UNAUTHORIZED après un déploiement signifie presque toujours que la clé API n'a pas été propagée au nouvel environnement.