Facturino / Documentation / Gestion des erreurs

Gestion des erreurs

L'API Facturino utilise des codes HTTP standards et retourne systématiquement un objet error structuré. Chaque erreur contient un identifiant de requête (request_id) à fournir au support pour toute investigation.

Format de réponse

Toutes les erreurs partagent la même enveloppe — un seul objet error en racine, jamais d'autres champs frères. Inspiré du modèle Stripe pour faciliter l'intégration.

{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_required_field",
    "message": "Field 'customerId' is required.",
    "param": "customerId",
    "doc_url": "https://facturino.com/docs/errors#missing_required_field",
    "hint": "Either provide a customerId or inline a 'buyer' object.",
    "request_id": "req_8a3b9f2e1d"
  }
}
ChampDescription
typeCatégorie de l'erreur (voir tableau ci-dessous)
codeCode machine-readable pour le branchement (ex. missing_required_field)
messageMessage en clair, lisible pour un humain (ne pas afficher tel quel aux utilisateurs finaux)
paramOptionnel — Nom du paramètre invalide quand applicable
doc_urlOptionnel — Lien vers la documentation détaillée pour ce code
hintOptionnel — Suggestion d'action concrète
request_idIdentifiant unique de la requête (préfixe req_) — à fournir au support
issuesOptionnel — Raisons détaillées quand un même refus en porte plusieurs (voir Détail des refus)

Détail des refus

Un refus porte toujours un code principal — c'est sur lui qu'une intégration branche, et il ne change pas. Quand ce refus repose sur plusieurs raisons, ou sur une raison plus précise que le code principal, l'enveloppe porte en plus un tableau issues.

Le champ est facultatif et additif : code, param, message et hint gardent exactement leur sens, et param continue de désigner le premier champ en cause. La clé est absente — jamais un tableau vide — quand le refus n'a rien de plus à dire. Un refus publie au plus 500 raisons, chaque message au plus 2 000 caractères : les mêmes bornes s'appliquent à la première réponse et à son rejeu par la même Idempotency-Key.

{
  "error": {
    "type": "validation_error",
    "code": "validation_error",
    "message": "Buyer territory could not be resolved (invalid_postal_code)",
    "param": "customerId",
    "request_id": "req_8a3b9f2e1d",
    "issues": [
      {
        "code": "invalid_postal_code",
        "param": "customer.address.postalCode",
        "message": "Buyer territory could not be resolved (invalid_postal_code)"
      }
    ]
  }
}
ChampDescription
codeCode stable, sur lequel vous pouvez brancher. Plus précis que le code principal quand celui-ci recouvre plusieurs faits.
paramChamp en cause (chemin pointé), ou null quand la raison n'en nomme aucun.
messageRaison lisible, en anglais. Ne cite jamais une donnée décrivant une personne ou une adresse (nom, adresse, code postal, numéro), ni un texte VIES, ni une donnée d'un tiers ; une date, un code, une référence de ligne, un pays de destination, un identifiant de profil ou un montant déclaré par le vendeur peuvent être nommés.

param n'est renseigné que lorsque la correspondance vaut pour toute requête : un pointeur approximatif enverrait corriger un champ qui était juste.

Code de détailparamSignification
invalid_postal_codecustomer.address.postalCodeLe code postal du client n'est pas un code postal de son pays.
french_postal_code_required, postal_code_required_for_territory_checkcustomer.address.postalCodeLe pays compte plusieurs territoires de TVA : le code postal est requis pour désigner le bon.
unknown_french_postal_territory, unknown_regional_postal_territorycustomer.address.postalCodeLe code postal ne correspond à aucun territoire connu de ce pays.
unknown_country_codecustomer.address.countryLe pays n'est pas un code ISO 3166-1 alpha-2 connu.
territory_conflictcustomer.address.countryLe pays et le code postal désignent deux territoires différents.
Raison portée par une lignelines[<index>]La position de la ligne dans le tableau lines de votre requête.
Toute autre raisonnullLa raison ne nomme aucun champ ; lisez son code et son message.

Les raisons proviennent du moteur de décision fiscale : territoire client non résolu, preuves de localisation insuffisantes, refus de décision. Elles sont jointes aux refus de POST /v1/tax-decisions, le seul endpoint qui prend une décision. Les documents (factures, avoirs, devis, factures récurrentes) s'adossent à une décision existante par son identifiant et ne produisent pas de raisons détaillées.

Codes HTTP

CodeSignificationAction
200 / 201SuccèsContinuer
202Accepté (job asynchrone)Suivre l'id du job via GET /v1/jobs/:id
204Succès, pas de contenuContinuer
400Requête invalide (validation, format)Corriger le payload, ne pas retenter
401Authentification manquante ou invalideVérifier la clé API
402Paiement requis (plan insuffisant)Mettre à niveau le plan
403Action interdite (scope, transition d'état)Vérifier permissions et statut
404Ressource introuvableVérifier l'identifiant
409Conflit (idempotency-key réutilisée, version)Voir error.code
422Donnée invalide après validation (SIRET inconnu, TVA invalide)Corriger le payload
429Trop de requêtesRetentez après le délai indiqué dans Retry-After
500Erreur serveurRetentez avec backoff exponentiel
503Service indisponible (PA injoignable, maintenance)Retentez après quelques secondes

Types d'erreur

Le champ error.type classifie l'erreur — utilisez-le comme premier discriminant dans votre code. Liste exhaustive :

TypeStatut HTTPDescription
invalid_request_error400Le payload est mal formé, un champ requis manque, une valeur est invalide.
validation_error422Schéma Zod refusé — voir error.param pour le champ fautif.
authentication_error401Clé API absente, invalide, révoquée ou expirée.
permission_error403La clé n'a pas le scope requis pour cette opération.
not_found_error404La ressource n'existe pas, ou n'est pas accessible avec cette clé (livemode mismatch).
conflict_error409Conflit (transition d'état impossible, ressource déjà finalisée).
rate_limit_error429Limite de requêtes dépassée — consulter Retry-After.
plan_limit_error402Plan insuffisant ou quota dépassé (factures/mois, taille storage…).
api_error500Erreur serveur — Facturino est notifié automatiquement.

Codes d'erreur fréquents

Le champ error.code est le discriminant fin sous le type. Le code est aussi le fragment du doc_url retourné dans la réponse (https://facturino.com/docs/errors#<code>).

CodeTypeCause
idempotency_errorconflict_errorIdentifiant historique conservé dans le catalogue. Le serveur renvoie conflict pour une clé d'idempotence réutilisée avec un contenu différent.
brute_force_blockedauthentication_errorAuthentification temporairement bloquée après trop de tentatives. Attendez avant de réessayer.
pa_operation_not_supportedinvalid_request_errorLa plateforme connectée ne prend pas en charge cette opération — 422. Vérifiez les opérations proposées par le connecteur.
missing_required_fieldinvalid_request_errorUn champ obligatoire est absent (voir error.param).
invalid_field_valueinvalid_request_errorValeur invalide (date hors plage, code pays inconnu, livemode mismatch…).
invalid_content_typeinvalid_request_errorLe header Content-Type doit être application/json.
validation_errorvalidation_errorSchéma Zod refusé : SIRET invalide, format date, longueur dépassée, valeur hors enum.
invalid_api_keyauthentication_errorClé inconnue ou mal formée.
api_key_revokedauthentication_errorLa clé a été révoquée depuis le dashboard.
api_key_expiredauthentication_errorLa clé a dépassé sa date d'expiration optionnelle.
missing_api_keyauthentication_errorHeader Authorization absent ou mal formé.
captcha_requiredauthentication_errorTrop de tentatives — résoudre le captcha avant de retenter.
scope_insufficientpermission_errorLa clé n'a pas le scope nécessaire pour cette opération.
not_foundnot_found_errorLa ressource n'existe pas ou n'est pas dans votre environnement (fac_test_ vs fac_live_).
conflictconflict_errorMême Idempotency-Key avec un contenu différent, requête encore en cours ou effet en attente de rapprochement. Un contenu différent exige une nouvelle clé ; un rapprochement exige de vérifier l'effet avant toute nouvelle opération.
invalid_status_transitioninvalid_request_errorTransition d'état impossible — 400 (cf. machine à états des factures/devis/avoirs ; ex: éditer une facture finalisée → émettre un avoir via POST /v1/credit-notes).
resource_deletedconflict_errorLa ressource a été supprimée (soft-delete) et n'est plus modifiable.
conflictconflict_errorMême Idempotency-Key réutilisée avec un payload différent (24 h).
rate_limit_exceededrate_limit_errorLimite par minute dépassée — voir Retry-After.
plan_limit_errorplan_limit_errorFonctionnalité réservée à un plan supérieur.
quota_exceededplan_limit_errorQuota mensuel du plan dépassé (factures, e-reporting, établissements…).
storage_quota_exceededplan_limit_errorQuota de stockage de fichiers (PDF, logos) dépassé pour ce plan.
payload_too_largeinvalid_request_errorCorps de requête au-delà de la limite (CSV, pièce jointe).
pa_unavailableinvalid_request_errorLa Plateforme Agréée est injoignable (timeout, 5xx en amont).
pa_not_configuredinvalid_request_errorAucune PA connectée pour cet établissement. Configurer depuis Paramètres → Facturation électronique.
buyer_identity_not_disclosedinvalid_request_errorLe nom ou une composante de l’adresse de l’acheteur contient un marqueur de non-diffusion du registre, ou l’identité corrigée nécessaire à son avoir d’annulation est incomplète. Finalisation ou dépôt refusé (400), sans nouvelle numérotation ni tentative de dépôt ; param désigne le champ à renseigner d’après les documents du client. Pour une facture déjà figée, corriger la fiche client puis établir un avoir total d’annulation avant remplacement : aucun renvoi ne réécrit son identité.
siret_not_foundinvalid_request_errorSIRET introuvable dans l'annuaire SIRENE.
vat_validation_failedinvalid_request_errorLe numéro TVA intracom n'a pas pu être validé par VIES.
schematron_validation_failedinvalid_request_errorLe document viole une règle Schematron EN16931 ou CIUS-FR (BT-46, BR-55…).
invalid_payment_amountinvalid_request_errorMontant de paiement nul ou négatif.
payment_exceeds_amount_dueinvalid_request_errorLe paiement dépasse le restant dû — non autorisé.
livemode_mismatchinvalid_request_error / validation_errorLe mouvement d'encaissement déclare une série (test ou live) qui contredit celle de la facture.
invoice_not_editableinvalid_request_errorLa facture ne peut plus être modifiée après sa finalisation.
method_not_allowedinvalid_request_errorUne décision fiscale est immuable : ni modification ni suppression. Créez une nouvelle décision liée à celle-ci.
not_supportedinvalid_request_errorOpération non prise en charge dans ce contexte, par exemple cloner une facture dont la TVA est fournie par l'intégration.
integration_vat_incoherentinvalid_request_errorLa TVA fournie par l'intégration (taxSource: integration) est incohérente avec les lignes ou les totaux.
email_mismatchinvalid_request_errorL'invitation a été émise pour une autre adresse e-mail que celle du compte connecté.
sandbox_payment_unavailableinvalid_request_errorPaiement en ligne indisponible pour une facture de test — le portail public répond 409, POST /v1/invoices/:id/payment-link répond 422 (les clés Stripe plateforme sont 100 % live). Remède : tester le flux avec une clé fac_live_ sur une facture live.
deposit_not_enabledinvalid_request_errorLe dépôt PA automatique n'est pas activé pour cet établissement (Paramètres → Facturation électronique).
tax_decision_requiredinvalid_request_errorAucune décision fiscale figée n'autorise ce parcours automatisé. Demander une décision avant de poursuivre.
tax_decision_not_finalinvalid_request_errorLa décision est suspendue ou non prise : aucun montant n'est arrêté, aucun document ne doit être finalisé et aucun règlement immédiat ne doit être encaissé.
tax_decision_expiredinvalid_request_errorLa fenêtre d'utilisation de la décision est expirée ; créer une nouvelle décision.
tax_decision_input_rejectedinvalid_request_errorLe payload tente d'imposer un résultat fiscal au lieu de décrire l'opération commerciale.
tax_decision_retry_invalidinvalid_request_errorLa reprise modifie l'opération initiale ; seules les preuves manquantes peuvent être ajoutées.
tax_decision_customer_mismatch / tax_decision_buyer_driftedinvalid_request_errorLe client ou l'identité acheteur ne correspond plus à la décision.
tax_decision_currency_mismatch / tax_decision_totals_inconsistentinvalid_request_errorLa devise ou les totaux divergent de la décision figée.
tax_decision_lines_mismatchinvalid_request_errorLes lignes de présentation ne couvrent pas exactement les lignes décidées.
eu_threshold_state_missinginvalid_request_errorAucun registre du seuil B2C UE n'est ouvert pour l'année de l'opération. Rien n'est supposé à zéro : ouvrir l'année avec POST /v1/eu-threshold-ledgers.
eu_threshold_external_coverage_incompleteinvalid_request_errorD'autres canaux de vente existent et ne sont pas déclarés complets jusqu'au jour de l'opération. Enregistrer un ajustement — même à zéro, qui dit simplement qu'il ne s'est rien passé.
eu_threshold_backdated_operationinvalid_request_errorL'opération est antérieure à une opération déjà comptée ; des décisions ont été figées sur ce cumul et il n'est jamais recalculé en silence.
eu_threshold_year_already_openinvalid_request_errorL'année est déjà ouverte : une déclaration d'ouverture n'est jamais réécrite. Corriger par un ajustement.
eu_threshold_decrease_not_sourcedinvalid_request_errorUn ajustement ne diminue jamais le cumul. Retirer un montant déjà compté passe par une correction qualifiée (POST /v1/eu-threshold-ledgers/:année/corrections).
eu_threshold_entry_conflictinvalid_request_errorCette référence nomme déjà un mouvement de ce registre, enregistré avec un autre contenu. Une écriture s'écrit une fois ; utiliser une nouvelle référence.
eu_threshold_correction_target_unknowninvalid_request_errorLa correction nomme un mouvement que ce registre ne détient pas. Une correction rend ce qu'un mouvement IDENTIFIÉ avait apporté (directive 2006/112/CE art. 90 §1) ; sinon, mettre le registre en revue.
eu_threshold_correction_target_not_correctableinvalid_request_errorLa correction nomme un mouvement qui n'a apporté aucun chiffre — tranche rendue, revue, autre correction. Corrigez le mouvement qui a compté l'opération.
eu_threshold_correction_exceeds_countedinvalid_request_errorLa correction dépasse le SOLDE du mouvement qu'elle nomme. Un mouvement rend ce qu'il a apporté une fois, quel que soit le nombre de corrections ; chaque mouvement publie son remainingMin.
eu_threshold_reconciliation_stale / eu_threshold_reconciliation_mismatchinvalid_request_errorUne revue se clôt par un rapprochement, jamais par un commentaire : un mouvement est survenu depuis le contrôle, ou les totaux vérifiés ne concordent pas avec ceux du registre (les deux chiffres sont rendus).
eu_threshold_reservation_lostinvalid_request_error409 — la tranche que cette décision tenait a disparu avant son écriture. La décision n'est PAS créée : la figer laisserait la vente hors du cumul que la suivante lit. Le registre passe en revue.
eu_threshold_review_requiredinvalid_request_errorLe registre est en revue : son cumul est reconnu faux, et aucune décision n'est figée dessus tant que la revue n'est pas close.
eu_threshold_not_under_reviewinvalid_request_errorRien à clore : ce registre n'est pas en revue.
eu_threshold_year_invalid / eu_threshold_date_outside_year / eu_threshold_date_in_futureinvalid_request_errorL'année n'est pas une année civile que ce registre tient, ou la date de complétude n'appartient pas à l'année du registre, ou elle est dans le futur — elle déclarerait complètes des ventes qui n'ont pas eu lieu.
eu_threshold_concurrent_decision_pendinginvalid_request_errorD'autres opérations de la société sont décidées au même instant, et le plafond tombe entre les deux bornes du cumul. Rien n'est figé sur un ordre encore instable : redemander la décision une fois les autres conclues. Une requête interrompue entre la réservation d'une tranche et l'écriture de sa décision laisse une tranche réservée : elle expire d'elle-même au bout de 15 minutes au plus et disparaît du total. Une opération proche du plafond peut recevoir ce même code pendant ce délai — comportement voulu, rien à purger.
location_evidence_relief_undeterminedinvalid_request_errorUne seule preuve de localisation fournie par un tiers, et l'allègement de l'art. 24 ter (100 000 €) n'est pas établi. Ouvrir le registre de l'année et déclarer les chiffres « services électroniques », ou apporter une seconde preuve.
eu_b2c_rate_supplied_mismatchinvalid_request_errorLe taux transmis par l'intégration n'est confirmé ni par le taux normal de destination ni par les bandes publiées du territoire du vendeur. Décision non finale, jamais corrigée en silence.
destination_regional_regime_not_sourcedinvalid_request_errorUn régime régional de l'État membre de destination suit la DESTINATION de l'opération, et son étendue n'est pas cartographiée par le registre des taux : ni le taux régional ni le taux national ne peuvent être affirmés pour cette famille d'opération. Cas actuel : la Grèce pour les biens — depuis le 01/01/2026, les îles de moins de 20 000 habitants appliquent un taux normal réduit aux biens qui y sont livrés, acquisitions intracommunautaires comprises. Une vente à distance de biens vers la Grèce est refusée, jamais taxée 24 % par défaut ; un service électronique distant reste, lui, taxé 24 %.
not_einvoicing_channelinvalid_request_errorLa décision route l'opération hors e-invoicing ; ne pas la déposer sur une PA. Son e-reporting éventuel reste dû.
endpoint_not_subscribedinvalid_request_errorLe rejeu ciblé (POST /v1/events/:id/retry avec endpointId) vise un endpoint inactif ou non abonné au type de l’événement.
buyer_not_in_directoryinvalid_request_errorValeur de einvoicing.paErrorCode, jamais une erreur HTTP : l'annuaire de la facturation électronique ne connaît pas l'acheteur, le dépôt est rejeté localement sans appel de plateforme et invoice.rejected part avec rejectionCategory: buyer_not_in_directory. Ce résultat ne valide pas le contenu de la facture. Envoyez-la par e-mail, puis renvoyez-la quand l’acheteur aura confirmé son adresse.
self_invoicing_not_allowedinvalid_request_errorLa facture, ou l’avoir, est adressée à son propre émetteur (même SIREN) : une entreprise ne s'envoie pas de facture électronique. Choisissez un client d'une autre entité juridique.
document_not_conformantinvalid_request_errorLe document ne peut pas être déposé : son CII enfreint encore une règle du CIUS-FR après régénération (par exemple BR-FR-08, cadre de facturation). Corrigez les données du document puis renvoyez-le.
ereporting_not_enabledinvalid_request_errorL'obligation existe, mais l'envoi automatique n'est pas activé pour cet établissement.
tax_snapshot_inconsistentinvalid_request_errorLa position fiscale figée du document se contredit ; elle n'est jamais complétée depuis des données vivantes. Remède : réémettre à partir d'une décision.
tax_profile_missing / tax_profile_incomplete / tax_profile_ambiguousinvalid_request_errorLes informations fiscales du vendeur ne couvrent pas la date d'effet, une déclaration manque, ou deux révisions se recouvrent. Aucune n'est devinée : corriger les informations, puis redemander la décision.
tax_decision_seller_territory_unresolvedinvalid_request_errorL'établissement du vendeur ne se résout pas en territoire canonique ; aucune position n'est figée sur un identifiant approximatif.
ereporting_declaration_rejectedinvalid_request_errorLa plateforme a refusé la déclaration (422). Elle est CONSERVÉE comme trace du refus. Remède : POST /v1/ereporting/declarations/:id/retry prépare une nouvelle tentative des mêmes données, à l'état reserved — rien n'est transmis par cet appel. Elle part au prochain traitement planifié, ou immédiatement via POST /v1/ereporting/declarations/:id/submit sur la NOUVELLE déclaration. Rejouer /retry renvoie 200 avec la tentative existante.
ereporting_declaration_not_rejectedinvalid_request_errorSeule une déclaration refusée peut être retentée.
ereporting_declaration_already_supersededinvalid_request_errorLa déclaration indique une tentative existante, mais son chaînage ne peut pas être validé (tentative absente, autre mode ou lien retour incohérent). Une chaîne valide est renvoyée directement en 200, sans cette erreur.
ereporting_attempt_limit_reachedinvalid_request_errorRenvoyer les mêmes données serait refusé de nouveau : corriger les documents source (avoir puis réémission). La période corrigée produit sa propre déclaration.
ereporting_declaration_incompleteinvalid_request_errorLa déclaration stockée ne porte pas les faits nécessaires à une nouvelle tentative.
related_invoice_not_foundinvalid_request_errorLa facture liée à l'avoir est introuvable (remboursement).
credit_note_not_refundableinvalid_request_errorL'avoir n'est pas dans un état remboursable (non finalisé, ou déjà intégralement remboursé).
invalid_refund_amountinvalid_request_errorMontant de remboursement nul, négatif ou mal formé.
refund_exceeds_credit_noteinvalid_request_errorLe remboursement dépasse le montant restant de l'avoir.
subscription_pausedinvalid_request_errorL'abonnement de l'émetteur est suspendu — l'opération est bloquée jusqu'à régularisation.
not_implementedinvalid_request_errorEndpoint pas encore disponible (statut 501).
internal_errorapi_errorErreur serveur — Facturino est notifié automatiquement.

Catégories de rejet d'un dépôt

Un dépôt rejeté ou refusé porte un code normalisé dans einvoicing.rejectionCode, son auteur dans einvoicing.rejectionSource (platform, buyer ou facturino) et sa lecture dans einvoicing.rejectionCategory. Le motif brut reste verbatim dans einvoicing.rejectionReason, les notes jointes dans einvoicing.rejectionNote, séparées par des retours à la ligne. Au renvoi, ces champs sont archivés dans previousSubmissions puis remis à null. Un document ancien peut ne pas encore les porter.

La classification lit le code local, puis le code DGFiP, puis le code HTTP. REJ_ADR donne addressing_error, REJ_SEMAN donne semantic_error, REJ_SYNT et REJ_XSD donnent format_invalid, REJ_DOUBLON donne duplicate et AUTRE donne other. Les codes de motif d’Iopole sont lus de la même façon : SYNTAX_ERROR donne format_invalid, SEMANTIC_ERROR donne semantic_error, DUPLICATED_INVOICE donne duplicate et ROUTING_FAILURE donne buyer_not_in_directory (sauf quand la plateforme ne trouve pas le vendeur : unknown). HTTP 401/403 donne platform_auth, 409 duplicate, 422 format_invalid, 429 et 5xx platform_unavailable. HTTP 400 reste unknown, sauf indice d’annuaire dans le pré-contrôle Super PDP. Le statut fr:213 seul ne prouve pas une erreur sémantique. La source est enregistrée d’après le canal : plateforme pour fr:213 et les réponses HTTP, acheteur pour fr:210, fr:207 et fr:208, Facturino pour son contrôle avant dépôt.

CatégorieCe que cela signifieProchaine étape
buyer_not_in_directoryLa recherche d’annuaire n’a trouvé aucune adresse active pour l’acheteur. Ce résultat ne valide pas le contenu de la facture.Facturino surveille l’annuaire et reprendra le dépôt dès qu’une adresse active sera disponible, après vérification de son éligibilité.
addressing_errorAdresse de routage incohérente avec l’annuaire (REJ_ADR).Facturino traite cet incident ; aucune action de votre part n’est demandée.
otherMotif non codifié (AUTRE), expliqué dans la note jointe.Facturino traite cet incident ; aucune action de votre part n’est demandée.
format_invalidLe fichier n'a pas passé les contrôles de réception (structure, schéma, format).Facturino traite cet incident ; aucune action de votre part n’est demandée.
semantic_errorLe contenu enfreint une règle de la facturation électronique (EN 16931, BR-FR…).Facturino traite cet incident ; aucune action de votre part n’est demandée.
duplicateLa plateforme signale un doublon ; Facturino recherche la preuve de réception correspondante.Facturino recherche la preuve de réception auprès de la plateforme avant tout nouvel envoi.
platform_authLa plateforme a refusé les identifiants de l'établissement.Facturino traite cet incident ; aucune action de votre part n’est demandée.
platform_unavailableLa plateforme n'a pas pu traiter la demande.Facturino reprendra automatiquement le traitement dans le délai prévu ; aucune intervention n’est demandée.
refused_by_buyerL'acheteur a refusé la facture sur sa plateforme (fr:210).Contactez votre client pour examiner son motif ; son refus ne déclenche aucun nouveau dépôt automatique.
suspendedL'acheteur a suspendu le traitement et attend un complément (fr:207/208).Répondez à votre client ; la facture reprendra son cours sur sa plateforme.
unknownMotif non reconnu.Facturino traite cet incident ; aucune action de votre part n’est demandée.

Stratégie de retry

Tous les codes 4xx sauf 409/429 sont permanents — corrigez le payload, ne retentez pas.

CasStratégie
429Lire le header Retry-After (secondes) et attendre exactement cette durée
500 / 502Backoff exponentiel : 1s, 2s, 4s, 8s — max 5 tentatives
503 (PA)Backoff long : 30s, 60s, 120s — max 3 tentatives sur 5 min
409 idempotencyNE PAS retenter — le payload diffère du précédent

Idempotence et retry. Toujours envoyer un header Idempotency-Key sur les requêtes POST. Sans cela, un retry après un timeout réseau peut créer un doublon. Voir Idempotence.

Exemples

Node.js :

try {
  const invoice = await facturino.invoices.create({ /* ... */ })
} catch (err) {
  if (err.type === 'invalid_request_error') {
    console.error(`Champ invalide: ${err.param} — ${err.message}`)
  } else if (err.type === 'rate_limit_error') {
    // Retentez après le délai indiqué dans Retry-After
    await sleep(err.retryAfterMs)
  } else {
    // Toujours logguer err.requestId pour le support
    logger.error({ requestId: err.requestId, code: err.code }, err.message)
  }
}

Python :

from facturino import Facturino, FacturinoError

try:
    invoice = client.invoices.create(...)
except FacturinoError as err:
    if err.type == "invalid_request_error":
        print(f"Champ invalide: {err.param} — {err.message}")
    elif err.type == "rate_limit_error":
        time.sleep(err.retry_after_seconds)
    else:
        logger.error({"request_id": err.request_id}, err.message)

Contacter le support

Pour toute erreur inexpliquée ou un comportement inattendu, contactez le support en fournissant :

  • Le request_id exact (préfixe req_) — il permet de retrouver les logs côté Facturino.
  • L'horodatage de la requête (à la seconde près).
  • Le payload envoyé (masquez les champs sensibles si nécessaire).

Contacter le support · best-effort (Gratuit, Essential), prioritaire (Pro).