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"
}
} | Champ | Description |
|---|---|
type | Catégorie de l'erreur (voir tableau ci-dessous) |
code | Code machine-readable pour le branchement (ex. missing_required_field) |
message | Message en clair, lisible pour un humain (ne pas afficher tel quel aux utilisateurs finaux) |
param | Optionnel — Nom du paramètre invalide quand applicable |
doc_url | Optionnel — Lien vers la documentation détaillée pour ce code |
hint | Optionnel — Suggestion d'action concrète |
request_id | Identifiant unique de la requête (préfixe req_) — à fournir au support |
issues | Optionnel — 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)"
}
]
}
} | Champ | Description |
|---|---|
code | Code stable, sur lequel vous pouvez brancher. Plus précis que le code principal quand celui-ci recouvre plusieurs faits. |
param | Champ en cause (chemin pointé), ou null quand la raison n'en nomme aucun. |
message | Raison 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étail | param | Signification |
|---|---|---|
invalid_postal_code | customer.address.postalCode | Le code postal du client n'est pas un code postal de son pays. |
french_postal_code_required, postal_code_required_for_territory_check | customer.address.postalCode | Le pays compte plusieurs territoires de TVA : le code postal est requis pour désigner le bon. |
unknown_french_postal_territory, unknown_regional_postal_territory | customer.address.postalCode | Le code postal ne correspond à aucun territoire connu de ce pays. |
unknown_country_code | customer.address.country | Le pays n'est pas un code ISO 3166-1 alpha-2 connu. |
territory_conflict | customer.address.country | Le pays et le code postal désignent deux territoires différents. |
| Raison portée par une ligne | lines[<index>] | La position de la ligne dans le tableau lines de votre requête. |
| Toute autre raison | null | La 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
| Code | Signification | Action |
|---|---|---|
200 / 201 | Succès | Continuer |
202 | Accepté (job asynchrone) | Suivre l'id du job via GET /v1/jobs/:id |
204 | Succès, pas de contenu | Continuer |
400 | Requête invalide (validation, format) | Corriger le payload, ne pas retenter |
401 | Authentification manquante ou invalide | Vérifier la clé API |
402 | Paiement requis (plan insuffisant) | Mettre à niveau le plan |
403 | Action interdite (scope, transition d'état) | Vérifier permissions et statut |
404 | Ressource introuvable | Vérifier l'identifiant |
409 | Conflit (idempotency-key réutilisée, version) | Voir error.code |
422 | Donnée invalide après validation (SIRET inconnu, TVA invalide) | Corriger le payload |
429 | Trop de requêtes | Retentez après le délai indiqué dans Retry-After |
500 | Erreur serveur | Retentez avec backoff exponentiel |
503 | Service 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 :
| Type | Statut HTTP | Description |
|---|---|---|
invalid_request_error | 400 | Le payload est mal formé, un champ requis manque, une valeur est invalide. |
validation_error | 422 | Schéma Zod refusé — voir error.param pour le champ fautif. |
authentication_error | 401 | Clé API absente, invalide, révoquée ou expirée. |
permission_error | 403 | La clé n'a pas le scope requis pour cette opération. |
not_found_error | 404 | La ressource n'existe pas, ou n'est pas accessible avec cette clé (livemode mismatch). |
conflict_error | 409 | Conflit (transition d'état impossible, ressource déjà finalisée). |
rate_limit_error | 429 | Limite de requêtes dépassée — consulter Retry-After. |
plan_limit_error | 402 | Plan insuffisant ou quota dépassé (factures/mois, taille storage…). |
api_error | 500 | Erreur 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>).
| Code | Type | Cause |
|---|---|---|
idempotency_error | conflict_error | Identifiant historique conservé dans le catalogue. Le serveur renvoie conflict pour une clé d'idempotence réutilisée avec un contenu différent. |
brute_force_blocked | authentication_error | Authentification temporairement bloquée après trop de tentatives. Attendez avant de réessayer. |
pa_operation_not_supported | invalid_request_error | La plateforme connectée ne prend pas en charge cette opération — 422. Vérifiez les opérations proposées par le connecteur. |
missing_required_field | invalid_request_error | Un champ obligatoire est absent (voir error.param). |
invalid_field_value | invalid_request_error | Valeur invalide (date hors plage, code pays inconnu, livemode mismatch…). |
invalid_content_type | invalid_request_error | Le header Content-Type doit être application/json. |
validation_error | validation_error | Schéma Zod refusé : SIRET invalide, format date, longueur dépassée, valeur hors enum. |
invalid_api_key | authentication_error | Clé inconnue ou mal formée. |
api_key_revoked | authentication_error | La clé a été révoquée depuis le dashboard. |
api_key_expired | authentication_error | La clé a dépassé sa date d'expiration optionnelle. |
missing_api_key | authentication_error | Header Authorization absent ou mal formé. |
captcha_required | authentication_error | Trop de tentatives — résoudre le captcha avant de retenter. |
scope_insufficient | permission_error | La clé n'a pas le scope nécessaire pour cette opération. |
not_found | not_found_error | La ressource n'existe pas ou n'est pas dans votre environnement (fac_test_ vs fac_live_). |
conflict | conflict_error | Mê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_transition | invalid_request_error | Transition 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_deleted | conflict_error | La ressource a été supprimée (soft-delete) et n'est plus modifiable. |
conflict | conflict_error | Même Idempotency-Key réutilisée avec un payload différent (24 h). |
rate_limit_exceeded | rate_limit_error | Limite par minute dépassée — voir Retry-After. |
plan_limit_error | plan_limit_error | Fonctionnalité réservée à un plan supérieur. |
quota_exceeded | plan_limit_error | Quota mensuel du plan dépassé (factures, e-reporting, établissements…). |
storage_quota_exceeded | plan_limit_error | Quota de stockage de fichiers (PDF, logos) dépassé pour ce plan. |
payload_too_large | invalid_request_error | Corps de requête au-delà de la limite (CSV, pièce jointe). |
pa_unavailable | invalid_request_error | La Plateforme Agréée est injoignable (timeout, 5xx en amont). |
pa_not_configured | invalid_request_error | Aucune PA connectée pour cet établissement. Configurer depuis Paramètres → Facturation électronique. |
buyer_identity_not_disclosed | invalid_request_error | Le 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_found | invalid_request_error | SIRET introuvable dans l'annuaire SIRENE. |
vat_validation_failed | invalid_request_error | Le numéro TVA intracom n'a pas pu être validé par VIES. |
schematron_validation_failed | invalid_request_error | Le document viole une règle Schematron EN16931 ou CIUS-FR (BT-46, BR-55…). |
invalid_payment_amount | invalid_request_error | Montant de paiement nul ou négatif. |
payment_exceeds_amount_due | invalid_request_error | Le paiement dépasse le restant dû — non autorisé. |
livemode_mismatch | invalid_request_error / validation_error | Le mouvement d'encaissement déclare une série (test ou live) qui contredit celle de la facture. |
invoice_not_editable | invalid_request_error | La facture ne peut plus être modifiée après sa finalisation. |
method_not_allowed | invalid_request_error | Une décision fiscale est immuable : ni modification ni suppression. Créez une nouvelle décision liée à celle-ci. |
not_supported | invalid_request_error | Opération non prise en charge dans ce contexte, par exemple cloner une facture dont la TVA est fournie par l'intégration. |
integration_vat_incoherent | invalid_request_error | La TVA fournie par l'intégration (taxSource: integration) est incohérente avec les lignes ou les totaux. |
email_mismatch | invalid_request_error | L'invitation a été émise pour une autre adresse e-mail que celle du compte connecté. |
sandbox_payment_unavailable | invalid_request_error | Paiement 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_enabled | invalid_request_error | Le dépôt PA automatique n'est pas activé pour cet établissement (Paramètres → Facturation électronique). |
tax_decision_required | invalid_request_error | Aucune décision fiscale figée n'autorise ce parcours automatisé. Demander une décision avant de poursuivre. |
tax_decision_not_final | invalid_request_error | La 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_expired | invalid_request_error | La fenêtre d'utilisation de la décision est expirée ; créer une nouvelle décision. |
tax_decision_input_rejected | invalid_request_error | Le payload tente d'imposer un résultat fiscal au lieu de décrire l'opération commerciale. |
tax_decision_retry_invalid | invalid_request_error | La reprise modifie l'opération initiale ; seules les preuves manquantes peuvent être ajoutées. |
tax_decision_customer_mismatch / tax_decision_buyer_drifted | invalid_request_error | Le client ou l'identité acheteur ne correspond plus à la décision. |
tax_decision_currency_mismatch / tax_decision_totals_inconsistent | invalid_request_error | La devise ou les totaux divergent de la décision figée. |
tax_decision_lines_mismatch | invalid_request_error | Les lignes de présentation ne couvrent pas exactement les lignes décidées. |
eu_threshold_state_missing | invalid_request_error | Aucun 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_incomplete | invalid_request_error | D'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_operation | invalid_request_error | L'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_open | invalid_request_error | L'année est déjà ouverte : une déclaration d'ouverture n'est jamais réécrite. Corriger par un ajustement. |
eu_threshold_decrease_not_sourced | invalid_request_error | Un 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_conflict | invalid_request_error | Cette 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_unknown | invalid_request_error | La 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_correctable | invalid_request_error | La 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_counted | invalid_request_error | La 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_mismatch | invalid_request_error | Une 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_lost | invalid_request_error | 409 — 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_required | invalid_request_error | Le 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_review | invalid_request_error | Rien à clore : ce registre n'est pas en revue. |
eu_threshold_year_invalid / eu_threshold_date_outside_year / eu_threshold_date_in_future | invalid_request_error | L'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_pending | invalid_request_error | D'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_undetermined | invalid_request_error | Une 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_mismatch | invalid_request_error | Le 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_sourced | invalid_request_error | Un 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_channel | invalid_request_error | La 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_subscribed | invalid_request_error | Le 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_directory | invalid_request_error | Valeur 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_allowed | invalid_request_error | La 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_conformant | invalid_request_error | Le 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_enabled | invalid_request_error | L'obligation existe, mais l'envoi automatique n'est pas activé pour cet établissement. |
tax_snapshot_inconsistent | invalid_request_error | La 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_ambiguous | invalid_request_error | Les 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_unresolved | invalid_request_error | L'établissement du vendeur ne se résout pas en territoire canonique ; aucune position n'est figée sur un identifiant approximatif. |
ereporting_declaration_rejected | invalid_request_error | La 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_rejected | invalid_request_error | Seule une déclaration refusée peut être retentée. |
ereporting_declaration_already_superseded | invalid_request_error | La 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_reached | invalid_request_error | Renvoyer 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_incomplete | invalid_request_error | La déclaration stockée ne porte pas les faits nécessaires à une nouvelle tentative. |
related_invoice_not_found | invalid_request_error | La facture liée à l'avoir est introuvable (remboursement). |
credit_note_not_refundable | invalid_request_error | L'avoir n'est pas dans un état remboursable (non finalisé, ou déjà intégralement remboursé). |
invalid_refund_amount | invalid_request_error | Montant de remboursement nul, négatif ou mal formé. |
refund_exceeds_credit_note | invalid_request_error | Le remboursement dépasse le montant restant de l'avoir. |
subscription_paused | invalid_request_error | L'abonnement de l'émetteur est suspendu — l'opération est bloquée jusqu'à régularisation. |
not_implemented | invalid_request_error | Endpoint pas encore disponible (statut 501). |
internal_error | api_error | Erreur 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égorie | Ce que cela signifie | Prochaine étape |
|---|---|---|
buyer_not_in_directory | La 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_error | Adresse de routage incohérente avec l’annuaire (REJ_ADR). | Facturino traite cet incident ; aucune action de votre part n’est demandée. |
other | Motif non codifié (AUTRE), expliqué dans la note jointe. | Facturino traite cet incident ; aucune action de votre part n’est demandée. |
format_invalid | Le 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_error | Le 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. |
duplicate | La 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_auth | La plateforme a refusé les identifiants de l'établissement. | Facturino traite cet incident ; aucune action de votre part n’est demandée. |
platform_unavailable | La 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_buyer | L'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. |
suspended | L'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. |
unknown | Motif 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.
| Cas | Stratégie |
|---|---|
429 | Lire le header Retry-After (secondes) et attendre exactement cette durée |
500 / 502 | Backoff exponentiel : 1s, 2s, 4s, 8s — max 5 tentatives |
503 (PA) | Backoff long : 30s, 60s, 120s — max 3 tentatives sur 5 min |
409 idempotency | NE 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_idexact (préfixereq_) — 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).