Aller au contenu

Codes d’erreur

Toutes les erreurs de l’API Sangho retournent un objet JSON structuré et plat (aucune clé imbriquée) avec un code d’erreur métier, un message lisible et un lien direct vers la section de cette page qui l’explique. Cliquer sur un lien doc_url reçu depuis votre console ou vos logs vous amène ici et surligne automatiquement la ligne du code concerné.

Structure d’une erreur

ChampTypeRequisDescription
typestringRequisCatégorie de l'erreur — une des 7 valeurs listées ci-dessous. Utile pour un traitement générique (afficher un message par catégorie).
codestringRequisCode métier Sangho précis — ex. AMOUNT_TOO_SMALL. C'est la source de vérité pour la gestion fine des cas d'erreur, à préférer à type et au code HTTP.
messagestringRequisDescription lisible de l'erreur, en français
statusintegerRequisCode HTTP associé — 400, 401, 403, 404, 409, 422, 429, 500
doc_urlstringOptionnelLien direct vers la ligne de cette page correspondant au code — ex. https://docs.sangho.ga/errors#AMOUNT_TOO_SMALL
paramstringOptionnelChamp en cause pour les erreurs de validation (422)
errorsobjectOptionnelDétail par champ pour les erreurs de validation multi-champs — {`{ "amount": ["..."] }`}
request_idstringOptionnelIdentifiant de la requête, à fournir au support en cas de besoin

Les 7 catégories (type)

Chaque erreur appartient à l’une de ces 7 catégories. Le SDK JavaScript expose la même taxonomie sur err.type — pratique pour un traitement générique par branche (retry, redirection login, etc.) avant de descendre au niveau du code précis si besoin.

typeStatuts HTTP typiquesSignification
AUTHENTICATION_ERROR401Clé API absente, invalide ou expirée
PERMISSION_ERROR403Identité reconnue, mais action non autorisée (clé publique, compte restreint, plan insuffisant…)
NOT_FOUND_ERROR404Ressource introuvable
CONFLICT_ERROR409Conflit — idempotency key réutilisée, doublon
VALIDATION_ERROR422Requête syntaxiquement correcte mais métier-invalide
RATE_LIMIT_ERROR429Quota d'appels dépassé
API_ERROR500Erreur côté Sangho

Codes HTTP standards

Le code HTTP indique la catégorie de l’erreur. Utilisez-le pour orienter votre logique de retry et d’affichage.

CodeDescription
200Succès — requête traitée
201Ressource créée avec succès
400Requête invalide — JSON malformé ou en-têtes incorrects
401Clé API manquante, expirée ou invalide
403Permission insuffisante — l'App n'a pas accès à cette ressource ou cette action
404Ressource introuvable — ID incorrect ou ressource supprimée
409Conflit — idempotency key réutilisée avec un corps de requête différent
422Validation métier échouée — règle business non respectée
429Trop de requêtes — rate limiting atteint, appliquer un backoff
500Erreur interne Sangho — réessayez avec un backoff exponentiel

Codes métiers

Tous les codes suivent le standard SCREAMING_SNAKE_CASE, sans préfixe. Branchez vos gestionnaires d’erreurs sur code plutôt que sur le statut HTTP — le code est la source de vérité.

Authentification — 401

CodeDescription
MISSING_API_KEYClé API absente — fournissez-la via Authorization: Bearer <clé>
INVALID_API_KEYClé API invalide, révoquée ou mal formée
EXPIRED_API_KEYClé API expirée — générez-en une nouvelle depuis votre dashboard
INVALID_TOKENToken JWT invalide ou expiré
AUTHENTICATION_FAILEDÉchec d'authentification générique — vérifiez le header Authorization

Autorisation — 403

Ces erreurs sont retournées quand l’identité est reconnue mais que l’opération n’est pas autorisée pour cette clé, ce compte ou ce plan.

CodeDescription
PERMISSION_DENIEDPermission refusée — votre clé n'est pas autorisée à effectuer cette action sur cette ressource
PUBLIC_KEY_NOT_ALLOWEDCette route requiert une clé secrète (sk_prod_ ou sk_test_). N'exposez jamais votre clé secrète côté client
APP_DISABLEDL'application est désactivée — réactivez-la depuis votre dashboard
APP_NOT_VERIFIEDApplication non vérifiée — KYC en attente d'approbation
INSUFFICIENT_PERMISSIONSPermissions insuffisantes pour cette ressource ou action
SANDBOX_ONLYCette route est réservée à l'environnement sandbox — utilisez une clé sk_test_
CURRENCY_NOT_IN_PLANLa devise demandée est active sur Sangho mais n'est pas incluse dans votre plan actuel — passez à un plan supérieur pour l'activer
COMPANY_BLACKLISTEDCe compte a été mis en liste noire et n'a plus accès à la plateforme — contactez le support
COMPANY_PAYMENTS_DISABLEDLes paiements entrants sont actuellement désactivés pour ce compte
COMPANY_PAYOUTS_DISABLEDLes virements sortants sont actuellement désactivés pour ce compte
COMPANY_SANDBOX_ONLYCe compte est restreint au mode test (sandbox) pour le moment
COMPANY_KYC_REQUIREDLa vérification KYC est requise avant de pouvoir traiter des paiements
Attention

Le code PERMISSION_DENIED est notamment retourné par POST /v1/reset lorsqu’elle est appelée avec une clé de production (sk_prod_). Cette route efface toutes les données de l’application et n’est accessible qu’en sandbox. Utilisez une clé sk_test_ pour y accéder.

Ressource introuvable — 404

CodeDescription
NOT_FOUNDRessource introuvable — ID incorrect, ressource supprimée ou accès interdit
CUSTOMER_NOT_FOUNDAucun Customer avec cet identifiant pour cette application
PRODUCT_NOT_FOUNDAucun Product avec cet identifiant
PAYMENT_INTENT_NOT_FOUNDAucun PaymentIntent avec cet identifiant
TRANSACTION_NOT_FOUNDAucune Transaction avec cet identifiant
REFUND_NOT_FOUNDAucun Refund avec cet identifiant
INVOICE_NOT_FOUNDAucune Invoice avec cet identifiant
SUBSCRIPTION_NOT_FOUNDAucune Subscription avec cet identifiant
CHECKOUT_SESSION_NOT_FOUNDAucune CheckoutSession avec cet identifiant
PAYMENT_METHOD_NOT_FOUNDAucun PaymentMethod avec cet identifiant
RECEIPT_NOT_FOUNDAucun Receipt avec cet identifiant
WEBHOOK_NOT_FOUNDAucun Webhook avec cet identifiant
SEARCH_NO_RESULTSAucun résultat pour la recherche — vérifiez le paramètre ?search=

Conflit — 409

CodeDescription
IDEMPOTENCY_CONFLICTUne requête avec la même Idempotency-Key a déjà été traitée avec un corps de requête différent
DUPLICATE_EMAILUn Customer avec cet email existe déjà pour cette application
DUPLICATE_SKUUn Product avec ce SKU existe déjà

Validation métier — 422

CodeDescription
VALIDATION_ERRORLes données fournies sont invalides — consultez le champ errors pour le détail par champ
MISSING_FIELDUn champ obligatoire est absent de la requête
INVALID_FIELDLa valeur d'un champ est incorrecte ou hors des valeurs acceptées
INVALID_FILTER_PARAMUn paramètre de filtre (?param=) ne correspond à aucun champ du modèle
INVALID_CURRENCYLa devise fournie n'est pas un code ISO 4217 reconnu, ou n'est pas activée sur Sangho — voir devises supportées
AMOUNT_TOO_SMALLMontant inférieur au minimum autorisé (100 XAF par défaut)
AMOUNT_TOO_LARGEMontant supérieur au plafond autorisé
INVALID_STATUS_TRANSITIONLa transition de statut demandée est interdite (ex. rouvrir une Invoice annulée)
PAYMENT_INTENT_UNCANCELABLEImpossible d'annuler ce PaymentIntent dans son statut actuel
PAYMENT_INTENT_NOT_CONFIRMABLEImpossible de confirmer ce PaymentIntent dans son statut actuel
PAYMENT_INTENT_NOT_CAPTURABLEImpossible de capturer — le statut doit être requires_capture
CAPTURE_AMOUNT_EXCEEDS_ORIGINALLe montant à capturer dépasse le montant original du PaymentIntent
CHECKOUT_SESSION_EXPIREDLa session de checkout a expiré — demandez au client d'en démarrer une nouvelle
SUBSCRIPTION_ALREADY_CANCELEDCette Subscription est déjà annulée et ne peut pas l'être à nouveau
INVOICE_NOT_DRAFTABLECette Invoice n'est pas dans un état permettant le retour en brouillon

Rate limiting — 429

CodeDescription
RATE_LIMIT_EXCEEDEDQuota d'appels API dépassé — consultez le header Retry-After pour la durée d'attente
TOO_MANY_REQUESTSTrop de requêtes en simultané depuis cette clé API

Paiements & Mobile Money

CodeDescription
INSUFFICIENT_FUNDSFonds insuffisants côté acheteur (Mobile Money ou carte)
PAYMENT_METHOD_NOT_SUPPORTEDMéthode de paiement non activée pour ce compte marchand
MOBILE_MONEY_TIMEOUTL'acheteur n'a pas confirmé le paiement dans le délai imparti (généralement 3 min)
MOBILE_MONEY_REJECTEDL'acheteur a refusé la demande de paiement sur son téléphone
MOBILE_MONEY_NUMBER_INVALIDNuméro Mobile Money incorrect ou non enregistré chez l'opérateur
MOBILE_MONEY_LIMIT_REACHEDPlafond journalier ou mensuel de l'opérateur atteint pour ce numéro
CUSTOMER_BLACKLISTEDLe Customer est dans la liste noire — aucun paiement autorisé

Webhooks & intégrations

CodeDescription
WEBHOOK_SIGNATURE_INVALIDSignature HMAC-SHA256 invalide — vérifiez votre webhook secret
WEBHOOK_ENDPOINT_UNREACHABLEL'URL webhook ne répond pas — Sangho retentera selon la politique de retry
TERMINAL_OFFLINELe terminal Sangho n'est pas joignable — vérifiez la connexion réseau
PDF_NOT_AVAILABLELe PDF de ce document n'est pas encore généré — réessayez dans quelques instants
METHOD_NOT_ALLOWEDMéthode HTTP non autorisée sur cet endpoint

Erreur serveur — 500

CodeDescription
INTERNAL_ERRORErreur interne Sangho — notre équipe a été notifiée. Réessayez avec un backoff exponentiel
DATABASE_ERRORErreur de base de données — temporaire, réessayez dans quelques instants
UPSTREAM_ERRORErreur d'un service tiers (opérateur, passerelle) — réessayez ou contactez le support

Stratégie de retry recommandée

Toutes les erreurs ne se valent pas. Le SDK JavaScript applique déjà cette logique automatiquement (voir SDK JavaScript) — utile à connaître si vous appelez l’API directement.

Code HTTPAction recommandée
400 / 422Ne pas réessayer — corriger les paramètres de la requête
401 / 403Ne pas réessayer — vérifier la clé API, les permissions ou le plan
404Ne pas réessayer — vérifier l'ID de la ressource
409Récupérer la réponse idempotente déjà produite — ne pas créer de doublon
429Attendre la durée indiquée dans le header Retry-After, puis réessayer
500Réessayer avec backoff exponentiel (1s → 2s → 4s → 8s, max 3 tentatives)
Note

Le header Retry-After est inclus dans toutes les réponses 429. Sa valeur est exprimée en secondes.

Structure d’une erreur

ResponseRéponse — AMOUNT_TOO_SMALL
json
{
  "status": 422,
  "type": "VALIDATION_ERROR",
  "code": "AMOUNT_TOO_SMALL",
  "message": "Le montant est trop faible. Le minimum est de 100 XAF.",
  "param": "amount",
  "doc_url": "https://docs.sangho.ga/errors#AMOUNT_TOO_SMALL",
  "request_id": "req_5d0cc04ea0664a50a14a247f"
}

Devise hors plan (403)

ResponseRéponse — CURRENCY_NOT_IN_PLAN
json
{
  "status": 403,
  "type": "PERMISSION_ERROR",
  "code": "CURRENCY_NOT_IN_PLAN",
  "message": "La devise 'USD' n'est pas incluse dans votre plan actuel.",
  "doc_url": "https://docs.sangho.ga/errors#CURRENCY_NOT_IN_PLAN",
  "request_id": "req_5d0cc04ea0664a50a14a247f"
}

Accès refusé — sandbox uniquement (403)

ResponseRéponse — PERMISSION_DENIED
json
{
  "status": 403,
  "type": "PERMISSION_ERROR",
  "code": "PERMISSION_DENIED",
  "message": "Le reset est uniquement disponible en environnement sandbox. Utilisez une clé sandbox (sk_test_*) pour accéder à cette route.",
  "doc_url": "https://docs.sangho.ga/errors#PERMISSION_DENIED",
  "request_id": "req_5d0cc04ea0664a50a14a247f"
}

Idempotency-Key réutilisée (409)

ResponseRéponse — IDEMPOTENCY_CONFLICT
json
{
  "status": 409,
  "type": "CONFLICT_ERROR",
  "code": "IDEMPOTENCY_CONFLICT",
  "message": "Une requête avec la même clé d'idempotence est déjà en cours ou a déjà produit un résultat différent.",
  "doc_url": "https://docs.sangho.ga/errors#IDEMPOTENCY_CONFLICT",
  "request_id": "req_9a1bc23de4567f89b01c234d"
}

Erreur de validation (422)

ResponseRéponse — VALIDATION_ERROR
json
{
  "status": 422,
  "type": "VALIDATION_ERROR",
  "code": "VALIDATION_ERROR",
  "message": "L'email est requis.",
  "errors": {
    "email": ["Ce champ est obligatoire."],
    "amount": ["La valeur doit être un entier positif."]
  },
  "doc_url": "https://docs.sangho.ga/errors#VALIDATION_ERROR",
  "request_id": "req_9a1bc23de4567f89b01c234d"
}

Rate limit atteint (429)

ResponseRéponse — RATE_LIMIT_EXCEEDED
json
{
  "status": 429,
  "type": "RATE_LIMIT_ERROR",
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Limite de requêtes atteinte. Réessayez dans 60 secondes.",
  "retry_after": 60,
  "doc_url": "https://docs.sangho.ga/errors#RATE_LIMIT_EXCEEDED",
  "request_id": "req_2f4a8b1c9e3d5f70a0b2c4d6"
}

Ressource introuvable (404)

ResponseRéponse — CUSTOMER_NOT_FOUND
json
{
  "status": 404,
  "type": "NOT_FOUND_ERROR",
  "code": "CUSTOMER_NOT_FOUND",
  "message": "Aucun(e) Customer avec l'identifiant 'cust_xyz123'.",
  "doc_url": "https://docs.sangho.ga/errors#CUSTOMER_NOT_FOUND",
  "request_id": "req_7c3e5a2d1b8f4096e5d7c9b1"
}

Gestion des erreurs — SDKs

bash
# Les erreurs HTTP retournent toujours un JSON plat (aucune clé imbriquée)
# Capturez le status HTTP et lisez le champ "code" directement
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" \
  -X POST https://api.sangho.ga/v1/payment-intents/ \
  -H "Authorization: Bearer sk_test_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"amount": -1, "currency": "XAF"}')


if [ "$HTTP_STATUS" != "200" ] && [ "$HTTP_STATUS" != "201" ]; then
  ERROR_CODE=$(cat response.json | python3 -c "import sys,json; print(json.load(sys.stdin)['code'])")
  echo "Erreur: $ERROR_CODE (HTTP $HTTP_STATUS)"
fi