{
  "openapi": "3.1.0",
  "info": {
    "title": "Facturino API",
    "version": "2026-09-01",
    "description": "API REST pour la facturation électronique conforme. Solution Compatible (SC) pour la réforme 2026.\n\n## Authentification\nToutes les requêtes authentifiées nécessitent un header `Authorization: Bearer <clé_api>`.\n- Clé test : `fac_test_xxx` (données sandbox isolées)\n- Clé live : `fac_live_xxx` (données production)\n\n## Montants\n- **Prix** : entiers en centimes d'euros (10000 = 100,00 EUR)\n- **Taux de TVA** : entiers en centièmes de pourcent (2000 = 20,00 %)\n- **Remises** : entiers en centièmes de pourcent (500 = 5,00 %)\n\n## Pagination\nPagination cursor-based avec `starting_after` et `limit` (défaut 25, max 100).\n\n## Idempotence\nL'en-tête `Idempotency-Key` protège le **rejeu d'une même requête**, pas la déduplication d'une intention.\n- **Même clé + même corps canonique** : la première réponse 2xx est rejouée à l'identique, sans ré-exécuter l'opération.\n- **Même clé + corps différent** : `409 conflict`. Une clé appartient à une requête, pas à un endpoint.\n- **Clés différentes** : deux opérations distinctes, même si les corps sont identiques. Deux requêtes qui décrivent la même opération ne sont **pas** dédupliquées automatiquement — c'est la clé, et elle seule, qui déclare que deux envois sont la même tentative.\n- **Corps canonique** : les clés JSON sont comparées dans un ordre stable, donc réordonner un objet ne change pas la requête. Modifier une valeur, ajouter ou retirer un champ, en revanche, la change. L'ordre des éléments d'un tableau est significatif : deux lignes permutées sont deux documents.\n- **Échec AVANT exécution** (validation, champ en lecture seule, sanitisation) : la clé est **libérée**, et un rejeu corrigé portant la même clé s'exécute.\n- **Refus métier PENDANT l'exécution** : la réponse d'erreur est conservée et rejouée ; l'opération n'est pas ré-exécutée.\n- **Portée** : 24 h, par clé d'API. `POST /v1/tax-decisions` porte en outre une idempotence MÉTIER durable, sans expiration — voir la ressource.\n\n## Erreurs\nFormat Stripe-like : `{error: {type, code, message, param, doc_url, request_id, hint}}`.\n\n## Versions de contrat\nLe contrat `/v1` est versionné par DATE (`YYYY-MM-DD`), comme chez Stripe. Épinglez-la avec l'en-tête `API-Version` (ou `Facturino-Version`) ; sans en-tête, la version courante s'applique.\n- **`2026-09-01` (courante)** : le PREMIER contrat stable. Cycle de décision fiscale complet : toute facture fiscalisée naît d'une décision immuable (`POST /v1/tax-decisions`), sous l'une des deux sources fiscales de même niveau — `facturino` (TVA déterminée par Facturino) ou `integration` (TVA fournie par l'intégration, validée en cohérence, jamais corrigée en silence). Les montants, les mentions légales et les trois axes d'obligation (e-invoicing, e-reporting transactions, e-reporting paiements) sont décidés côté serveur dans les deux parcours.\n- Les futures versions datées seront immuables et coexisteront avec celle-ci : une date publiée ne change jamais de sens rétroactivement.\n\n## Corps HTTP\nUn JSON invalide répond 400 invalid_request_error ; un corps dépassant 1 Mio répond 413 payload_too_large, avec request_id dans les deux cas.\n\n\n**En-têtes de débit** : chaque réponse /v1 porte X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset. Les fenêtres par clé sont fixes, alignées sur la minute UTC (pas glissantes). Limit et Remaining sont des entiers lorsque le compteur a été évalué, sinon la chaîne `not_applied` (routes publiques, lectures test, refus antérieur au contrôle). Reset est le début de la prochaine minute en secondes Unix. Un 429 porte Retry-After, à respecter.",
    "contact": {
      "name": "Support Facturino",
      "email": "contact@facturino.com",
      "url": "https://facturino.com/docs/"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://facturino.com/api",
      "description": "Production & Sandbox (différencié par le préfixe de la clé API)"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Health",
      "description": "Vérification de disponibilité"
    },
    {
      "name": "Invoices",
      "description": "Gestion des factures"
    },
    {
      "name": "Customers",
      "description": "Gestion des clients"
    },
    {
      "name": "Products",
      "description": "Catalogue produits"
    },
    {
      "name": "Quotes",
      "description": "Gestion des devis"
    },
    {
      "name": "CreditNotes",
      "description": "Gestion des avoirs"
    },
    {
      "name": "Payments",
      "description": "Enregistrement des paiements (sous-ressource de factures)"
    },
    {
      "name": "Companies",
      "description": "Gestion des entreprises et CGV"
    },
    {
      "name": "WebhookEndpoints",
      "description": "Configuration des endpoints webhook"
    },
    {
      "name": "Events",
      "description": "Événements webhook"
    },
    {
      "name": "RecurringInvoices",
      "description": "Factures récurrentes"
    },
    {
      "name": "ReceivedInvoices",
      "description": "Factures reçues (achat)"
    },
    {
      "name": "Exports",
      "description": "Exports FEC et RGPD"
    },
    {
      "name": "Reporting",
      "description": "Rapports TVA et chiffre d'affaires"
    },
    {
      "name": "Archives",
      "description": "Archives et chaîne de hachage"
    },
    {
      "name": "Ereporting",
      "description": "Déclarations e-reporting"
    },
    {
      "name": "Jobs",
      "description": "Suivi des tâches asynchrones"
    },
    {
      "name": "Billing",
      "description": "Abonnement et facturation Facturino"
    },
    {
      "name": "Account",
      "description": "Gestion du compte utilisateur"
    },
    {
      "name": "CSV",
      "description": "Import/export CSV"
    },
    {
      "name": "Validate",
      "description": "Validation de conformité"
    },
    {
      "name": "Sandbox",
      "description": "Outils de test (clé fac_test_ uniquement)"
    },
    {
      "name": "Reference",
      "description": "Référentiels publics : formes juridiques, codes NAF, taux et libellés utiles à l'intégration."
    },
    {
      "name": "Portal",
      "description": "Portail client et page de paiement publics (authentification par token)"
    },
    {
      "name": "Usage",
      "description": "Consommation vs limites du plan"
    },
    {
      "name": "TaxDecisions",
      "description": "Décisions fiscales immuables. Une décision fixe la TVA, les montants de la facture et les obligations de transmission pour tout le cycle, quel que soit le moyen de paiement."
    },
    {
      "name": "EuThresholdLedgers",
      "description": "Registre annuel du seuil commun de 10 000 € HT. C'est la face OPÉRATIONNELLE du seuil : ce que le vendeur déclare à l'ouverture d'une année, et ce que les autres canaux y ajoutent ensuite. Le profil fiscal, lui, ne porte que la règle stable."
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Vérifier la disponibilité de l'API",
        "description": "Endpoint public sans authentification. Retourne le statut de l'API, la version et la région.",
        "tags": [
          "Health"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "API disponible",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "version": {
                      "type": "string",
                      "example": "2026-09-01"
                    },
                    "region": {
                      "type": "string",
                      "example": "europe-west1"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/reference/legal-forms": {
      "get": {
        "operationId": "listLegalForms",
        "summary": "Liste des formes juridiques INSEE",
        "description": "Données publiques INSEE (niveau III). Réponse mise en cache 24 h. Pas d'authentification.",
        "tags": [
          "Reference"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Liste des formes juridiques",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegalForm"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/reference/naf-codes": {
      "get": {
        "operationId": "listNafCodes",
        "summary": "Liste des codes NAF Rev. 2 INSEE",
        "description": "Données publiques INSEE (Nomenclature des Activités Françaises Rev. 2). Réponse mise en cache 24 h. Pas d'authentification.",
        "tags": [
          "Reference"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Liste des codes NAF",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/NafCode"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/portal/{token}": {
      "get": {
        "operationId": "getPortalInvoice",
        "summary": "Consulter une facture via le portail client",
        "description": "Endpoint public authentifié par token (pas de clé API). Retourne les données publiques de la facture pour le portail client. Le token est généré via `POST /v1/invoices/{id}/portal-link`.",
        "tags": [
          "Portal"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 32
            },
            "description": "Token de portail (64 caractères hex)"
          }
        ],
        "responses": {
          "200": {
            "description": "Données publiques de la facture",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "portal_invoice"
                    },
                    "number": {
                      "type": "string",
                      "nullable": true
                    },
                    "status": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string",
                      "example": "eur"
                    },
                    "issuer": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "address": {
                          "$ref": "#/components/schemas/Address"
                        }
                      }
                    },
                    "customer": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "description": {
                            "type": "string"
                          },
                          "quantity": {
                            "type": "string"
                          },
                          "unit": {
                            "type": "string"
                          },
                          "unitPrice": {
                            "type": "integer"
                          },
                          "vatRate": {
                            "type": "integer"
                          },
                          "lineTotal": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "totalHT": {
                          "type": "integer",
                          "nullable": true
                        },
                        "totalVAT": {
                          "type": "integer",
                          "nullable": true
                        },
                        "totalTTC": {
                          "type": "integer",
                          "nullable": true
                        },
                        "amountDue": {
                          "type": "integer",
                          "nullable": true
                        }
                      }
                    },
                    "dates": {
                      "type": "object",
                      "properties": {
                        "issued": {
                          "type": "string",
                          "nullable": true
                        },
                        "due": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "notes": {
                      "type": "string",
                      "nullable": true
                    },
                    "payment_link_url": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Token expiré"
          }
        }
      }
    },
    "/v1/pay/{token}": {
      "get": {
        "operationId": "getPaymentPage",
        "summary": "Consulter la page de paiement d'une facture",
        "description": "Endpoint public authentifié par token (pas de clé API). Retourne un résumé de la facture et le lien de paiement Stripe. Le token est le même que celui du portail, généré via `POST /v1/invoices/{id}/portal-link`.",
        "tags": [
          "Portal"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 32
            },
            "description": "Token de paiement (64 caractères hex)"
          }
        ],
        "responses": {
          "200": {
            "description": "Résumé de la facture pour paiement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "payment_page"
                    },
                    "number": {
                      "type": "string",
                      "nullable": true
                    },
                    "total_ttc": {
                      "type": "string",
                      "nullable": true
                    },
                    "currency": {
                      "type": "string",
                      "example": "eur"
                    },
                    "issuer_name": {
                      "type": "string"
                    },
                    "due_date": {
                      "type": "string",
                      "nullable": true
                    },
                    "status": {
                      "type": "string"
                    },
                    "payment_link_url": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Token expiré"
          }
        }
      }
    },
    "/v1/pay/{token}/checkout": {
      "post": {
        "operationId": "createPaymentCheckout",
        "summary": "Créer une session de paiement carte (Stripe Connect)",
        "description": "Endpoint public authentifié par token (pas de clé API). Crée une session de paiement Stripe pour le solde restant dû de la facture et retourne l'URL de redirection. Nécessite que l'émetteur ait activé les paiements en ligne (compte Stripe Connect, `charges_enabled`).",
        "tags": [
          "Portal"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 32
            },
            "description": "Token de paiement (64 caractères hex)"
          }
        ],
        "responses": {
          "200": {
            "description": "Session de paiement créée",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "checkout_session"
                    },
                    "url": {
                      "type": "string",
                      "description": "URL de redirection vers la page de paiement Stripe"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Facture déjà payée, sans solde, ou paiement en ligne non configuré par l'émetteur"
          },
          "410": {
            "description": "Token expiré"
          }
        }
      }
    },
    "/v1/quote-portal/{token}": {
      "get": {
        "operationId": "getQuotePortal",
        "summary": "Consulter un devis via le portail public",
        "description": "Endpoint public authentifié par token (pas de clé API). Retourne les données publiques du devis pour le portail client ; le premier accès fait passer le statut `sent` à `viewed`. Le token est généré à l'envoi du devis.",
        "tags": [
          "Portal"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 20
            },
            "description": "Token de portail du devis"
          }
        ],
        "responses": {
          "200": {
            "description": "Données publiques du devis",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "portal_quote"
                    },
                    "number": {
                      "type": "string",
                      "nullable": true
                    },
                    "status": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string",
                      "example": "eur"
                    },
                    "issuer": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "address": {
                          "$ref": "#/components/schemas/Address"
                        }
                      }
                    },
                    "customer": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "totalHT": {
                          "type": "string",
                          "nullable": true
                        },
                        "totalVAT": {
                          "type": "string",
                          "nullable": true
                        },
                        "totalTTC": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "dates": {
                      "type": "object",
                      "properties": {
                        "issued": {
                          "type": "string",
                          "nullable": true
                        },
                        "validUntil": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "notes": {
                      "type": "string",
                      "nullable": true
                    },
                    "pdf_url": {
                      "type": "string",
                      "nullable": true
                    },
                    "actionable": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "Token expiré"
          }
        }
      }
    },
    "/v1/quote-portal/{token}/accept": {
      "post": {
        "operationId": "acceptQuotePortal",
        "summary": "Accepter un devis via le portail public",
        "description": "Endpoint public authentifié par token. Passe le devis à `accepted` (uniquement depuis `sent` ou `viewed`).",
        "tags": [
          "Portal"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 20
            },
            "description": "Token de portail du devis"
          }
        ],
        "responses": {
          "200": {
            "description": "Devis accepté",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "portal_quote"
                    },
                    "status": {
                      "type": "string",
                      "const": "accepted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Devis non actionnable (statut incompatible)"
          },
          "410": {
            "description": "Token expiré"
          }
        }
      }
    },
    "/v1/quote-portal/{token}/refuse": {
      "post": {
        "operationId": "refuseQuotePortal",
        "summary": "Refuser un devis via le portail public",
        "description": "Endpoint public authentifié par token. Passe le devis à `refused` (uniquement depuis `sent` ou `viewed`).",
        "tags": [
          "Portal"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 20
            },
            "description": "Token de portail du devis"
          }
        ],
        "responses": {
          "200": {
            "description": "Devis refusé",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "portal_quote"
                    },
                    "status": {
                      "type": "string",
                      "const": "refused"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Devis non actionnable (statut incompatible)"
          },
          "410": {
            "description": "Token expiré"
          }
        }
      }
    },
    "/v1/invoices": {
      "post": {
        "operationId": "createInvoice",
        "summary": "Créer une facture brouillon",
        "description": "Crée une nouvelle facture au statut `draft`. Les totaux sont calculés automatiquement à partir des lignes. Le client est référencé par son ID et un snapshot de ses données est stocké.",
        "tags": [
          "Invoices"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvoiceInput"
              },
              "example": {
                "customerId": "cus_abc123",
                "type": "standard",
                "buyer": {
                  "companyName": "Acme SARL",
                  "siret": "00012345500008",
                  "address": {
                    "line1": "10 rue de la Paix",
                    "postalCode": "75002",
                    "city": "Paris",
                    "country": "FR"
                  }
                },
                "lines": [
                  {
                    "description": "Prestation de conseil",
                    "quantity": "5",
                    "unitPrice": 15000,
                    "vatRate": 2000,
                    "vatCode": "S",
                    "unit": "hour"
                  }
                ],
                "dates": {
                  "issued": "2026-09-15",
                  "due": "2026-03-31"
                },
                "payment": {
                  "terms": "Paiement à 30 jours",
                  "termsDays": 30,
                  "method": "transfer",
                  "latePaymentRate": "3 fois le taux légal",
                  "collectionFee": "40 EUR"
                },
                "notes": "Merci pour votre confiance."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Facture créée",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "id": "inv_abc123",
                  "object": "invoice",
                  "type": "standard",
                  "status": "draft",
                  "number": null,
                  "currency": "eur",
                  "livemode": false,
                  "created": "2026-09-15T10:00:00.000Z",
                  "updated": "2026-09-15T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "get": {
        "operationId": "listInvoices",
        "summary": "Lister les factures",
        "description": "Retourne une liste paginée de factures, triées par date de création décroissante. Supporte le filtrage par statut et la pagination cursor-based bidirectionnelle.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "name": "convertedFrom",
            "in": "query",
            "description": "Filtrer sur les factures issues d'un devis donné (identifiant `quo_...`, comparé au champ `sourceQuoteId`).",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^quo_"
            }
          },
          {
            "$ref": "#/components/parameters/include_deleted"
          },
          {
            "$ref": "#/components/parameters/date_from"
          },
          {
            "$ref": "#/components/parameters/date_to"
          },
          {
            "$ref": "#/components/parameters/sort"
          },
          {
            "$ref": "#/components/parameters/expand"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée de factures",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/invoices/incoming": {
      "post": {
        "operationId": "createIncomingInvoice",
        "summary": "Déclarer une facture reçue manuellement",
        "description": "Crée une entrée dans la collection `receivedInvoices` (même forme que les factures reçues via PA). Réservé aux plans Essential+ (feature `received_invoices`). Pour les factures reçues via le réseau PA, voir `/v1/received-invoices`.",
        "tags": [
          "Invoices"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "senderName",
                  "amount"
                ],
                "properties": {
                  "senderName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "senderSiret": {
                    "type": "string",
                    "description": "SIRET 14 chiffres (validation Luhn)"
                  },
                  "amount": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Montant TTC en centimes"
                  },
                  "reference": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Facture reçue déclarée (objet received_invoice)"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "get": {
        "operationId": "listIncomingInvoices",
        "summary": "Lister les factures reçues (déclarées manuellement)",
        "description": "Retourne une liste paginée des factures reçues déclarées via cette sous-route. Les factures reçues via PA sont dans `/v1/received-invoices`.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/include_deleted"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée"
          }
        }
      }
    },
    "/v1/invoices/{id}": {
      "get": {
        "operationId": "getInvoice",
        "summary": "Récupérer une facture",
        "description": "Retourne tous les détails d'une facture, y compris les lignes, totaux, statut de la e-facture et les dates du cycle de vie. Le paramètre `expand` hydrate des ressources liées sous une clé `expanded` (notamment `credit_notes`, qui ajoute `expanded.credit_notes[]` et `expanded.net_balance`).",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Liste séparée par des virgules de ressources liées à inliner sous `expanded` : `customer`, `items.product`, `credit_notes` (avoirs liés + solde net).",
            "required": false,
            "schema": {
              "type": "string",
              "example": "credit_notes"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Facture",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateInvoice",
        "summary": "Modifier une facture brouillon",
        "description": "Seules les factures au statut `draft` peuvent être modifiées. Les totaux sont recalculés automatiquement si les lignes sont fournies.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateInvoiceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Facture mise à jour",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteInvoice",
        "summary": "Supprimer une facture brouillon",
        "description": "Suppression logique (soft-delete). Seules les factures `draft` peuvent être supprimées.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Facture supprimée"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/bind-tax-decision": {
      "post": {
        "operationId": "bindInvoiceTaxDecision",
        "summary": "Adosser une décision fiscale à un brouillon commercial",
        "description": "Fige une décision fiscale FINALE sur un brouillon commercial existant — typiquement celui produit par `POST /v1/quotes/{id}/convert`. Ferme le cycle devis sur UN SEUL document : convertir -> décider -> adosser -> finaliser, sans créer de seconde facture ni abandonner le brouillon converti.\n\nLa TVA, les montants, les mentions et les trois axes d'obligation sont copiés verbatim depuis la décision ; la requête ne décrit que la présentation des lignes. La facture reste un brouillon : la finalisation est un appel distinct (`POST /v1/invoices/{id}/finalize`).\n\nIdempotent sur la décision : rejouer le même appel renvoie la même facture. Adosser une AUTRE décision à une facture déjà adossée, ou la même décision à une seconde facture, renvoie 409.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BindTaxDecisionInput"
              },
              "example": {
                "taxDecisionId": "taxdec_abc123",
                "decisionLines": [
                  {
                    "taxLineRef": "l1",
                    "unit": "unit"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Brouillon adossé à la décision",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "id": "inv_abc123",
                  "object": "invoice",
                  "status": "draft",
                  "taxSource": "facturino",
                  "taxDecisionId": "taxdec_abc123",
                  "number": null,
                  "livemode": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/invoices/{id}/finalize": {
      "post": {
        "operationId": "finalizeInvoice",
        "summary": "Finaliser une facture (attribuer un numéro)",
        "description": "Attribue un numéro séquentiel de manière atomique et verrouille la facture. Transition irréversible : `draft` -> `finalized`. Génère automatiquement les mentions légales (CGI art. 242 nonies A) et programme les tâches de relance.\n\n**Corps facultatif.** Une facture encaissée d'avance est émise acquittée dans le même appel : transmettez `payment` et l'encaissement est appliqué DANS LA MÊME TRANSACTION que la numérotation. Le PDF et le Factur-X d'origine sont alors rendus sur une facture déjà réglée (« PAYÉE », BT-113 / BT-115 renseignés). Sans corps, le comportement est inchangé.\n\n`payment` est le même objet que `POST /v1/invoices/{id}/payments` : mêmes champs, mêmes méthodes, montant en centimes entiers. Le sous-document créé est indistinguable de celui d'un encaissement enregistré après coup.\n\nTout ou rien : un encaissement refusé (`payment_exceeds_amount_due`, 422, avec `error.issues` pointant `payment.amount`) refuse la finalisation — aucun numéro n'est brûlé et la facture reste un brouillon.\n\nL'original est figé à l'émission ; les copies PDF suivent les encaissements ultérieurs ; le Factur-X, lui, ne change jamais. Un marqueur de non-diffusion dans le nom ou l’adresse du client refuse la finalisation avant numérotation : HTTP 400, code buyer_identity_not_disclosed, param customer.snapshot.<champ>. Renseigner les données réelles d’après les documents du client ; aucun instantané finalisé n’est réécrit.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FinalizeInvoiceInput"
              },
              "examples": {
                "sansEncaissement": {
                  "summary": "Finalisation simple (corps absent ou vide)",
                  "value": {}
                },
                "avecEncaissement": {
                  "summary": "Facture réglée d'avance : émise acquittée",
                  "value": {
                    "payment": {
                      "amount": 120000,
                      "method": "card",
                      "reference": "ch_3Kj9aLZ",
                      "paidAt": "2026-05-10T12:00:00.000Z"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Facture finalisée — `paid` ou `partially_paid` lorsqu'un encaissement accompagne l'appel",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "id": "inv_abc123",
                  "object": "invoice",
                  "status": "paid",
                  "number": "FAC-00001",
                  "livemode": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/invoices/{id}/send": {
      "post": {
        "operationId": "sendInvoice",
        "summary": "Envoyer la facture via la Plateforme Agréée du client",
        "description": "Envoie la facture à la Plateforme Agréée connectée par le client. Requis : document finalisé dont la transmission est `pending` ou `rejected` — soit `status` `finalized` ou `rejected`, soit une facture déjà encaissée (`paid`, `partially_paid`, `overdue`) émise acquittée. Retourne `202 Accepted` avec `status: \"sending\"` ; la facture y reste tant que la PA n'a pas confirmé le dépôt (premier contrôle une minute après l'envoi, puis toutes les quinze minutes), puis progresse (`deposited` / `transmitted` / `rejected`). Un rejet porte `einvoicing.paErrorCode`, `einvoicing.rejectionReason` (mots de la plateforme) et `einvoicing.rejectionCategory` (lecture stable : `buyer_not_in_directory` (aucune adresse active trouvée pour l’acheteur ; envoyez-la par e-mail, sans en déduire la conformité du contenu), `addressing_error`, `other`, `format_invalid`, `semantic_error`, `duplicate`, `platform_auth`, `platform_unavailable`, `refused_by_buyer`, `suspended`, `unknown`).\n\n**Annuaire avant dépôt.** L'annuaire de la facturation électronique est consulté avant tout dépôt. Un acheteur absent est rejeté localement, sans appel de dépôt : `paErrorCode` et `rejectionCategory` valent `buyer_not_in_directory`, la facture passe `rejected` et l'événement `invoice.rejected` part avec ces champs ; l'envoi par e-mail est la voie à suivre, un renvoi est possible quand l'acheteur aura choisi sa plateforme. Un annuaire injoignable ne bloque pas : le dépôt est tenté et la plateforme décide.\n\n**Garde fiscale.** L'éligibilité vient de la DÉCISION figée sur le document, jamais de l'adresse client courante, du SIRET du moment ni de `einvoicing.depositEnabled`. La garde s'exécute AVANT toute mutation : un document refusé n'est jamais passé à `sending` et n'apparaît jamais comme transmis.\n\nErreurs stables :\n- `tax_decision_required` — le document ne porte aucune décision fiscale. Demandez une décision (`POST /v1/tax-decisions`) puis réémettez. Le document existant, ses fichiers et le dépôt manuel restent disponibles.\n- `tax_snapshot_inconsistent` — la position fiscale figée se contredit ; elle n'est jamais complétée depuis des données vivantes. Demandez une nouvelle décision puis réémettez.\n- `not_einvoicing_channel` — la décision oriente l'opération hors du canal e-invoicing (B2C, acheteur étranger, Guyane, Mayotte, COM). Elle ne doit pas être déposée auprès d'une PA ; son obligation d'e-reporting, si elle existe, reste due et n'est pas éteinte par ce refus.\n- `self_invoicing_not_allowed` — le client porte le SIREN de l'émetteur : une entreprise ne s'envoie pas de facture électronique, l'annuaire la lui renverrait. Choisissez un client d'une autre entité juridique.\n- `deposit_not_enabled` / `pa_not_configured` — infrastructure : le dépôt réseau est coupé ou aucune PA n'est connectée. L'obligation, elle, n'est pas affectée.\n\nDans tous ces cas, le téléchargement Factur-X / XML (CII, UBL) et le dépôt manuel restent disponibles sur tous les plans.\n\nLe rejet enregistre rejectionCode, rejectionSource et rejectionNote dans einvoicing. Le pré-contrôle annuaire porte rejectionCode: buyer_not_in_directory et rejectionSource: facturino. Les textes restent verbatim ; seuls les codes classent le verdict. Ces champs sont archivés dans previousSubmissions puis remis à null au renvoi. L’annuaire est interrogé avec 0009:<SIRET> si le SIRET contient 14 chiffres, sinon 0225:<SIREN>. Une absence active provoque un rejet local buyer_not_in_directory ; une réponse indéterminée laisse le dépôt inchangé. Une adresse active différente régénère le CII soumis (BT-49), sous la même clé d’idempotence ; einvoicing.routingIdentifier et submissionArtefact.routingIdentifier exposent l’adresse utilisée. Le renvoi d’un dépôt rejeté ouvre une nouvelle tentative sous le même numéro, archive les faits de l’ancienne dans previousSubmissions et remet les champs de rejet ainsi que buyerReachableAt et directoryCheckedAt à null. Aucun renvoi automatique après la revérification quotidienne.\n\nIdentité figée de l’acheteur : un marqueur de non-diffusion dans son nom ou son adresse refuse le dépôt avec HTTP 400, code buyer_identity_not_disclosed et param customer.snapshot.<champ>, avant toute lecture de fichier, régénération, mutation de la facture ou connexion à la plateforme. Ce contrôle vaut aussi pour les factures finalisées avant le correctif, les renvois et le mode test. Corriger la fiche client ne réécrit pas la facture : établir un avoir total d’annulation avec l’identité corrigée, puis une facture de remplacement si nécessaire. After durable acceptance, Facturino owns deposit, status monitoring and technical recovery. No integrator cron is required. A directory absence is monitored and retried automatically when reachable; a buyer refusal is never permission to resend. Inspect einvoicing.obligation for responsibility and the next action.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "202": {
            "description": "Envoi en cours",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "const": "invoice"
                    },
                    "status": {
                      "type": "string",
                      "const": "sending"
                    }
                  }
                },
                "example": {
                  "id": "inv_abc123",
                  "object": "invoice",
                  "status": "sending"
                }
              }
            }
          },
          "400": {
            "description": "Refus. Outre les erreurs de requête habituelles, la garde fiscale répond ici : `tax_decision_required`, `tax_snapshot_inconsistent`, `not_einvoicing_channel`, `self_invoicing_not_allowed`, `deposit_not_enabled`, `pa_not_configured`. Aucune mutation n'a eu lieu : le document n'est jamais passé à `sending`. Identité acheteur masquée : buyer_identity_not_disclosed, sans tentative de dépôt ; corriger la fiche client et passer par un avoir total d’annulation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/cancel": {
      "post": {
        "operationId": "cancelInvoice",
        "summary": "Annuler un brouillon de facture",
        "description": "Annule (soft-delete) une facture en statut `draft`. Équivalent de `DELETE /v1/invoices/{id}`. Les factures finalisées sont immuables sous CGI art. 289 — pour corriger ou rembourser, créer un avoir via `POST /v1/credit-notes`.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Brouillon annulé (soft-deleted).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "invoice"
                      ]
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/invoices/{id}/email": {
      "post": {
        "operationId": "emailInvoice",
        "summary": "Envoyer la facture par e-mail",
        "description": "Envoie la facture finalisée au client par e-mail avec le PDF (et optionnellement le XML Factur-X) en pièce jointe. Retourne `status: sent` ou `status: pending` (PDF en cours de génération, poll via `/v1/jobs/:id`).",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientEmail": {
                    "type": "string",
                    "format": "email",
                    "description": "Adresse override (sinon contacts client)"
                  },
                  "customMessage": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "includeXml": {
                    "type": "boolean",
                    "description": "Joindre le XML Factur-X (defaults false)"
                  },
                  "customSubject": {
                    "type": "string",
                    "maxLength": 200
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "E-mail envoyé.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "sent"
                          ]
                        },
                        "invoiceId": {
                          "type": "string"
                        },
                        "recipient": {
                          "type": "string"
                        },
                        "sentAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "PDF en cours de génération.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending"
                      ]
                    },
                    "invoiceId": {
                      "type": "string"
                    },
                    "jobId": {
                      "type": "string"
                    },
                    "pollUrl": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "pdf_generating"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/invoices/{id}/remind": {
      "post": {
        "operationId": "remindInvoice",
        "summary": "Envoyer un rappel de paiement",
        "description": "Envoie un email de rappel de paiement au client. Seules les factures post-finalized et non payées peuvent recevoir un rappel. Le niveau de relance et un message personnalisé peuvent être spécifiés.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "level": {
                    "type": "integer",
                    "description": "Niveau de relance (1, 2, 3...)",
                    "default": 1
                  },
                  "message": {
                    "type": "string",
                    "description": "Message personnalisé ajouté au rappel"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rappel envoyé",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reminder_sent": {
                      "type": "boolean",
                      "const": true
                    },
                    "sent_to": {
                      "type": "string",
                      "format": "email"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/clone": {
      "post": {
        "operationId": "cloneInvoice",
        "summary": "Dupliquer une facture en nouveau brouillon",
        "description": "Crée une copie de la facture en statut `draft`. Le numéro, les fichiers, le portail et les tâches de relance sont réinitialisés.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "201": {
            "description": "Nouvelle facture brouillon créée",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/pdf": {
      "get": {
        "operationId": "getInvoicePdf",
        "summary": "Obtenir le PDF de la facture",
        "description": "Retourne une URL signée si le PDF existe déjà, sinon déclenche une génération asynchrone via Pub/Sub et retourne un job.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "name": "template",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "classic"
            },
            "description": "Modèle de mise en page"
          }
        ],
        "responses": {
          "200": {
            "description": "URL signée du PDF existant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 900
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Génération en cours (job créé)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/facturx": {
      "get": {
        "operationId": "getInvoiceFacturx",
        "summary": "Obtenir le Factur-X (PDF/A-3 + XML)",
        "description": "Retourne une URL signée si le Factur-X existe déjà, sinon déclenche une génération asynchrone. Le Factur-X est un PDF/A-3b avec XML CII EN16931 embarqué.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "name": "template",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "classic"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "URL signée du Factur-X",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Génération en cours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/xml": {
      "get": {
        "operationId": "getInvoiceXml",
        "summary": "Obtenir le XML structuré (CII ou UBL)",
        "description": "Génère le XML à la volée pour une facture finalisée. Format CII (Factur-X EN16931) par défaut, UBL 2.1 avec `?format=ubl`. Un brouillon (`draft`) est refusé (400) : pas de numéro et contenu mutable.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "cii",
                "ubl"
              ],
              "default": "cii"
            },
            "description": "Format XML : `cii` (défaut) ou `ubl`"
          }
        ],
        "responses": {
          "200": {
            "description": "Document XML",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/status": {
      "get": {
        "operationId": "getInvoiceStatus",
        "summary": "Obtenir le statut et les informations de e-facturation",
        "description": "Endpoint léger retournant le statut actuel de la facture, les informations PA (paStatus, paId) et les dates clés. Utile pour le polling.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Statut de la facture",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "$ref": "#/components/schemas/InvoiceStatus"
                    },
                    "einvoicing": {
                      "type": "object",
                      "properties": {
                        "paStatus": {
                          "type": "string",
                          "nullable": true
                        },
                        "paId": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "dates": {
                      "type": "object",
                      "properties": {
                        "due": {
                          "type": "string",
                          "format": "date",
                          "nullable": true
                        },
                        "finalizedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": "deposited",
                  "einvoicing": {
                    "paStatus": "deposited",
                    "paId": "PA-2026-00001"
                  },
                  "dates": {
                    "due": "2026-03-31",
                    "finalizedAt": "2026-09-15T10:00:00.000Z"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/verify": {
      "get": {
        "operationId": "verifyInvoiceArchive",
        "summary": "Vérifier la chaîne de hachage d'archivage",
        "description": "Vérifie l'intégrité de l'archive de la facture en contrôlant la chaîne de hachage SHA-256.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Résultat de vérification",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "verified": {
                      "type": "boolean"
                    },
                    "archive": {
                      "type": "object",
                      "nullable": true
                    },
                    "chain_length": {
                      "type": "integer"
                    },
                    "details": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/events": {
      "get": {
        "operationId": "listInvoiceEvents",
        "summary": "Lister les événements du cycle de vie",
        "description": "Retourne l'historique complet des transitions de statut de la facture.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des événements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LifecycleEntry"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/audit-trail": {
      "get": {
        "operationId": "getInvoiceAuditTrail",
        "summary": "Consulter le journal d'audit de la facture",
        "description": "Retourne les entrées du journal d'audit pour une facture donnée. Chaque entrée contient l'action effectuée, le timestamp, un identifiant utilisateur anonymisé et les détails des modifications (sans PII). Requiert le plan Pro ou supérieur.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "name": "action",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filtrer par type d'action (create, update, finalize, send, etc.)"
          }
        ],
        "responses": {
          "200": {
            "description": "Journal d'audit de la facture",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "audit_trail"
                    },
                    "invoiceId": {
                      "type": "string",
                      "example": "inv_abc123"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string",
                            "example": "finalize"
                          },
                          "timestamp": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "user_id": {
                            "type": "string",
                            "description": "Identifiant anonymisé (6 derniers caractères)",
                            "example": "usr_a1b2c3"
                          },
                          "details": {
                            "type": "object",
                            "nullable": true,
                            "description": "Modifications avant/après (sans PII)"
                          }
                        }
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/audit-trail/pdf": {
      "post": {
        "operationId": "generateAuditTrailPdf",
        "summary": "Générer le PDF de la piste d'audit (PAF)",
        "description": "Génère un PDF récapitulatif de la Piste d'Audit Fiable pour la facture. Contient le titre, le numéro de facture, un tableau chronologique des événements et le hash de vérification. Traitement asynchrone via Pub/Sub. Requiert le plan Pro ou supérieur.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "202": {
            "description": "Génération du PDF lancée (asynchrone)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Identifiant du job",
                      "example": "job_abc123"
                    },
                    "object": {
                      "type": "string",
                      "const": "job"
                    },
                    "type": {
                      "type": "string",
                      "const": "audit_trail_pdf"
                    },
                    "status": {
                      "type": "string",
                      "const": "pending"
                    },
                    "invoiceId": {
                      "type": "string",
                      "example": "inv_abc123"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/portal-link": {
      "post": {
        "operationId": "createPortalLink",
        "summary": "Créer un lien de portail client",
        "description": "Génère un token signé HMAC (64 caractères hex) et stocke le token + date d'expiration sur la facture. Le lien permet au client de consulter sa facture sans authentification. Expire dans 24h.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Lien de portail créé",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri",
                      "example": "https://facturino.com/portal/abc123..."
                    },
                    "token": {
                      "type": "string",
                      "description": "Token brut (64 caractères hex)"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/payment-link": {
      "post": {
        "operationId": "createPaymentLink",
        "summary": "Créer un lien de paiement Stripe (Pro+)",
        "description": "Crée une session Stripe Checkout pour le paiement en ligne de la facture. Nécessite une configuration Stripe Connect préalable via `POST /v1/companies/stripe-connect`.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "success_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "cancel_url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lien de paiement créé",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "payment_link"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "session_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{id}/payment-token": {
      "post": {
        "operationId": "createPaymentToken",
        "summary": "Générer un jeton de paiement sécurisé (Pro+)",
        "description": "Génère un token de paiement sécurisé avec une URL de paiement. Expire dans 24h.",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Jeton créé (expire 24h)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "payment_token"
                    },
                    "token": {
                      "type": "string"
                    },
                    "pay_url": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{invoiceId}/payments": {
      "post": {
        "operationId": "recordPayment",
        "summary": "Enregistrer un paiement",
        "description": "Enregistre un paiement dans une transaction atomique. Met à jour le statut de la facture (`partially_paid` ou `paid`) et envoie l'événement de cycle de vie fr:212 à la PA.",
        "tags": [
          "Payments"
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la facture (préfixe `inv_`)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentInput"
              },
              "example": {
                "amount": 75000,
                "method": "transfer",
                "reference": "VIR-2026-001",
                "paidAt": "2026-03-15T14:00:00.000Z"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Paiement enregistré",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "get": {
        "operationId": "listPayments",
        "summary": "Lister les paiements d'une facture",
        "description": "Retourne la liste des paiements enregistrés pour une facture donnée.",
        "tags": [
          "Payments"
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée de paiements",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentList"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/invoices/{invoiceId}/payments/{paymentId}/cancel": {
      "post": {
        "operationId": "cancelPayment",
        "summary": "Annuler un paiement",
        "description": "Annule un paiement précédemment enregistré (dans une transaction atomique) : le paiement passe au statut `cancelled` (piste d'audit conservée), le solde restant dû de la facture est recalculé et le statut de la facture est réajusté. Refusé si le paiement a déjà été déclaré à l'administration (e-reporting) : émettre une écriture rectificative à la place.",
        "tags": [
          "Payments"
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la facture (préfixe `inv_`)"
          },
          {
            "name": "paymentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID du paiement (préfixe `pay_`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Paiement annulé",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "const": "payment"
                    },
                    "status": {
                      "type": "string",
                      "const": "cancelled"
                    },
                    "invoiceStatus": {
                      "type": "string",
                      "description": "Nouveau statut de la facture après recalcul du solde"
                    },
                    "amountDue": {
                      "type": "integer",
                      "description": "Solde restant dû recalculé, en centimes"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Paiement déjà annulé, ou déjà déclaré à l'administration (e-reporting)"
          }
        }
      }
    },
    "/v1/customers/lookup": {
      "post": {
        "operationId": "lookupCustomer",
        "summary": "Rechercher un client par SIRET, TVA ou nom (Sirene / VIES)",
        "description": "Interroge l'API Sirene de l'INSEE (par SIRET ou nom) ou VIES (par numéro de TVA intracommunautaire) pour rechercher une entreprise. Retourne les informations publiques (raison sociale, adresse, numéro TVA). Fournir exactement UN des trois paramètres. Les marqueurs [NON-DIFFUSIBLE] et [ND] sont retirés : champs vides dans Sirene, null dans VIES (qui retire aussi ---). data.disclosure ou results[].disclosure décrit le statut de diffusion et les champs masqués. Un résultat trouvé ou un SIRET vérifié ne certifie pas le nom ni l’adresse saisis : les obtenir auprès du client.",
        "tags": [
          "Customers"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fournir exactement un seul de `siret`, `vatNumber` ou `query`.",
                "oneOf": [
                  {
                    "required": [
                      "siret"
                    ]
                  },
                  {
                    "required": [
                      "vatNumber"
                    ]
                  },
                  {
                    "required": [
                      "query"
                    ]
                  }
                ],
                "properties": {
                  "siret": {
                    "type": "string",
                    "description": "SIRET 14 chiffres (recherche Sirene)"
                  },
                  "vatNumber": {
                    "type": "string",
                    "minLength": 4,
                    "maxLength": 20,
                    "description": "Numéro de TVA intracommunautaire, ex: `FR31000123455` (validation VIES)"
                  },
                  "query": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 200,
                    "description": "Recherche par nom (Sirene)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Résultat Sirene ou VIES, sans donnée protégée présentée comme réelle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerLookupResult"
                },
                "example": {
                  "object": "sirene_lookup",
                  "found": true,
                  "data": {
                    "name": "",
                    "disclosure": {
                      "status": "P",
                      "withheldFields": [
                        "name",
                        "address.line1",
                        "address.line2",
                        "address.postalCode"
                      ]
                    },
                    "siret": "00012345500008",
                    "siren": "000123455",
                    "vatNumber": "FR31000123455",
                    "legalForm": null,
                    "naf": null,
                    "address": {
                      "line1": "",
                      "line2": "",
                      "postalCode": "",
                      "city": "Paris",
                      "country": "FR"
                    },
                    "active": true
                  },
                  "warning": "The register does not publish some identity or address fields; obtain them from the customer documents."
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers": {
      "post": {
        "operationId": "createCustomer",
        "summary": "Créer un client",
        "description": "Crée un nouveau client. La validation VIES est effectuée en arrière-plan pour les clients EU avec numéro TVA.",
        "tags": [
          "Customers"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCustomerInput"
              },
              "example": {
                "name": "Acme SAS",
                "type": "company",
                "siret": "00012345500008",
                "address": {
                  "line1": "10 rue de la Paix",
                  "postalCode": "75002",
                  "city": "Paris",
                  "country": "FR"
                },
                "contacts": [
                  {
                    "email": "contact@acme.fr",
                    "phone": "0123456789"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Client créé",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "id": "cus_abc123",
                  "object": "customer",
                  "name": "Acme SAS",
                  "type": "company",
                  "siret": "00012345500008",
                  "active": true,
                  "livemode": false,
                  "created": "2026-09-15T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      },
      "get": {
        "operationId": "listCustomers",
        "summary": "Lister les clients",
        "description": "Retourne une liste paginée de clients, triés par date de création décroissante.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/include_deleted"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerList"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "summary": "Récupérer un client",
        "description": "Retourne les détails complets d'un client.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            },
            "description": "Client"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateCustomer",
        "summary": "Modifier un client",
        "description": "Met à jour les champs fournis du client. Seuls les champs présents dans le body sont modifiés.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCustomerInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            },
            "description": "Client mis à jour"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteCustomer",
        "summary": "Supprimer un client (soft-delete)",
        "description": "Suppression logique. Le client est marqué comme supprimé mais reste accessible via `include_deleted=true`.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Client supprimé"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/customers/import": {
      "post": {
        "operationId": "importCustomersCsv",
        "summary": "Importer des clients via CSV (max 1000 lignes)",
        "description": "Importe des clients à partir de données CSV. Accepte soit du texte CSV brut (Content-Type: text/csv), soit un JSON avec un champ `csv` contenant le texte CSV. Utilise un verrou distribué pour empêcher les imports concurrents. Chaque ligne est validée individuellement via le schéma Zod.",
        "tags": [
          "CSV"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "csv"
                ],
                "properties": {
                  "csv": {
                    "type": "string",
                    "description": "Contenu CSV (header + lignes de données). Colonnes supportées : name, email, siret, vat_number, phone, address_line1, address_city, address_postal_code, address_country"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Résultat de l'import",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "import_result"
                    },
                    "imported": {
                      "type": "integer",
                      "description": "Nombre de lignes importées avec succès"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "line": {
                            "type": "integer"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "total_rows": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "409": {
            "description": "Un autre import est déjà en cours"
          }
        }
      }
    },
    "/v1/customers/export": {
      "get": {
        "operationId": "exportCustomersCsv",
        "summary": "Exporter les clients en CSV",
        "description": "Exporte tous les clients actifs (non supprimés) au format CSV (RFC 4180). Max 10 000 lignes. Retourne directement le fichier CSV.",
        "tags": [
          "CSV"
        ],
        "responses": {
          "200": {
            "description": "Fichier CSV",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/products": {
      "post": {
        "operationId": "createProduct",
        "summary": "Créer un produit",
        "description": "Crée un nouveau produit dans le catalogue.",
        "tags": [
          "Products"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProductInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            },
            "description": "Produit créé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      },
      "get": {
        "operationId": "listProducts",
        "summary": "Lister les produits",
        "description": "Retourne une liste paginée de produits du catalogue.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "name": "q",
            "in": "query",
            "description": "Recherche par préfixe du nom du produit (insensible à la casse).",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Filtrer sur une catégorie exacte.",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "active",
            "in": "query",
            "description": "Filtrer sur les produits actifs (`true`) ou inactifs (`false`).",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/include_deleted"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductList"
                }
              }
            },
            "description": "Liste paginée"
          }
        }
      }
    },
    "/v1/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Récupérer un produit",
        "description": "Retourne les détails complets d'un produit.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            },
            "description": "Produit"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateProduct",
        "summary": "Modifier un produit",
        "description": "Met à jour les champs fournis du produit.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProductInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            },
            "description": "Produit mis à jour"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteProduct",
        "summary": "Supprimer un produit (soft-delete)",
        "description": "Suppression logique du produit.",
        "tags": [
          "Products"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Produit supprimé"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/products/import": {
      "post": {
        "operationId": "importProductsCsv",
        "summary": "Importer des produits via CSV (max 1000 lignes)",
        "description": "Importe des produits à partir de données CSV. Accepte soit du texte CSV brut, soit un JSON avec un champ `csv`. Utilise un verrou distribué. Colonnes supportées : name, description, unit_price (en euros), vat_rate (en pourcent), unit, reference, active.",
        "tags": [
          "CSV"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "csv"
                ],
                "properties": {
                  "csv": {
                    "type": "string",
                    "description": "Contenu CSV (header + lignes de données)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Résultat de l'import",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "import_result"
                    },
                    "imported": {
                      "type": "integer"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "line": {
                            "type": "integer"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "total_rows": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "409": {
            "description": "Un autre import est déjà en cours"
          }
        }
      }
    },
    "/v1/products/export": {
      "get": {
        "operationId": "exportProductsCsv",
        "summary": "Exporter les produits en CSV",
        "description": "Exporte tous les produits actifs (non supprimés) au format CSV (RFC 4180). Max 10 000 lignes. Retourne directement le fichier CSV.",
        "tags": [
          "CSV"
        ],
        "responses": {
          "200": {
            "description": "Fichier CSV",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quotes": {
      "post": {
        "operationId": "createQuote",
        "summary": "Créer un devis",
        "description": "Crée un nouveau devis au statut `draft`. Les totaux sont calculés automatiquement.",
        "tags": [
          "Quotes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateQuoteInput"
              },
              "example": {
                "customerId": "cus_abc123",
                "lines": [
                  {
                    "description": "Audit de sécurité",
                    "quantity": "3",
                    "unitPrice": 50000,
                    "vatRate": 2000,
                    "vatCode": "S",
                    "unit": "day"
                  }
                ],
                "dates": {
                  "issued": "2026-09-15",
                  "validUntil": "2026-04-01"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Devis créé",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "id": "quo_abc123",
                  "object": "quote",
                  "status": "draft",
                  "livemode": false,
                  "created": "2026-09-15T10:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      },
      "get": {
        "operationId": "listQuotes",
        "summary": "Lister les devis",
        "description": "Retourne une liste paginée de devis.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/include_deleted"
          },
          {
            "$ref": "#/components/parameters/date_from"
          },
          {
            "$ref": "#/components/parameters/date_to"
          },
          {
            "$ref": "#/components/parameters/sort"
          },
          {
            "$ref": "#/components/parameters/expand"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée de devis"
          }
        }
      }
    },
    "/v1/quotes/{id}": {
      "get": {
        "operationId": "getQuote",
        "summary": "Récupérer un devis",
        "description": "Retourne les détails complets d'un devis.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            },
            "description": "Devis"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateQuote",
        "summary": "Modifier un devis brouillon",
        "description": "Seuls les devis au statut `draft` peuvent être modifiés.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateQuoteInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Devis mis à jour"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "delete": {
        "operationId": "deleteQuote",
        "summary": "Supprimer un devis brouillon",
        "description": "Seuls les devis au statut `draft` peuvent être supprimés (soft-delete).",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Devis supprimé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/quotes/{id}/send": {
      "post": {
        "operationId": "sendQuote",
        "summary": "Envoyer un devis au client",
        "description": "Attribue un numéro atomiquement (si premier envoi) et fait passer le statut à `sent`. Programme un rappel d'expiration si `validUntil` est défini.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Devis envoyé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/quotes/{id}/email": {
      "post": {
        "operationId": "emailQuote",
        "summary": "Envoyer le devis par e-mail",
        "description": "Envoie le devis au client par e-mail avec le PDF en pièce jointe. Retourne `status: sent` ou `status: pending` (PDF en cours de génération, poll via `/v1/jobs/:id`).",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "customMessage": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "customSubject": {
                    "type": "string",
                    "maxLength": 200
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "E-mail envoyé."
          },
          "202": {
            "description": "PDF en cours de génération."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/quotes/{id}/accept": {
      "post": {
        "operationId": "acceptQuote",
        "summary": "Accepter un devis",
        "description": "Fait passer le devis au statut `accepted`. Annule le rappel d'expiration programmé. Un devis expiré (au-delà de sa date de validité `validUntil`) ne peut plus être accepté (400) — le renvoyer pour prolonger sa validité.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Devis accepté"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/quotes/{id}/refuse": {
      "post": {
        "operationId": "refuseQuote",
        "summary": "Refuser un devis",
        "description": "Fait passer le devis au statut `refused`. Annule le rappel d'expiration programmé.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Devis refusé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/quotes/{id}/convert": {
      "post": {
        "operationId": "convertQuoteToInvoice",
        "summary": "Convertir un devis accepté en facture brouillon",
        "description": "Crée une nouvelle facture `draft` à partir des données du devis dans une transaction atomique. Le devis passe au statut `converted`.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "201": {
            "description": "Facture brouillon créée",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/quotes/{id}/clone": {
      "post": {
        "operationId": "cloneQuote",
        "summary": "Dupliquer un devis en brouillon",
        "description": "Crée un nouveau devis `draft` reprenant le client, les lignes, les totaux et les notes du devis source. Numéro, version, suivi, signature et lien de conversion sont réinitialisés.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "201": {
            "description": "Devis brouillon créé",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/quotes/{id}/pdf": {
      "get": {
        "operationId": "getQuotePdf",
        "summary": "Obtenir le PDF du devis",
        "description": "Retourne une URL signée si le PDF existe déjà, sinon déclenche une génération asynchrone via Pub/Sub.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "name": "template",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "classic"
            },
            "description": "Modèle de mise en page"
          }
        ],
        "responses": {
          "200": {
            "description": "URL signée du PDF existant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 900
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Génération en cours (job créé)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/quotes/{id}/signature-proof": {
      "get": {
        "operationId": "getQuoteSignatureProof",
        "summary": "Obtenir la preuve de signature/acceptation d'un devis",
        "description": "Retourne les métadonnées de l'acceptation du devis : date, email du signataire, adresse IP et hash du document. Disponible uniquement pour les devis au statut `accepted` ou `converted`.",
        "tags": [
          "Quotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Preuve de signature",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "signature_proof"
                    },
                    "quote_id": {
                      "type": "string"
                    },
                    "quote_number": {
                      "type": "string",
                      "nullable": true
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "accepted_by": {
                      "type": "string",
                      "nullable": true,
                      "description": "Email du signataire"
                    },
                    "ip_address": {
                      "type": "string",
                      "nullable": true,
                      "description": "Adresse IP du signataire"
                    },
                    "user_agent": {
                      "type": "string",
                      "nullable": true
                    },
                    "signature_hash": {
                      "type": "string",
                      "nullable": true,
                      "description": "Hash SHA-256 du document signé"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/credit-notes": {
      "post": {
        "operationId": "createCreditNote",
        "summary": "Créer un avoir",
        "description": "Crée un avoir lié à une facture existante. Le cumul des avoirs non refusés ne peut excéder le montant TTC de la facture corrigée (400).\n\nPour annuler une facture émise dont l’identité acheteur contient un marqueur de non-diffusion, un avoir total couvrant toutes les lignes reprend le nom et l’adresse corrigés de la même fiche client. La finalisation relit cette fiche dans la transaction et conserve les identifiants légaux présents sur l’original, le pays et la nature fiscale, la décision fiscale et les références à la facture. Une mention est ajoutée à l’historique de l’avoir. L’original et ses fichiers ne sont pas réécrits ; seuls ses compteurs d’avoirs évoluent. Une identité encore masquée ou incomplète reste refusée. Les autres avoirs gardent l’identité figée de l’original.",
        "tags": [
          "CreditNotes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCreditNoteInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                }
              }
            },
            "description": "Avoir créé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "get": {
        "operationId": "listCreditNotes",
        "summary": "Lister les avoirs",
        "description": "Retourne une liste paginée d'avoirs.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/include_deleted"
          },
          {
            "$ref": "#/components/parameters/date_from"
          },
          {
            "$ref": "#/components/parameters/date_to"
          },
          {
            "$ref": "#/components/parameters/sort"
          },
          {
            "$ref": "#/components/parameters/expand"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée d'avoirs"
          }
        }
      }
    },
    "/v1/credit-notes/{id}": {
      "get": {
        "operationId": "getCreditNote",
        "summary": "Récupérer un avoir",
        "description": "Retourne les détails complets d'un avoir.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                }
              }
            },
            "description": "Avoir"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateCreditNote",
        "summary": "Modifier un avoir brouillon",
        "description": "Seuls les avoirs au statut `draft` peuvent être modifiés.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCreditNoteInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Avoir mis à jour"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "delete": {
        "operationId": "deleteCreditNote",
        "summary": "Supprimer un avoir brouillon",
        "description": "Suppression logique. Seuls les avoirs `draft` peuvent être supprimés.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Avoir supprimé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/finalize": {
      "post": {
        "operationId": "finalizeCreditNote",
        "summary": "Finaliser un avoir (attribuer un numéro)",
        "description": "Attribue un numéro séquentiel et verrouille l'avoir. Transition irréversible. Un marqueur de non-diffusion dans le nom ou l’adresse du client refuse la finalisation avant numérotation : HTTP 400, code buyer_identity_not_disclosed, param customer.snapshot.<champ>. Renseigner les données réelles d’après les documents du client ; aucun instantané finalisé n’est réécrit.\n\nPour annuler une facture émise dont l’identité acheteur contient un marqueur de non-diffusion, un avoir total couvrant toutes les lignes reprend le nom et l’adresse corrigés de la même fiche client. La finalisation relit cette fiche dans la transaction et conserve les identifiants légaux présents sur l’original, le pays et la nature fiscale, la décision fiscale et les références à la facture. Une mention est ajoutée à l’historique de l’avoir. L’original et ses fichiers ne sont pas réécrits ; seuls ses compteurs d’avoirs évoluent. Une identité encore masquée ou incomplète reste refusée. Les autres avoirs gardent l’identité figée de l’original.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Avoir finalisé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/send": {
      "post": {
        "operationId": "sendCreditNote",
        "summary": "Envoyer un avoir via la PA",
        "description": "Envoie l'avoir (TypeCode 381) à la PA connectée par le client pour transmission. After durable acceptance, Facturino owns deposit, status monitoring and technical recovery. No integrator cron is required. A directory absence is monitored and retried automatically when reachable; a buyer refusal is never permission to resend. Inspect einvoicing.obligation for responsibility and the next action.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "202": {
            "description": "Envoi en cours"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/refund": {
      "post": {
        "operationId": "refundCreditNote",
        "summary": "Enregistrer le remboursement d'un avoir",
        "description": "Enregistre le décaissement d'un avoir finalisé au client. Sous le régime TVA sur les encaissements, ce remboursement génère une correction négative dans le volet e-reporting « données de paiement ». Le montant est en centimes ; omis, il vaut le TTC total de l'avoir.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Montant remboursé en centimes (ex. 12000 = 120,00 €). Omis = TTC total de l'avoir."
                  },
                  "method": {
                    "type": "string",
                    "description": "Moyen de paiement. De nouvelles valeurs peuvent être ajoutées ; les clients doivent tolérer les valeurs inconnues.",
                    "enum": [
                      "transfer",
                      "card",
                      "check",
                      "cash",
                      "direct_debit",
                      "sepa",
                      "paypal"
                    ]
                  },
                  "refundedAt": {
                    "type": "string",
                    "format": "date",
                    "description": "Date du remboursement (ISO 8601). Défaut : aujourd'hui."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Remboursement enregistré"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/email": {
      "post": {
        "operationId": "emailCreditNote",
        "summary": "Envoyer l'avoir par e-mail",
        "description": "Envoie l'avoir au client par e-mail avec le PDF (et optionnellement le XML Factur-X) en pièce jointe. Retourne `status: sent` ou `status: pending` (PDF en cours de génération, poll via `/v1/jobs/:id`).",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "recipientEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "customMessage": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "includeXml": {
                    "type": "boolean"
                  },
                  "customSubject": {
                    "type": "string",
                    "maxLength": 200
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "E-mail envoyé."
          },
          "202": {
            "description": "PDF en cours de génération."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/pdf": {
      "get": {
        "operationId": "getCreditNotePdf",
        "summary": "Obtenir le PDF de l'avoir",
        "description": "Retourne une URL signée ou déclenche une génération asynchrone.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Document PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/facturx": {
      "get": {
        "operationId": "getCreditNoteFacturx",
        "summary": "Obtenir le Factur-X de l'avoir (PDF/A-3 + XML, TypeCode 381)",
        "description": "Retourne une URL signée si le Factur-X existe déjà, sinon déclenche une génération asynchrone. Le Factur-X est un PDF/A-3b avec XML CII EN16931 embarqué (InvoiceTypeCode 381, référence BT-25 à la facture d'origine). Pour le XML CII brut (dépôt manuel), voir `/v1/credit-notes/{id}/xml`.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          },
          {
            "name": "template",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "classic"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "URL signée du Factur-X",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Génération en cours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/credit-notes/{id}/xml": {
      "get": {
        "operationId": "getCreditNoteXml",
        "summary": "Obtenir le XML CII de l'avoir (TypeCode 381)",
        "description": "Génère le XML CII de l'avoir à la volée (InvoiceTypeCode 381, EN16931 + CIUS-FR). Utilisé pour le dépôt manuel quand la plateforme destinataire attend un XML brut plutôt que le PDF Factur-X. Un brouillon (`draft`) est refusé (400) : pas de numéro et contenu mutable.",
        "tags": [
          "CreditNotes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Document XML CII",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/companies": {
      "get": {
        "operationId": "listCompanies",
        "summary": "Lister les entreprises de l'utilisateur",
        "description": "Retourne les entreprises associées au compte. Seule l'entreprise de la clé API est détaillée intégralement ; les autres n'exposent que leur id, object et nom (isolation multi-établissement). Les entreprises supprimées sont exclues.",
        "tags": [
          "Companies"
        ],
        "responses": {
          "200": {
            "description": "Liste des entreprises"
          }
        }
      },
      "post": {
        "operationId": "createCompany",
        "summary": "Créer une nouvelle entreprise",
        "description": "Crée une entreprise supplémentaire rattachée au compte. Soumis à la limite du plan (1 Free, 1 Essential, 10 Pro, 50/200/500 pour les plans Cabinets). Retourne 402 si la limite est atteinte.",
        "tags": [
          "Companies"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "siret",
                  "address"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "siret": {
                    "type": "string",
                    "pattern": "^\\d{14}$"
                  },
                  "vatNumber": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "legalForm": {
                    "$ref": "#/components/schemas/LegalFormInput"
                  },
                  "naf": {
                    "$ref": "#/components/schemas/NafInput"
                  },
                  "tvaIntracom": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "rcs": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "capitalSocial": {
                    "type": "string",
                    "maxLength": 20
                  },
                  "address": {
                    "$ref": "#/components/schemas/Address"
                  },
                  "vatRegime": {
                    "type": "string",
                    "enum": [
                      "normal",
                      "normal_quarterly",
                      "franchise",
                      "simplified",
                      "debit"
                    ],
                    "default": "normal"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "phone": {
                    "type": "string",
                    "maxLength": 30
                  },
                  "website": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 200
                  },
                  "bankDetails": {
                    "type": "object",
                    "properties": {
                      "iban": {
                        "type": "string",
                        "maxLength": 34
                      },
                      "bic": {
                        "type": "string",
                        "maxLength": 11
                      },
                      "bankName": {
                        "type": "string",
                        "maxLength": 100
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Entreprise créée"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/v1/companies/{id}": {
      "get": {
        "operationId": "getCompany",
        "summary": "Récupérer une entreprise",
        "description": "Retourne les détails complets d'une entreprise (raison sociale, SIRET, adresse, paramètres de facturation).",
        "tags": [
          "Companies"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Entreprise"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateCompany",
        "summary": "Modifier une entreprise",
        "description": "Met à jour les informations de l'entreprise.",
        "tags": [
          "Companies"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Entreprise mise à jour"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/companies/{id}/cgv": {
      "post": {
        "operationId": "uploadCgv",
        "summary": "Téléverser un PDF de CGV (max 5 Mo, base64)",
        "description": "Upload d'un document PDF de conditions générales de vente encodé en base64.",
        "tags": [
          "Companies"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "content"
                ],
                "properties": {
                  "content": {
                    "type": "string",
                    "description": "PDF encodé en base64"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "CGV téléversées"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "get": {
        "operationId": "getCgv",
        "summary": "Obtenir l'URL de téléchargement des CGV",
        "description": "Retourne une URL signée valide 24h.",
        "tags": [
          "Companies"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "URL signée (24h)"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "deleteCgv",
        "summary": "Supprimer les CGV",
        "description": "Supprime le document CGV stocké.",
        "tags": [
          "Companies"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "CGV supprimées"
          }
        }
      }
    },
    "/v1/pa-providers": {
      "get": {
        "operationId": "listPAProviders",
        "summary": "Lister les Plateformes Agréées compatibles",
        "description": "Retourne la liste des PA supportées par Facturino avec leurs métadonnées (nom, type d'authentification, URL d'inscription). Endpoint public, pas d'authentification requise.",
        "tags": [
          "Companies"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Liste des PA compatibles"
          }
        }
      }
    },
    "/v1/exports/invoices": {
      "post": {
        "operationId": "exportInvoices",
        "summary": "Préparer un export ZIP de factures",
        "description": "Génère un ZIP contenant le PDF Factur-X et le XML CII de chaque facture correspondant aux filtres. Traitement asynchrone — retourne 202 Accepted avec un `job_id`. Quand l'archive est prête, une notification `export_ready` est envoyée par email + in-app avec un lien direct de téléchargement (24 h). Consulter `GET /v1/exports/{id}` pour suivre l'avancement et récupérer la `download_url` (signed URL 1 h).",
        "tags": [
          "Exports"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "period_start": {
                    "type": "string",
                    "format": "date",
                    "description": "Borne inférieure sur `dates.issued` (incluse). Omis = pas de borne basse.",
                    "example": "2026-01-01"
                  },
                  "period_end": {
                    "type": "string",
                    "format": "date",
                    "description": "Borne supérieure sur `dates.issued` (incluse). Omis = pas de borne haute.",
                    "example": "2026-12-31"
                  },
                  "statuses": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 20,
                    "description": "Whitelist de statuts. Omis = tous sauf `draft`. Exemples : `[\"paid\", \"partially_paid\"]` pour les factures encaissées.",
                    "example": [
                      "paid",
                      "partially_paid"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job d'export créé",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "job_a1b2c3"
                    },
                    "object": {
                      "type": "string",
                      "const": "job"
                    },
                    "type": {
                      "type": "string",
                      "const": "invoice_export"
                    },
                    "status": {
                      "type": "string",
                      "const": "pending"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/v1/webhook-endpoints": {
      "post": {
        "operationId": "createWebhookEndpoint",
        "summary": "Créer un endpoint webhook",
        "description": "Enregistre une URL pour recevoir les notifications d'événements. Le secret de signature est retourné à la création. L'URL doit être en HTTPS, publiquement joignable et résolvable en DNS vers une adresse publique — les hôtes non résolvables ou privés (localhost, IP internes) sont refusés avec un 400 `invalid_field_value` (protection SSRF).",
        "tags": [
          "WebhookEndpoints"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Endpoint créé (contient le secret)"
          }
        }
      },
      "get": {
        "operationId": "listWebhookEndpoints",
        "summary": "Lister les endpoints webhook",
        "description": "Retourne tous les endpoints webhook configurés.",
        "tags": [
          "WebhookEndpoints"
        ],
        "responses": {
          "200": {
            "description": "Liste des endpoints"
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}": {
      "get": {
        "operationId": "getWebhookEndpoint",
        "summary": "Récupérer un endpoint webhook",
        "description": "Retourne les détails d'un endpoint webhook (URL, événements, statut).",
        "tags": [
          "WebhookEndpoints"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoint"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateWebhookEndpoint",
        "summary": "Modifier un endpoint webhook",
        "description": "Met à jour l'URL, les événements ou le statut actif/inactif d'un endpoint.",
        "tags": [
          "WebhookEndpoints"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookEndpointInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Endpoint mis à jour"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "summary": "Supprimer un endpoint webhook",
        "description": "Supprime définitivement un endpoint webhook.",
        "tags": [
          "WebhookEndpoints"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Endpoint supprimé"
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/test": {
      "post": {
        "operationId": "testWebhookEndpoint",
        "summary": "Tester un endpoint webhook",
        "description": "Envoie un événement de test à l'endpoint pour vérifier la connectivité.",
        "tags": [
          "WebhookEndpoints"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Test envoyé"
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "operationId": "listEvents",
        "summary": "Lister les événements webhook",
        "description": "Retourne l'historique des événements webhook avec leur statut de livraison. Les événements de facture et d’avoir, dont invoice.rejected, invoice.refused et credit_note.credit_refused, portent rejectionCode (code de motif normalisé) et rejectionSource (platform, buyer, facturino), null hors rejet/refus/suspension ou si absent sur un document ancien. rejectionReason conserve le motif brut verbatim, rejectionCategory sa lecture parmi les onze catégories de einvoicing. Les notes verbatim se lisent dans einvoicing.rejectionNote du document. Un renvoi archive les champs dans previousSubmissions et les remet à null. Les événements d’avoir portent aussi data.relatedInvoiceNumber, numéro de la facture corrigée figé à la création (null si absent sur un avoir ancien). fr:211 est une mention d’historique seule, sans événement webhook.",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filtrer par type d'événement (ex: `invoice.created`)"
          },
          {
            "name": "delivered",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": "Filtrer par statut de livraison"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée d'événements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "url": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEvent"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "operationId": "getEvent",
        "summary": "Récupérer un événement",
        "description": "Retourne le détail d'un événement webhook (type, payload, tentatives de livraison). Les événements de facture et d’avoir, dont invoice.rejected, invoice.refused et credit_note.credit_refused, portent rejectionCode (code de motif normalisé) et rejectionSource (platform, buyer, facturino), null hors rejet/refus/suspension ou si absent sur un document ancien. rejectionReason conserve le motif brut verbatim, rejectionCategory sa lecture parmi les onze catégories de einvoicing. Les notes verbatim se lisent dans einvoicing.rejectionNote du document. Un renvoi archive les champs dans previousSubmissions et les remet à null. Les événements d’avoir portent aussi data.relatedInvoiceNumber, numéro de la facture corrigée figé à la création (null si absent sur un avoir ancien). fr:211 est une mention d’historique seule, sans événement webhook.",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Événement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEvent"
                },
                "examples": {
                  "invoice_rejected": {
                    "summary": "invoice.rejected",
                    "value": {
                      "id": "evt_invoice_rejected",
                      "object": "event",
                      "type": "invoice.rejected",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "data": {
                        "id": "inv_00090",
                        "object": "invoice",
                        "number": "FAC2026-00090",
                        "status": "rejected",
                        "previous_status": "deposited",
                        "livemode": true,
                        "documentStatus": "finalized",
                        "transmissionStatus": "rejected",
                        "transmissionDetail": null,
                        "paymentStatus": "unpaid",
                        "paErrorCode": null,
                        "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
                        "rejectionCategory": "addressing_error",
                        "rejectionCode": "REJ_ADR",
                        "rejectionSource": "platform",
                        "metadata": {}
                      },
                      "request": null
                    }
                  },
                  "invoice_refused": {
                    "summary": "invoice.refused",
                    "value": {
                      "id": "evt_invoice_refused",
                      "object": "event",
                      "type": "invoice.refused",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "data": {
                        "id": "inv_00090",
                        "object": "invoice",
                        "number": "FAC2026-00090",
                        "status": "refused",
                        "previous_status": "deposited",
                        "livemode": true,
                        "documentStatus": "finalized",
                        "transmissionStatus": "rejected",
                        "transmissionDetail": "refused",
                        "paymentStatus": "unpaid",
                        "paErrorCode": null,
                        "rejectionReason": "Montant contesté.",
                        "rejectionCategory": "refused_by_buyer",
                        "rejectionCode": null,
                        "rejectionSource": "buyer",
                        "metadata": {}
                      },
                      "request": null
                    }
                  },
                  "credit_note_credit_refused": {
                    "summary": "credit_note.credit_refused",
                    "value": {
                      "id": "evt_credit_note_credit_refused",
                      "object": "event",
                      "type": "credit_note.credit_refused",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "data": {
                        "id": "cn_example",
                        "object": "credit_note",
                        "number": "AV2026-00001",
                        "status": "credit_refused",
                        "previous_status": "deposited",
                        "livemode": true,
                        "documentStatus": "finalized",
                        "transmissionStatus": "rejected",
                        "transmissionDetail": "refused",
                        "paymentStatus": "unpaid",
                        "paErrorCode": null,
                        "rejectionReason": "Montant contesté.",
                        "rejectionCategory": "refused_by_buyer",
                        "rejectionCode": null,
                        "rejectionSource": "buyer",
                        "metadata": {},
                        "relatedInvoiceId": "inv_00090",
                        "relatedInvoiceNumber": "FAC2026-00090"
                      },
                      "request": null
                    }
                  },
                  "payment_received": {
                    "summary": "payment.received",
                    "value": {
                      "id": "evt_payment_received",
                      "object": "event",
                      "type": "payment.received",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "data": {
                        "id": "inv_00090",
                        "object": "invoice",
                        "number": "FAC2026-00090",
                        "status": "paid",
                        "previous_status": "rejected",
                        "livemode": true,
                        "documentStatus": "finalized",
                        "transmissionStatus": "rejected",
                        "transmissionDetail": null,
                        "paymentStatus": "paid",
                        "paErrorCode": null,
                        "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
                        "rejectionCategory": "addressing_error",
                        "rejectionCode": "REJ_ADR",
                        "rejectionSource": "platform",
                        "metadata": {},
                        "amount": "11.88",
                        "total_paid": "11.88",
                        "total_due": "11.88",
                        "total": "11.88",
                        "amountDue": "0.00"
                      },
                      "request": null
                    }
                  },
                  "invoice_created": {
                    "summary": "invoice.created",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_invoice_created",
                      "type": "invoice.created",
                      "data": {
                        "id": "inv_x",
                        "object": "invoice",
                        "status": "draft"
                      }
                    }
                  },
                  "credit_note_created": {
                    "summary": "credit_note.created",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_credit_note_created",
                      "type": "credit_note.created",
                      "data": {
                        "id": "cn_x",
                        "object": "credit_note",
                        "status": "draft"
                      }
                    }
                  },
                  "quote_created": {
                    "summary": "quote.created",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_quote_created",
                      "type": "quote.created",
                      "data": {
                        "id": "qt_x",
                        "object": "quote",
                        "status": "draft",
                        "number": null,
                        "metadata": {}
                      }
                    }
                  },
                  "customer_created": {
                    "summary": "customer.created",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_customer_created",
                      "type": "customer.created",
                      "data": {
                        "id": "cus_x",
                        "object": "customer",
                        "livemode": false
                      }
                    }
                  },
                  "payment_created": {
                    "summary": "payment.created",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_payment_created",
                      "type": "payment.created",
                      "data": {
                        "invoiceId": "inv_00090",
                        "paymentId": "pay_fixture",
                        "amount": "11.88",
                        "method": "card"
                      }
                    }
                  },
                  "invoice_incoming_received": {
                    "summary": "invoice.incoming.received",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_invoice_incoming_received",
                      "type": "invoice.incoming.received",
                      "data": {
                        "id": "rec_fixture",
                        "object": "received_invoice",
                        "pa_invoice_id": "pa_fixture",
                        "sender_siret": "73282932000074",
                        "sender_name": "INTEK CENTER",
                        "number": "FAC2026-00112",
                        "total_ht": "9.90",
                        "total_tva": "1.98",
                        "total_ttc": "11.88"
                      }
                    }
                  },
                  "recurring_invoice_generated": {
                    "summary": "recurring_invoice.generated",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_recurring_invoice_generated",
                      "type": "recurring_invoice.generated",
                      "data": {
                        "id": "inv_x",
                        "object": "invoice",
                        "recurringInvoiceId": "recurring_fixture",
                        "livemode": false
                      }
                    }
                  },
                  "recurring_invoice_failed": {
                    "summary": "recurring_invoice.failed",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_recurring_invoice_failed",
                      "type": "recurring_invoice.failed",
                      "data": {
                        "id": "recurring_fixture",
                        "object": "recurring_invoice",
                        "error": "Operation unavailable"
                      }
                    }
                  },
                  "ereporting_submitted": {
                    "summary": "ereporting.submitted",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_ereporting_submitted",
                      "type": "ereporting.submitted",
                      "data": {
                        "id": "rep_fixture",
                        "object": "ereporting",
                        "status": "submitted",
                        "type": "b2c",
                        "period": "2026-09",
                        "attempt": 2
                      }
                    }
                  },
                  "export_ready": {
                    "summary": "export.ready",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_export_ready",
                      "type": "export.ready",
                      "data": {
                        "id": "job_fixture",
                        "object": "export",
                        "count": 10
                      }
                    }
                  },
                  "subscription_paused": {
                    "summary": "subscription.paused",
                    "value": {
                      "object": "event",
                      "apiVersion": "2026-09-01",
                      "created": "2026-09-07T12:36:00.000Z",
                      "livemode": true,
                      "request": null,
                      "id": "evt_subscription_paused",
                      "type": "subscription.paused",
                      "data": {
                        "status": "paused",
                        "pausedUntil": null,
                        "stripeSubscriptionId": "sub_fixture"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/events/{id}/retry": {
      "post": {
        "operationId": "retryEvent",
        "summary": "Relancer la livraison d'un événement",
        "description": "Relance la livraison d’un événement. Sans corps : nouvelle tentative vers chaque endpoint où la livraison a échoué (un événement déjà livré partout est refusé). Avec `endpointId` : rejeu vers ce seul endpoint, même s’il a déjà été livré, pour rattraper un traitement corrigé côté intégrateur ; l’endpoint doit être actif et abonné au type de l’événement. Votre récepteur doit rester idempotent par identifiant d’événement.",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Relance programmée",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "event"
                      ]
                    },
                    "retryScheduled": {
                      "type": "boolean"
                    },
                    "endpointId": {
                      "type": "string",
                      "description": "Présent quand le rejeu cible un endpoint."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "endpointId": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100,
                    "description": "Endpoint cible du rejeu (`we_…`). Facultatif."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/recurring-invoices": {
      "post": {
        "operationId": "createRecurringInvoice",
        "summary": "Créer une facture récurrente",
        "description": "Configure une facturation récurrente qui génère automatiquement des factures selon la fréquence définie.",
        "tags": [
          "RecurringInvoices"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRecurringInvoiceInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Facture récurrente créée"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "get": {
        "operationId": "listRecurringInvoices",
        "summary": "Lister les factures récurrentes",
        "description": "Retourne la liste des configurations de facturation récurrente.",
        "tags": [
          "RecurringInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée"
          }
        }
      }
    },
    "/v1/recurring-invoices/{id}": {
      "get": {
        "operationId": "getRecurringInvoice",
        "summary": "Récupérer une facture récurrente",
        "description": "Retourne les détails d'une configuration de facturation récurrente.",
        "tags": [
          "RecurringInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Facture récurrente"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateRecurringInvoice",
        "summary": "Modifier une facture récurrente",
        "description": "Met à jour la configuration d'une facturation récurrente.",
        "tags": [
          "RecurringInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateRecurringInvoiceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mise à jour effectuée"
          }
        }
      },
      "delete": {
        "operationId": "deleteRecurringInvoice",
        "summary": "Désactiver une facture récurrente",
        "description": "Désactive la facturation récurrente. Les factures déjà générées ne sont pas affectées.",
        "tags": [
          "RecurringInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "204": {
            "description": "Désactivée"
          }
        }
      }
    },
    "/v1/recurring-invoices/{id}/pause": {
      "post": {
        "operationId": "pauseRecurringInvoice",
        "summary": "Mettre en pause une facture récurrente",
        "description": "Suspend temporairement la génération automatique de factures.",
        "tags": [
          "RecurringInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Mise en pause"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/recurring-invoices/{id}/resume": {
      "post": {
        "operationId": "resumeRecurringInvoice",
        "summary": "Reprendre une facture récurrente",
        "description": "Reprend la génération automatique de factures après une pause.",
        "tags": [
          "RecurringInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Reprise"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/received-invoices": {
      "get": {
        "operationId": "listReceivedInvoices",
        "summary": "Lister les factures reçues",
        "description": "Retourne les factures reçues via la PA (cycle achat). Supporte la pagination cursor-based et le filtrage par statut.",
        "tags": [
          "ReceivedInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée"
          }
        }
      }
    },
    "/v1/received-invoices/{id}": {
      "get": {
        "operationId": "getReceivedInvoice",
        "summary": "Récupérer une facture reçue",
        "description": "Retourne les détails d'une facture reçue. Envoie automatiquement l'événement fr:204 (prise en charge) à la PA lors de la première consultation.",
        "tags": [
          "ReceivedInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Facture reçue"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/received-invoices/{id}/approve": {
      "post": {
        "operationId": "approveReceivedInvoice",
        "summary": "Approuver une facture reçue",
        "description": "Approuve la facture reçue et envoie l'événement fr:205 à la PA. Statut requis : `received`, `available` ou `suspended`.",
        "tags": [
          "ReceivedInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Approuvée"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/received-invoices/{id}/refuse": {
      "post": {
        "operationId": "refuseReceivedInvoice",
        "summary": "Refuser une facture reçue",
        "description": "Refuse la facture reçue et envoie l'événement fr:206 à la PA avec le motif de refus. Statut requis : `received`, `available` ou `suspended`.",
        "tags": [
          "ReceivedInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Refusée"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/received-invoices/{id}/suspend": {
      "post": {
        "operationId": "suspendReceivedInvoice",
        "summary": "Suspendre une facture reçue",
        "description": "Suspend le traitement de la facture reçue et envoie l'événement fr:207 à la PA. Statut requis : `received` ou `available`. Une facture suspendue peut ensuite être approuvée ou refusée.",
        "tags": [
          "ReceivedInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Suspendue"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/received-invoices/{id}/record-payment": {
      "post": {
        "operationId": "recordReceivedInvoicePayment",
        "summary": "Enregistrer un paiement sur une facture reçue",
        "description": "Enregistre le paiement d'une facture reçue et envoie l'événement fr:212 à la PA. Statut requis : `received`, `available`, `approved` ou `suspended`.",
        "tags": [
          "ReceivedInvoices"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "description": "Montant du paiement en centimes d'euros",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Paiement enregistré",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "const": "received_invoice"
                    },
                    "reconciled": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/exports/fec": {
      "post": {
        "operationId": "exportFec",
        "summary": "Exporter le FEC (Pro, art. A.47 A-1 LPF)",
        "description": "Démarre une génération asynchrone du Fichier des Écritures Comptables. Nécessite un plan Pro.",
        "tags": [
          "Exports"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "period_start": {
                    "type": "string",
                    "format": "date",
                    "example": "2026-01-01"
                  },
                  "period_end": {
                    "type": "string",
                    "format": "date",
                    "example": "2026-12-31"
                  },
                  "send_to_accountant": {
                    "type": "boolean",
                    "default": false
                  },
                  "accountant_email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Génération FEC en cours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/v1/exports/fec/{id}": {
      "get": {
        "operationId": "getFecExportStatus",
        "summary": "Vérifier le statut d'un export FEC",
        "description": "Retourne le statut du job FEC. Si terminé, inclut une URL de téléchargement signée valide 1h.",
        "tags": [
          "Exports"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Statut du job (inclut download_url si terminé)"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/exports/{id}": {
      "get": {
        "operationId": "getExportStatus",
        "summary": "Vérifier le statut d'un export",
        "description": "Retourne le statut d'un job d'export générique. Si terminé, inclut une URL de téléchargement.",
        "tags": [
          "Exports"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Statut du job"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/reporting/vat": {
      "get": {
        "operationId": "getVatReport",
        "summary": "Rapport TVA par période",
        "description": "Calcule le rapport de TVA collectée sur les factures finalisées d'une période donnée. Retourne la ventilation par taux de TVA avec les totaux HT, TVA et TTC. Montants en centimes d'euros. Requiert un plan Essential ou Pro.",
        "tags": [
          "Reporting"
        ],
        "parameters": [
          {
            "name": "period_start",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Début de la période (YYYY-MM-DD)"
          },
          {
            "name": "period_end",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Fin de la période (YYYY-MM-DD)"
          }
        ],
        "responses": {
          "200": {
            "description": "Rapport TVA",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "vat_report"
                    },
                    "period": {
                      "type": "object",
                      "properties": {
                        "start": {
                          "type": "string",
                          "format": "date"
                        },
                        "end": {
                          "type": "string",
                          "format": "date"
                        }
                      }
                    },
                    "vat_breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "rate": {
                            "type": "integer",
                            "description": "Taux TVA (Decimal string, ex: \"20.00\")"
                          },
                          "taxable_amount": {
                            "type": "integer",
                            "description": "Base imposable en centimes"
                          },
                          "vat_amount": {
                            "type": "integer",
                            "description": "Montant TVA en centimes"
                          }
                        }
                      }
                    },
                    "total_ht": {
                      "type": "integer",
                      "description": "Total HT en centimes"
                    },
                    "total_vat": {
                      "type": "integer",
                      "description": "Total TVA en centimes"
                    },
                    "total_ttc": {
                      "type": "integer",
                      "description": "Total TTC en centimes"
                    },
                    "invoice_count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/v1/reporting/revenue": {
      "get": {
        "operationId": "getRevenueReport",
        "summary": "Rapport chiffre d'affaires par période",
        "description": "Calcule le rapport de chiffre d'affaires (factures - avoirs) sur une période donnée. Inclut optionnellement une ventilation par mois ou trimestre. Montants en centimes d'euros. Requiert un plan Essential ou Pro.",
        "tags": [
          "Reporting"
        ],
        "parameters": [
          {
            "name": "period_start",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Début de la période (YYYY-MM-DD)"
          },
          {
            "name": "period_end",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Fin de la période (YYYY-MM-DD)"
          },
          {
            "name": "group_by",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "month",
                "quarter"
              ]
            },
            "description": "Ventiler par mois ou trimestre"
          }
        ],
        "responses": {
          "200": {
            "description": "Rapport chiffre d'affaires",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "revenue_report"
                    },
                    "period": {
                      "type": "object",
                      "properties": {
                        "start": {
                          "type": "string",
                          "format": "date"
                        },
                        "end": {
                          "type": "string",
                          "format": "date"
                        }
                      }
                    },
                    "revenue": {
                      "type": "object",
                      "properties": {
                        "invoiced": {
                          "type": "integer",
                          "description": "Total facturé TTC en centimes"
                        },
                        "credit_notes": {
                          "type": "integer",
                          "description": "Total avoirs TTC en centimes (négatif)"
                        },
                        "net": {
                          "type": "integer",
                          "description": "Chiffre d'affaires net en centimes"
                        }
                      }
                    },
                    "payments": {
                      "type": "object",
                      "properties": {
                        "received": {
                          "type": "integer",
                          "description": "Paiements reçus en centimes"
                        },
                        "outstanding": {
                          "type": "integer",
                          "description": "Reste à encaisser en centimes"
                        }
                      }
                    },
                    "invoice_count": {
                      "type": "integer"
                    },
                    "credit_note_count": {
                      "type": "integer"
                    },
                    "breakdown": {
                      "type": "array",
                      "description": "Ventilation par période (présent si group_by est spécifié)",
                      "items": {
                        "type": "object",
                        "properties": {
                          "period": {
                            "type": "string",
                            "description": "Clé de période (ex: \"2026-03\" ou \"2026-Q1\")"
                          },
                          "revenue": {
                            "type": "object"
                          },
                          "payments": {
                            "type": "object"
                          },
                          "invoice_count": {
                            "type": "integer"
                          },
                          "credit_note_count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          }
        }
      }
    },
    "/v1/archives": {
      "get": {
        "operationId": "listArchives",
        "summary": "Lister les factures archivées",
        "description": "Retourne les informations d'archivage (hash chain) des factures finalisées.",
        "tags": [
          "Archives"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée d'archives"
          }
        }
      }
    },
    "/v1/archives/{invoiceId}": {
      "get": {
        "operationId": "getArchive",
        "summary": "Récupérer les informations d'archivage d'une facture",
        "description": "Retourne le hash SHA-256, le previousHash et les métadonnées d'archivage.",
        "tags": [
          "Archives"
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Informations d'archivage"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/ereporting/declarations": {
      "post": {
        "operationId": "createEreportingDeclaration",
        "summary": "Créer une déclaration e-reporting",
        "description": "Crée une déclaration de transactions exclues du périmètre de la e-facturation (B2C France, intra-EU, export, services hors champ).",
        "tags": [
          "Ereporting"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateEreportingInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Déclaration créée",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EReporting"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "get": {
        "operationId": "listEreportingDeclarations",
        "summary": "Lister les déclarations e-reporting",
        "description": "Retourne l'historique des déclarations e-reporting.",
        "tags": [
          "Ereporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/starting_after"
          },
          {
            "$ref": "#/components/parameters/status"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginée",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "url": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EReporting"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/ereporting/declarations/{id}": {
      "get": {
        "operationId": "getEreportingDeclaration",
        "summary": "Récupérer une déclaration e-reporting",
        "description": "Retourne les détails d'une déclaration e-reporting.",
        "tags": [
          "Ereporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Déclaration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EReporting"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/ereporting/declarations/{id}/submit": {
      "post": {
        "operationId": "submitEreportingDeclaration",
        "summary": "Soumettre une déclaration à la PA",
        "description": "Transmet la déclaration e-reporting via la PA connectée par le client. Utilise un verrou distribué pour empêcher les soumissions concurrentes.\n\n**Refus de la plateforme.** La déclaration passe à l'état `rejected` et y RESTE : elle est la trace de ce qui a été transmis et du motif du refus. La réponse est `422 ereporting_declaration_rejected` et porte le motif. Pour transmettre une nouvelle tentative des mêmes faits, appelez `POST /v1/ereporting/declarations/{id}/retry`. Si c'est le CONTENU qui est en cause, corrigez les documents source (avoir puis réémission) : la période corrigée produit sa propre déclaration. An accepted response persists an obligation owned by Facturino. Temporary failures or uncertain receipt remain visible in obligation and are recovered automatically; the caller must not schedule retries. This manual route is optional: automatic reporting follows the fiscal calendar.",
        "tags": [
          "Ereporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Déclaration soumise",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EReporting"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Verrouillage en cours"
          },
          "422": {
            "description": "La plateforme a refusé la déclaration (`ereporting_declaration_rejected`), ou la déclaration n'est pas dans un état transmissible. La déclaration refusée est conservée.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "operationId": "getJob",
        "summary": "Vérifier le statut d'un job asynchrone",
        "description": "Retourne le statut d'un job (PDF, Factur-X, FEC, import CSV, etc.). Poll ce endpoint pour suivre l'avancement.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Statut du job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/billing/subscription": {
      "get": {
        "operationId": "getSubscription",
        "summary": "Obtenir le statut de l'abonnement",
        "description": "Retourne les détails de l'abonnement Facturino (plan, statut, date de renouvellement).",
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "description": "Abonnement actuel"
          }
        }
      }
    },
    "/v1/account/export": {
      "post": {
        "operationId": "exportAccountData",
        "summary": "Export de données RGPD (article 20)",
        "description": "Exporte toutes les données du compte en JSON. URL signée valide 7 jours.",
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "URL de téléchargement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "export"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/validate": {
      "post": {
        "operationId": "validateInvoiceData",
        "summary": "Valider la conformité d'une facture sans la créer",
        "description": "Vérifie la conformité Factur-X/CIUS-FR sans persister les données. Utile pour valider un formulaire avant soumission.",
        "tags": [
          "Validate"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvoiceInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Résultat de validation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean"
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "schema_version": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sandbox/reset": {
      "post": {
        "operationId": "resetSandbox",
        "summary": "Réinitialiser les données de test",
        "description": "Suppression logique de toutes les données en mode test. Nécessite une clé `fac_test_`.",
        "tags": [
          "Sandbox"
        ],
        "responses": {
          "200": {
            "description": "Données réinitialisées",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "sandbox_reset"
                    },
                    "deletedCount": {
                      "type": "integer",
                      "description": "Nombre de documents de test supprimés (soft-delete)."
                    },
                    "fixturesCreated": {
                      "type": "integer",
                      "description": "Nombre de fixtures sandbox recréées après reset."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/sandbox/simulate-status/{invoiceId}": {
      "post": {
        "operationId": "simulateInvoiceStatus",
        "summary": "Simuler un changement de statut PA (test uniquement)",
        "description": "Simule un changement de statut comme si la PA l'avait envoyé. Déclenche les webhooks associés. Nécessite une clé `fac_test_`.",
        "tags": [
          "Sandbox"
        ],
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "deposited",
                      "transmitted",
                      "available",
                      "received",
                      "approved",
                      "refused",
                      "suspended",
                      "rejected",
                      "paid",
                      "partially_paid",
                      "overdue",
                      "credit_deposited",
                      "credit_transmitted",
                      "credit_approved",
                      "credit_refused"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Statut simulé"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Récupérer le contexte du compte",
        "description": "Endpoint « who am I » : retourne userId, société active, plan, livemode et permissions de la clé courante.",
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "Contexte du compte",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account/exports/{id}/download": {
      "get": {
        "operationId": "downloadAccountExport",
        "summary": "Télécharger un export RGPD",
        "description": "Retourne une URL signée valable 5 minutes pour un export RGPD préparé via `POST /v1/account/export`.",
        "tags": [
          "Account"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "URL de téléchargement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "export_url"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Consommation et limites du plan",
        "description": "Retourne la consommation du mois en cours (factures, devis, clients, produits, membres, endpoints, requêtes API) face aux limites du plan.",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "Consommation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/invoices": {
      "get": {
        "operationId": "listPlatformInvoices",
        "summary": "Lister les factures d'abonnement",
        "description": "Liste les factures émises par INTEK CENTER pour votre abonnement Facturino (50 dernières).",
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "description": "Liste des factures de plateforme",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlatformInvoice"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/invoices/{id}/pdf": {
      "get": {
        "operationId": "getPlatformInvoicePdf",
        "summary": "Télécharger le PDF d'une facture d'abonnement",
        "description": "Retourne une URL signée vers le PDF de la facture de plateforme. Si le PDF est en cours de génération, renvoie 202.",
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "URL du PDF",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "file_url"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "PDF en cours de génération"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/companies/{id}/milestones": {
      "post": {
        "operationId": "setCompanyMilestone",
        "summary": "Marquer un jalon d’onboarding comme atteint",
        "tags": [
          "Companies"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "milestone"
                ],
                "properties": {
                  "milestone": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Jalon enregistré"
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/tax-decisions": {
      "post": {
        "operationId": "createTaxDecision",
        "summary": "Demander une décision fiscale",
        "description": "Décrit une OPÉRATION et reçoit la position fiscale correspondante. L'appelant ne décrit jamais le résultat : taux, catégorie, code VATEX, mention légale, axe de transmission, montants fiscaux, jeu de règles et territoire normalisé sont refusés en entrée et renvoyés en sortie.\n\nLe territoire n'est jamais accepté pré-normalisé : transmettez le pays déclaré et, quand le territoire l'exige, le code postal ; la normalisation est faite côté serveur par le normalisateur territorial unique.\n\n**Idempotence.** Cette ressource porte sa propre idempotence MÉTIER, durable au-delà de la fenêtre HTTP de 24 h — elle n'utilise donc pas le cache d'idempotence de transport. Ce qui est garanti : **une même `Idempotency-Key` accompagnée d'un même corps canonique renvoie toujours la même décision**, sans nouvelle consultation VIES et sans second calcul. Ce qui ne l'est PAS : deux demandes qui décrivent la même opération commerciale sous **deux clés différentes** produisent DEUX décisions distinctes. Rien ne les rapproche automatiquement — c'est la clé qui déclare que deux envois sont la même tentative, et le rapprochement des montants dépend de ce choix.\n\n**Périmètre.** Facturino tranche la TVA française et les obligations françaises correspondantes. Lorsqu'une fiscalité étrangère peut s'appliquer sans changer ce que vous facturez, la décision reste finale et porte `foreignTaxReviewRequired: true` — ce cas doit être vérifié hors Facturino. Lorsqu'une fiscalité locale peut au contraire changer le montant à facturer — ou ce qui est réellement encaissé —, aucune décision finale n'est rendue.\n\n**Collectivités d'outre-mer et TAAF : sept collectivités, sept réponses.** PM, BL, MF, PF, NC, WF et TF n'appartiennent pas à la France au sens de la TVA (CGI art. 256-0), mais chacune légifère sa propre fiscalité indirecte : aucune règle unique ne les couvre, et aucune n'est déduite d'une voisine. Pour une opération SITUÉE dans l'une d'elles, la décision est finale uniquement lorsqu'un texte officiel de la collectivité met sa taxe à la charge de l'acheteur assujetti — c'est le cas de la Nouvelle-Calédonie (art. Lp. 507-1 : autoliquidation par le preneur assujetti). Sinon la réponse est `unsupported`, sans montant, avec un code précis :\n- `com_taaf_local_tax_not_determined` — acheteur NON assujetti (service fourni par voie électronique à un particulier, CGI art. 259 D) : un particulier n'autoliquide rien, seul le fournisseur pourrait être appelé à collecter la taxe de la collectivité ;\n- `com_taaf_local_regime_not_sourced` — acheteur assujetti, mais aucun texte officiel de la collectivité n'établit qui supporte sa taxe ;\n- `com_taaf_payment_withholding_not_modelled` — Polynésie française : le client opère une retenue à la source de 10 % sur le paiement, et la prestation y est taxée localement.\n\nUn vendeur ÉTABLI dans l'une de ces sept collectivités reçoit `seller_com_taaf_local_tax_not_determined`, pour la même raison : c'est la fiscalité locale de sa collectivité qui fixe ce qu'il facture.\n\n**Aucun de ces codes n'a d'issue par la source `integration`.** Une ligne de décision porte `vatRate`, `vatCode` et `vatexCode` — trois champs qui décrivent la TVA française. Ce contrat n'a ni juridiction ni schéma de taxe locale, ni retenue à la source : y déclarer une TGC, une taxe de consommation, un droit local ou une retenue reviendrait à les facturer comme de la TVA française. Tant qu'il n'existe pas de contrat local distinct et complet, ces opérations restent NON FINALES sous LES DEUX sources fiscales, avec le même code et sans montant. La seule exception est celle qui est sourcée : la Nouvelle-Calédonie en B2B, où l'article Lp. 507-1 met la taxe à la charge du preneur assujetti — finale sous les deux sources.\n\n**Les deux sources fiscales suivent la MÊME règle B2C UE.** Une décision `integration` traverse la même territorialité, la même couverture, le même seuil, la même option et le même mécanisme déclaratif : ce qui diffère est seulement l'issue. Là où la source `facturino` PRODUIT le taux, la source `integration` COMPARE celui qui lui est fourni au résultat légal. Égalité : décision finale. Catégorie impossible sur une vente B2C taxée à destination (`AE`, `K`, `G`, `O`) ou `placeOfSupply` contredisant le lieu tranché : `422 integration_vat_incoherent`. La confrontation vaut pour LES DEUX lieux : à destination le résultat légal est le taux normal de l'État membre du consommateur, à l'origine ce sont les bandes publiées du territoire du vendeur. Un taux qu'aucune des deux ne confirme donne `eu_b2c_rate_supplied_mismatch`, décision NON finale et sans montant — Facturino ne détient ni les taux réduits d'un autre État membre ni toutes les bandes françaises, il ne peut donc ni confirmer ni corriger le vôtre. À l'origine, ce refus n'affirme AUCUNE fiscalité étrangère : l'opération est taxée en France. Une règle qui ne conclut pas bloque à l'identique sous les deux sources, en conservant le sens de `pending_verification` (un fait manque) et d'`unsupported` (aucune règle ici). `goodsMovement` est OBLIGATOIRE sur une ligne de biens dès que l'acheteur est un particulier d'un autre État membre : c'est ce mouvement qui décide de la vente à distance, et il n'est jamais supposé.\n\nPour la source `integration`, `placeOfSupply` est OBLIGATOIRE sur chaque ligne dès que l'acheteur est établi dans l'une des sept : c'est le lieu de l'opération qui dit si la taxe de la collectivité est en jeu, et il n'est jamais supposé (`422 integration_vat_incoherent` sinon). Un lieu situé en France — bien qui ne quitte pas le territoire, service général B2C — rend la décision finale comme ailleurs. Aucune taxe locale n'est jamais encodée en TVA française, en taux zéro ni en code VATEX inventé.\n\n**Ventes B2C dans l'Union : quatre décisions, jamais une seule.** Un service fourni par voie électronique à un particulier d'un autre État membre (directive 2006/112/CE art. 58) et une vente à distance intracommunautaire de biens (art. 33, a) relèvent d'un régime commun. Une prestation de services GÉNÉRALE à un particulier n'en relève pas : elle reste taxée là où le fournisseur est établi (art. 45). Quatre questions distinctes sont tranchées dans cet ordre :\n1. l'opération est-elle couverte par une règle à destination ;\n2. le seuil commun de 10 000 € HT — `1 000 000` centimes, plafond INCLUSIF — permet-il encore la taxation à l'origine (art. 59 quater §1) ? Il n'est ouvert qu'au vendeur établi dans un SEUL État membre, et l'opération qui fait franchir le cumul est elle-même taxée à destination ;\n3. le vendeur a-t-il OPTÉ pour la taxation à destination (art. 59 quater §3) ? L'option engage l'année d'exercice et les deux années civiles complètes suivantes ;\n4. comment la taxe due à destination est-elle DÉCLARÉE — guichet unique UE ou immatriculation locale valable pour cet État à cette date ?\n\nLe guichet unique répond à la DERNIÈRE de ces questions : c'est un mode de déclaration et de paiement, pas une règle de lieu. Il ne déplace jamais un lieu de taxation, et son absence ne restaure jamais la TVA française — elle laisse la décision sans montant. Attention cependant : l'inscription au guichet unique peut CONSTATER ou IMPLIQUER la renonciation au régime du seuil pour les opérations concernées, comme l'option de l'art. 59 quater §3. Ce qui produit cet effet est la démarche accomplie auprès de l'administration, pas la déclaration enregistrée dans Facturino : déclarez l'option et l'inscription telles qu'elles existent, avec leurs dates. Une inscription est DATÉE : un guichet ouvert en octobre ne déclare pas une vente de septembre.\n\nLe cumul du seuil vit dans un REGISTRE ANNUEL — `/v1/eu-threshold-ledgers`, un par société, par mode et par année civile — et non sur le profil fiscal : une révision de profil est une règle immuable sur laquelle des décisions ont été figées, un chiffre d'affaires courant bouge à chaque vente et se corrige. Facturino tient le registre des opérations qu'il reçoit ; les ventes réalisées sur vos autres canaux y sont intégrées par la déclaration d'ouverture ou par un ajustement. Rien n'est supposé : aucune année ne démarre à zéro d'elle-même, aucune vente faite ailleurs n'est présumée absente.\n\nCe registre porte DEUX compteurs strictement séparés : le seuil commun de 10 000 € (biens vendus à distance ET services transfrontières) et le seuil de 100 000 € du règlement 282/2011 art. 24 ter, qui décide combien de preuves de localisation sont exigées et ne compte QUE les services fournis par voie électronique. Une vente à distance de biens alimente le premier et jamais le second ; l'allègement à une seule preuve est donc CALCULÉ par le moteur sur ce second compteur, jamais déclaré par le vendeur, et figé sur la décision (`euB2cDestination.evidenceRelief`). Sans données complètes il vaut `undeterminable` : deux preuves sont exigées, et `location_evidence_relief_undetermined` dit quel fait manque.\n\nUne décision RÉSERVE sa tranche du cumul dans une transaction, puis la CONSOMME dans la transaction qui écrit la décision. Si la tranche a DISPARU au moment de l'écriture, la transaction est abandonnée et la décision n'est pas écrite (`409 eu_threshold_reservation_lost`) : figer une décision que le cumul ne porte pas rendrait l'année sous-évaluée pour la suivante. Le registre passe alors en revue, par un chemin distinct qui survit à cet abandon. Sinon : un rejeu de la même `Idempotency-Key` ne compte rien deux fois, et une décision qui n'aboutit pas rend sa tranche. Le cumul qui précède une opération a DEUX bornes — ce qui est acquis à coup sûr, et ce même total augmenté des tranches tenues par les opérations décidées au même instant. Un verdict n'est figé que s'il tient aux deux : c'est ce qui le rend insensible à l'abandon de l'une d'elles, et ce qui permet à une tranche abandonnée de DISPARAÎTRE au lieu de rester dans le total comme un chiffre d'affaires qui n'a jamais existé. Codes non finaux, tous sans montant :\n- `eu_threshold_state_missing` — aucun registre ouvert pour l'année de l'opération ;\n- `eu_threshold_external_coverage_incomplete` — d'autres canaux existent et ne sont pas déclarés complets jusqu'au jour de l'opération : enregistrez un ajustement, même à zéro ;\n- `eu_threshold_backdated_operation` — l'opération précède une opération déjà comptée, et des décisions ont été figées sur ce cumul : il n'est jamais recalculé en silence ;\n- `destination_threshold_operation_value_missing` — la valeur d'une ligne ne peut pas être dimensionnée, donc aucune tranche ne peut être prise ;\n- `destination_threshold_price_mode_ambiguous` — prix TTC : le montant HT dépend du taux, que le seuil doit justement déterminer, et les deux hypothèses tombent de part et d'autre du plafond ;\n- `destination_option_period_invalid` — option déclarée sur une durée plus courte que le minimum légal ;\n- `destination_option_scope_not_sourced` — option déclarée par un vendeur établi hors de France : la durée d'engagement est fixée par l'État membre où l'option est exercée, et seule la durée française est sourcée ici ;\n- `destination_establishment_in_member_state` — le vendeur déclare un établissement dans l'État membre de destination : l'établissement qui intervient décide alors du lieu comme du mécanisme (le guichet unique UE ne couvre pas les services fournis dans un État membre d'établissement), et aucun fait du contrat ne le nomme. Une simple immatriculation locale n'est pas un établissement ;\n- `eu_threshold_concurrent_decision_pending` — 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é, la même opération se décide proprement une fois les autres conclues ;\n- `eu_threshold_review_required` — 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 ;\n- `location_evidence_relief_undetermined` — une seule preuve fournie par un tiers, et l'allègement de l'art. 24 ter n'est pas établi : ouvrez le registre de l'année et déclarez les chiffres « services », ou apportez une seconde preuve ;\n- `destination_mechanism_missing` — taxation à destination due, sans guichet unique UE ni immatriculation locale valable pour cet État à cette date ;\n- `destination_regional_regime_not_sourced` — 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 ici : ni le taux régional ni le taux national ne peuvent être affirmés pour cette famille d'opération ;\n- `franchise_destination_taxation_not_modelled` — vendeur en franchise en base (CGI art. 293 B) dont l'opération est taxée par un autre État membre : c'est le régime des petites entreprises de cet État qui décide, et il n'est pas modélisé.\n\n`euB2cDestination.threshold` fige la tranche du registre elle-même : quel registre, à quelle version, à quelle position dans son ordre total, sous quel mode de couverture, avec les chiffres d'ouverture, les ajustements intégrés, les DEUX bornes du cumul avant et le nombre d'opérations concurrentes qu'elles comptent, la valeur de l'opération et le cumul après. `euB2cDestination.mechanism` porte l'inscription DATÉE qui a servi l'opération.\n\n**Taux de destination.** Le taux vient d'un registre LOCAL, daté et versionné : aucun appel réseau pendant une décision, et une décision rejouée des années plus tard reproduit le même taux. Seul le taux NORMAL des 27 États membres est tabulé — le périmètre des taux réduits suit une classification nationale que Facturino ne détient pas, et un service électronique ordinaire n'est jamais assimilé à une publication électronique : `destination_rate_band_not_available`. Une date antérieure au registre donne `destination_rate_not_sourced_for_date`. Une RÉGION qui publie son propre taux standard n'entraîne jamais le blocage d'un pays entier ; ce qui la gouverne décide de la réponse :\n- lorsque le taux suit le LIEU de l'opération, la région est un territoire canonique à part entière, résolu par le normalisateur territorial unique et taxé comme tout autre territoire. C'est le cas du Portugal : `PT-CO` continent 23 %, `PT-MA` Madère 22 %, `PT-AC` Açores 16 % (CIVA art. 18, plages postales CTT). Seule une adresse portugaise SANS code postal ne place aucune région : `destination_regional_scope_undetermined`, avec le fait manquant, jamais le taux du continent par défaut ;\n- lorsque le taux est réservé aux opérations réalisées dans la zone par un fournisseur qui y est ÉTABLI, un vendeur distant ne l'acquiert pas par l'adresse du client : le taux national est la réponse, FINALE, et aucune cartographie postale n'est nécessaire pour le dire. C'est le cas de Jungholz et du Kleinwalsertal en Autriche (§ 10(4) UStG — 20 %, pas 19 %), et du régime insulaire grec POUR LES SERVICES, que l'AADE réserve au fournisseur établi dans l'île pour une opération réalisée sur place : un service électronique fourni depuis la France est taxé 24 %, à Athènes comme à Kalymnos ;\n- lorsque le taux suit au contraire la DESTINATION de l'opération et que la zone n'est pas cartographiée par ce registre, ni le taux régional ni le taux national ne peuvent être affirmés : `destination_regional_regime_not_sourced`, décision NON finale et sans montant. C'est le cas de 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, et la liste de ces îles n'est pas cartographiée par code postal ici. Une vente à distance de biens vers la Grèce est donc refusée — à Athènes comme à Kalymnos — jamais taxée 24 % par défaut. La réponse est ainsi rendue par FAMILLE d'opération : un même État membre peut être tranché pour les services et ouvert pour les biens.\n\n**Axes tranchés malgré un blocage.** Une décision non finale garde ses trois axes de premier niveau à `null` — on ne lit pas un axe sur une décision qui n'aboutit pas — mais ce que le droit français a déjà tranché n'est pas perdu pour autant : `settledObligations` le porte comme VALEUR, et non comme phrase dans `obligationReasons`. Cet objet n'autorise rien : dépôt sur une plateforme agréée et paiement restent interdits tant que la décision n'est pas finale.\n\n**Contrat d'idempotence.** L'en-tête `Idempotency-Key` est OBLIGATOIRE (255 caractères au plus). `201` à la création, `200` au rejeu de la même clé avec le même corps, `409` si la même clé est réutilisée avec un corps différent ou si une décision portant cette clé est encore en cours de calcul.\n\n**Immuabilité.** Une décision ne se modifie pas : `PATCH`, `PUT` et `DELETE` répondent `405`.\n\n**Rejeu avec de nouvelles preuves — à ne pas confondre avec l'idempotence.** L'idempotence rejoue une décision DÉJÀ prise, à l'identique. Le rejeu avec preuves en prend une NOUVELLE, sur la même opération commerciale et des faits enrichis : envoyez une nouvelle requête, avec une **nouvelle `Idempotency-Key`**, les preuves complétées, et `retryOfTaxDecisionId` pointant la décision suspendue. La filiation est vérifiée : seule une décision `pending_verification` se reprend, l'empreinte de l'opération commerciale doit être identique (client, date, devise, mode de prix, lignes, quantités, prix, remises), et le profil vendeur comme le jeu de règles doivent être restés les mêmes. Sinon : `422 tax_decision_retry_invalid`. Réutiliser l'ancienne clé ne rejouerait rien de neuf — elle renverrait la décision suspendue.",
        "tags": [
          "TaxDecisions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTaxDecisionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rejeu : cette `Idempotency-Key`, accompagnée du MÊME corps canonique, avait déjà produit cette décision — renvoyée à l'identique. Aucune seconde décision n'est prise, et aucune consultation VIES n'est refaite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxDecision"
                }
              }
            }
          },
          "201": {
            "description": "Décision créée. Une décision `final` porte les montants ; une décision `pending_verification` ou `unsupported` renvoie `totals` et `amountToCharge` à `null` — jamais `0`. Une demande invalide est refusée par une erreur HTTP et n'est pas persistée comme décision. Ne finalisez aucun document fiscal et n'encaissez aucun règlement immédiat sur une décision non finale.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxDecision"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`Idempotency-Key` déjà utilisée avec un corps canonique différent, ou décision portant cette clé encore en cours de calcul (fenêtre de 60 s). Aucune décision n'est créée. Pour trancher une autre opération, utilisez une autre clé ; pour re-trancher la même sur de nouvelles preuves, utilisez une nouvelle clé et `retryOfTaxDecisionId`. · `eu_threshold_reservation_lost` : la tranche du registre a disparu avant l'écriture ; la décision n'est pas créée et le registre passe en revue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyRequired"
          }
        ]
      }
    },
    "/v1/tax-decisions/{id}": {
      "get": {
        "operationId": "getTaxDecision",
        "summary": "Lire une décision fiscale",
        "description": "Une décision est IMMUABLE : elle reste lisible indéfiniment, y compris après expiration. `expired: true` indique qu'elle ne peut plus ouvrir un paiement, sans altérer ce qu'elle a décidé.",
        "tags": [
          "TaxDecisions"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Identifiant `taxdec_`"
          }
        ],
        "responses": {
          "200": {
            "description": "Décision",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxDecision"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "patchTaxDecisionNotAllowed",
        "summary": "Non autorisé — une décision est immuable",
        "description": "Une décision fiscale ne se modifie pas. Demandez-en une nouvelle, éventuellement avec `retryOfTaxDecisionId`.",
        "tags": [
          "TaxDecisions"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "405": {
            "description": "Méthode non autorisée"
          }
        }
      },
      "put": {
        "operationId": "putTaxDecisionNotAllowed",
        "summary": "Non autorisé — une décision est immuable",
        "tags": [
          "TaxDecisions"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "405": {
            "description": "Méthode non autorisée"
          }
        }
      },
      "delete": {
        "operationId": "deleteTaxDecisionNotAllowed",
        "summary": "Non autorisé — une décision est immuable",
        "description": "Une décision déjà utilisée par un document légal n'est jamais supprimée.",
        "tags": [
          "TaxDecisions"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "405": {
            "description": "Méthode non autorisée"
          }
        }
      }
    },
    "/v1/ereporting/declarations/{id}/retry": {
      "post": {
        "operationId": "retryEreportingDeclaration",
        "summary": "Préparer une nouvelle tentative d'une déclaration refusée",
        "description": "Crée la tentative SUIVANTE d'une déclaration que la plateforme a refusée.\n\nLa déclaration refusée n'est jamais remise dans un état transmissible : elle est la trace légale de ce qui a été envoyé et du motif du refus. La nouvelle tentative est une déclaration DISTINCTE, chaînée dans les deux sens (`supersedesDeclarationId` sur la nouvelle, `supersededByDeclarationId` sur l'ancienne), avec un identifiant déterministe dérivé des mêmes faits et du numéro de tentative.\n\n**Ce que cet appel fait, et ne fait pas.** Il RÉSERVE la tentative — elle naît à l'état `reserved`, et rien n'est encore transmis à la plateforme. La transmission se produit ensuite de deux façons : le traitement planifié transmet automatiquement les déclarations réservées au prochain passage, ou vous la déclenchez explicitement avec `POST /v1/ereporting/declarations/{id}/submit` sur la NOUVELLE déclaration.\n\n**Idempotent.** Rejouer l'appel ne crée jamais une seconde tentative : la réponse est `200` avec `replayed: true` et la tentative existante, y compris si vous aviez perdu la première réponse. Le chaînage est VÉRIFIÉ avant d'être suivi — même établissement, même mode, lien retour intact — et une chaîne qui ne tient pas est signalée plutôt que masquée par un `200`.\n\n**Ce que la reprise ne fait pas.** Elle ne modifie pas les faits. Si la plateforme a refusé le CONTENU, le renvoyer à l'identique sera refusé de nouveau : corrigez les documents source (avoir puis réémission), et la période corrigée produira sa propre déclaration. Au-delà de cinq tentatives, l'API le dit explicitement (`ereporting_attempt_limit_reached`).\n\nIntroduit par le contrat `2026-09-01`. Plan Essential, Pro ou Cabinets requis.",
        "tags": [
          "Ereporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/resourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "La tentative existait déjà (rejeu) : elle est renvoyée telle quelle, et aucune seconde déclaration n'est créée.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/EReporting"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "replayed": {
                          "type": "boolean",
                          "description": "Propre à cette opération : `false` quand cet appel a créé la tentative (`201`), `true` quand elle existait déjà et est renvoyée telle quelle (`200`). Le corps est par ailleurs la ressource `ereporting` complète."
                        }
                      },
                      "required": [
                        "replayed"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Nouvelle tentative créée, à l'état `reserved`. Rien n'a encore été transmis : le traitement planifié la transmettra au prochain passage, ou appelez `POST /v1/ereporting/declarations/{id}/submit` sur la NOUVELLE déclaration.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/EReporting"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "replayed": {
                          "type": "boolean",
                          "description": "Propre à cette opération : `false` quand cet appel a créé la tentative (`201`), `true` quand elle existait déjà et est renvoyée telle quelle (`200`). Le corps est par ailleurs la ressource `ereporting` complète."
                        }
                      },
                      "required": [
                        "replayed"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Aucune nouvelle tentative possible : `ereporting_declaration_not_rejected`, `ereporting_declaration_already_superseded`, `ereporting_attempt_limit_reached` ou `ereporting_declaration_incomplete`. Chaque erreur nomme l'action réellement possible.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers": {
      "post": {
        "operationId": "openEuThresholdLedger",
        "summary": "Ouvrir une année du registre du seuil",
        "description": "Déclare l'ouverture d'une année civile : le total HT des ventes couvertes de l'année précédente, celui déjà réalisé cette année, et le MODE DE COUVERTURE.\n\nLes ventes hors Facturino ne sont jamais supposées absentes. `facturino_only` est une déclaration du vendeur — toutes les opérations couvertes passent par Facturino ; `mixed_channels` dit que d'autres canaux existent, et le registre ne sert alors une décision que jusqu'au jour où ces canaux sont déclarés complets. Un ajustement fait avancer ce jour.\n\nUne année déjà ouverte n'est jamais réécrite (`eu_threshold_year_already_open`) : des décisions y ont été figées. La correction se fait par ajustement, et l'historique reste intact.",
        "tags": [
          "EuThresholdLedgers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenEuThresholdLedgerInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Année ouverte.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EuThresholdLedger"
                }
              }
            }
          },
          "401": {
            "description": "Clé API absente ou invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Portée insuffisante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "L'état du registre refuse la demande.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers/{year}": {
      "parameters": [
        {
          "name": "year",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\d{4}$"
          }
        }
      ],
      "get": {
        "operationId": "getEuThresholdLedger",
        "summary": "Lire le registre d'une année",
        "description": "Renvoie la déclaration d'ouverture, les totaux acquis, ce qui reste sous le plafond, les tranches actuellement tenues par des décisions en cours et les derniers mouvements. Les tranches tenues comptent déjà dans le total : c'est ce qui empêche deux opérations simultanées de partir du même chiffre.",
        "tags": [
          "EuThresholdLedgers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Le registre.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EuThresholdLedger"
                }
              }
            }
          },
          "401": {
            "description": "Clé API absente ou invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Portée insuffisante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Aucun registre ouvert pour cette année, dans ce mode et cette société.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            },
            "description": "Taille de la page de mouvements incluse dans la réponse."
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Curseur : identifiant du dernier mouvement de la page précédente."
          }
        ]
      },
      "patch": {
        "operationId": "patchEuThresholdLedger",
        "summary": "Non autorisé — le registre est append-only",
        "description": "Une ouverture n'est jamais réécrite et un mouvement jamais supprimé : la correction est un ajustement de plus, que l'audit montre.",
        "tags": [
          "EuThresholdLedgers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "405": {
            "description": "Ressource immuable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "putEuThresholdLedger",
        "summary": "Non autorisé — le registre est append-only",
        "description": "Une ouverture n'est jamais réécrite et un mouvement jamais supprimé : la correction est un ajustement de plus, que l'audit montre.",
        "tags": [
          "EuThresholdLedgers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "405": {
            "description": "Ressource immuable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteEuThresholdLedger",
        "summary": "Non autorisé — le registre est append-only",
        "description": "Une ouverture n'est jamais réécrite et un mouvement jamais supprimé : la correction est un ajustement de plus, que l'audit montre.",
        "tags": [
          "EuThresholdLedgers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "405": {
            "description": "Ressource immuable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers/{year}/adjustments": {
      "parameters": [
        {
          "name": "year",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\d{4}$"
          }
        }
      ],
      "post": {
        "operationId": "recordEuThresholdAdjustment",
        "summary": "Enregistrer un chiffre réalisé hors Facturino",
        "description": "Ajoute au registre le chiffre d'affaires couvert réalisé sur un autre canal, et fait avancer le jour jusqu'auquel ces canaux sont complets. Le montant va par PAIRE : `amount` pour le seuil commun de 10 000 €, `evidenceAmount` pour la part faite de services fournis par voie électronique — le périmètre du seuil de 100 000 € des preuves de localisation, qui ne compte jamais une vente à distance de biens.\n\nL'écriture est append-only et IDEMPOTENTE sur votre propre référence (`reference`), comparée sur une empreinte canonique du corps ENTIER : rejouer le même ajustement n'ajoute rien, et réutiliser la référence pour un autre montant, une autre date, une autre répartition ou un autre motif répond `eu_threshold_entry_conflict`. Votre référence ne devient jamais un identifiant de document : elle est hachée, et conservée telle quelle comme donnée.\n\nUn montant NÉGATIF reste impossible ici. Retirer un montant déjà compté est une CORRECTION qualifiée (`POST /v1/eu-threshold-ledgers/{year}/corrections`) : la directive 2006/112/CE art. 90 §1 réduit la base d'imposition d'une opération identifiée, donc la correction nomme le mouvement qu'elle corrige, sa qualification, la ressource sur laquelle elle repose et sa preuve, et ne rend jamais plus que ce que ce mouvement avait apporté. Ce qui ne peut pas être qualifié ainsi n'est pas soustrait : le registre passe en revue (`POST /v1/eu-threshold-ledgers/{year}/review`) et cesse de décider.",
        "tags": [
          "EuThresholdLedgers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EuThresholdAdjustmentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Mouvement enregistré ; le registre à jour est renvoyé.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EuThresholdLedger"
                }
              }
            }
          },
          "401": {
            "description": "Clé API absente ou invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Portée insuffisante.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Aucun registre ouvert pour cette année, dans ce mode et cette société.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`eu_threshold_entry_conflict` (même référence, corps différent), `eu_threshold_state_missing`, `eu_threshold_date_outside_year`, `eu_threshold_date_in_future`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers/{year}/entries": {
      "parameters": [
        {
          "name": "year",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\d{4}$"
          }
        }
      ],
      "get": {
        "tags": [
          "EuThresholdLedgers"
        ],
        "summary": "Paginer les mouvements du registre",
        "description": "Les mouvements du registre, du plus récent au plus ancien, page par page. Le registre les conserve tous : une page n'en montre qu'une partie, et `next_cursor` donne la suivante.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Une page de mouvements.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "url": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EuThresholdEntry"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Aucun registre pour cette année, ce mode ou ce tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers/{year}/corrections": {
      "parameters": [
        {
          "name": "year",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\d{4}$"
          }
        }
      ],
      "post": {
        "tags": [
          "EuThresholdLedgers"
        ],
        "summary": "Rendre un montant qualifié",
        "description": "Sort du cumul courant un montant qu'un mouvement IDENTIFIÉ de ce registre avait apporté, au titre de la réduction de base de la directive 2006/112/CE art. 90 §1. Les décisions déjà figées ne sont jamais réécrites : elles restaient exactes sur les chiffres de leur jour. Seul le total que les PROCHAINES opérations liront est corrigé.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EuThresholdCorrectionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Le registre après la correction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EuThresholdLedger"
                }
              }
            }
          },
          "422": {
            "description": "`eu_threshold_correction_target_unknown`, `eu_threshold_correction_target_not_correctable`, `eu_threshold_correction_exceeds_counted` (solde du mouvement épuisé), `eu_threshold_entry_conflict`, `eu_threshold_state_missing`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers/{year}/review": {
      "parameters": [
        {
          "name": "year",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\d{4}$"
          }
        }
      ],
      "post": {
        "tags": [
          "EuThresholdLedgers"
        ],
        "summary": "Mettre le registre en revue",
        "description": "Arrête les décisions sur ce registre : son cumul est reconnu faux, et une décision est figée sur les chiffres qu'elle lit. Toute réservation répond alors `eu_threshold_review_required`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EuThresholdReviewInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Le registre, en revue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EuThresholdLedger"
                }
              }
            }
          },
          "422": {
            "description": "`eu_threshold_state_missing`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/eu-threshold-ledgers/{year}/review/resolve": {
      "parameters": [
        {
          "name": "year",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\d{4}$"
          }
        }
      ],
      "post": {
        "tags": [
          "EuThresholdLedgers"
        ],
        "summary": "Clore la revue",
        "description": "Le registre sert de nouveau les décisions — sur une RÉCONCILIATION qui concorde, jamais sur un commentaire. Voir `EuThresholdReviewResolutionInput`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EuThresholdReviewResolutionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Le registre, de nouveau ouvert.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EuThresholdLedger"
                }
              }
            }
          },
          "422": {
            "description": "`eu_threshold_reconciliation_stale`, `eu_threshold_reconciliation_mismatch`, `eu_threshold_not_under_review`, `eu_threshold_state_missing`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Clé API Facturino. Préfixe `fac_test_` pour le mode test, `fac_live_` pour la production."
      }
    },
    "parameters": {
      "resourceId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Identifiant unique de la ressource (préfixé, ex: `inv_`, `cus_`)"
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        },
        "description": "Nombre de résultats par page (max 100)"
      },
      "starting_after": {
        "name": "starting_after",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "ID du dernier élément de la page précédente. Si ce curseur a été supprimé, n’existe pas ou appartient à une autre ressource : 400 invalid_field_value, param starting_after. Recommencez sans curseur pour repartir de la première page."
      },
      "status": {
        "name": "status",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Filtrer par statut"
      },
      "include_deleted": {
        "name": "include_deleted",
        "in": "query",
        "schema": {
          "type": "boolean",
          "default": false
        },
        "description": "Inclure les ressources supprimées (soft-delete)"
      },
      "date_from": {
        "name": "date_from",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Filtrer sur la date d'émission à partir de cette date (incluse, `YYYY-MM-DD`)"
      },
      "date_to": {
        "name": "date_to",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Filtrer sur la date d'émission jusqu'à cette date (incluse, `YYYY-MM-DD`)"
      },
      "sort": {
        "name": "sort",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "created",
            "updated"
          ],
          "default": "created"
        },
        "description": "Champ de tri (décroissant)"
      },
      "expand": {
        "name": "expand",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Hydrater des ressources référencées, séparées par des virgules. Valeurs : `customer`, `items.product`. Les données apparaissent sous `expanded` (la forme canonique n'est pas modifiée)."
      },
      "idempotencyKeyRequired": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Clé d'idempotence MÉTIER, obligatoire sur cette ressource. Une décision fiscale précède un encaissement : sans clé, une tentative rejouée trancherait une seconde décision, et deux montants différents pourraient être débités pour une même opération. Le rejeu de la MÊME clé avec le MÊME corps canonique (clés JSON comparées dans un ordre stable, ordre des lignes significatif) renvoie la décision déjà prise (`200`) ; la même clé avec un corps différent est refusée (`409`). Deux clés différentes restent deux demandes : elles produisent deux décisions, même si l'opération décrite est la même. La garantie est durable — elle ne dépend pas de la fenêtre HTTP de 24 h.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        },
        "example": "op-2026-09-15-cus_abc-001"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Requête invalide",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Clé API manquante ou invalide",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Fonctionnalité réservée à un plan supérieur",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Échec de validation (Zod strict)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Accès refusé (scope ou rôle insuffisant)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Ressource introuvable",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Limite de débit atteinte. Consultez les headers `X-RateLimit-*` et `Retry-After`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflit d'idempotence ou de concurrence.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Account": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "account"
          },
          "userId": {
            "type": "string"
          },
          "companyId": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "essential",
              "pro"
            ]
          },
          "livemode": {
            "type": "boolean"
          },
          "apiKeyPrefix": {
            "type": "string",
            "description": "Préfixe de la clé utilisée (`fac_test_` ou `fac_live_`)"
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write"
              ]
            }
          },
          "company": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "siret": {
                "type": "string"
              },
              "vatRegime": {
                "type": "string"
              },
              "vatOnDebits": {
                "type": "boolean",
                "description": "VAT exigible on debits (at invoicing) rather than on collections. Independent of the declarative regime."
              }
            }
          },
          "user": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "email": {
                "type": "string"
              },
              "displayName": {
                "type": "string"
              },
              "emailVerified": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "UsageCounter": {
        "type": "object",
        "properties": {
          "used": {
            "type": "integer"
          },
          "limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`null` = illimité"
          }
        }
      },
      "Usage": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "usage"
          },
          "plan": {
            "type": "string"
          },
          "periodStart": {
            "type": "string",
            "format": "date-time"
          },
          "counters": {
            "type": "object",
            "properties": {
              "apiRequestsMonth": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "invoicesMonth": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "quotesMonth": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "customers": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "products": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "members": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "webhookEndpoints": {
                "$ref": "#/components/schemas/UsageCounter"
              },
              "companies": {
                "$ref": "#/components/schemas/UsageCounter"
              }
            }
          }
        }
      },
      "PlatformInvoice": {
        "type": "object",
        "description": "Facture d’abonnement émise par INTEK CENTER vers l’abonné Facturino.",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "platform_invoice"
          },
          "status": {
            "type": "string"
          },
          "number": {
            "type": "string"
          },
          "totals": {
            "type": "object"
          },
          "dates": {
            "type": "object"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                },
                "lineTotal": {
                  "type": "integer"
                }
              }
            }
          },
          "metadata": {
            "type": "object"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "message"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request_error",
                  "validation_error",
                  "authentication_error",
                  "permission_error",
                  "not_found_error",
                  "conflict_error",
                  "rate_limit_error",
                  "plan_limit_error",
                  "api_error"
                ],
                "description": "Type d'erreur"
              },
              "code": {
                "type": "string",
                "description": "Code machine. Codes fiscaux introduits avec le moteur de décision :\n\n- `tax_decision_required` — le document n'a pas de décision fiscale : ses obligations n'ont jamais été arrêtées et ne sont pas devinées. Demandez une décision, puis réémettez. Le téléchargement Factur-X/XML et le dépôt manuel restent disponibles.\n- `tax_snapshot_inconsistent` — la position figée du document est incomplète ; elle n'est jamais complétée depuis des données vivantes.\n- `not_einvoicing_channel` — la décision place l'opération hors du canal de facturation électronique (B2C, acheteur étranger, Guyane, Mayotte, collectivités d'outre-mer). Elle ne se dépose pas sur une plateforme agréée ; son obligation d'e-reporting reste due.\n- `deposit_not_enabled` — le dépôt réseau est désactivé pour cet établissement. L'obligation d'e-reporting est inchangée.\n- `pa_not_configured` — aucune plateforme agréée n'est connectée.\n- `ereporting_not_enabled` — l'e-reporting automatique n'est pas activé pour cet établissement.\n- `tax_decision_not_final` — la décision n'est pas finale : aucun montant n'est arrêté et aucune facture ne peut s'y adosser.\n- `tax_decision_expired` — la décision a expiré ; demandez-en une nouvelle.\n- `tax_decision_customer_mismatch` / `tax_decision_buyer_drifted` — l'acheteur n'est plus celui sur lequel la décision a été prise.\n- `tax_decision_lines_mismatch` — les lignes de présentation ne couvrent pas exactement la décision.\n- `tax_decision_totals_inconsistent` — la décision est incohérente et n'est pas utilisée.\n- `not_supported` — le cas n'est pas tranché par Facturino, ou la plateforme ne prend pas en charge le flux demandé ; l'obligation reste due et visible.\n- `tax_profile_missing` / `tax_profile_incomplete` / `tax_profile_ambiguous` — le profil fiscal vendeur ne couvre pas la date d'effet, une déclaration y manque, ou deux révisions se recouvrent. Aucune n'est devinée : corrigez le profil, puis redemandez la décision.\n- `tax_decision_input_rejected` — la demande ne relève d'aucun jeu de règles ; elle n'est pas persistée comme décision.\n- `tax_decision_retry_invalid` — la décision visée par `retryOfTaxDecisionId` ne peut pas être reprise (elle est finale, non reprenable, ou porte une autre opération commerciale).\n- `tax_decision_seller_territory_unresolved` — l'établissement du vendeur ne se résout pas en territoire canonique ; aucune position n'est figée sur un identifiant approximatif.\n- `ereporting_declaration_rejected` — la plateforme a refusé la déclaration. Elle est CONSERVÉE comme trace du refus ; `POST /v1/ereporting/declarations/{id}/retry` en transmet une nouvelle tentative.\n- `ereporting_declaration_not_rejected` — seule une déclaration refusée peut être retentée.\n- `ereporting_declaration_already_superseded` — une nouvelle tentative existe déjà ; suivez-la.\n- `ereporting_attempt_limit_reached` — le contenu lui-même doit être corrigé (avoir puis réémission) : le renvoyer à l'identique serait refusé de nouveau.\n- `ereporting_declaration_incomplete` — la déclaration stockée ne porte pas les faits nécessaires à une nouvelle tentative.\n- `idempotency_error` — voir l'en-tête `Idempotency-Key` de la ressource concernée."
              },
              "message": {
                "type": "string",
                "description": "Message lisible"
              },
              "param": {
                "type": "string",
                "nullable": true,
                "description": "Champ en erreur"
              },
              "doc_url": {
                "type": "string",
                "nullable": true,
                "description": "Lien vers la documentation"
              },
              "request_id": {
                "type": "string",
                "description": "ID de requête pour le support"
              },
              "hint": {
                "type": "string",
                "nullable": true,
                "description": "Suggestion de correction"
              },
              "issues": {
                "type": "array",
                "description": "FACULTATIF. Raisons détaillées d'un même refus, quand il en porte plusieurs. 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 — lorsqu'un refus n'a rien de plus à dire. Un refus publie au plus 500 raisons, chaque `message` au plus 2 000 caractères — mêmes bornes sur la première réponse et sur son rejeu.\n\nChaque entrée porte un `code` stable, plus précis que le code principal lorsque celui-ci recouvre plusieurs faits : un territoire client qui ne se résout pas répond `validation_error` en principal et, selon le fait, `invalid_postal_code` ou `territory_conflict` en détail.\n\n`param` n'est renseigné que lorsque la correspondance vaut pour toute requête — `invalid_postal_code` → `customer.address.postalCode`, `territory_conflict` → `customer.address.country`, une raison portée par une ligne → `lines[<index>]` — et vaut `null` sinon : un pointeur approximatif enverrait corriger un champ qui était juste.",
                "items": {
                  "$ref": "#/components/schemas/ErrorIssue"
                }
              }
            }
          }
        },
        "description": "Erreur au format Stripe-like. Un `409 conflict` sur une décision déjà consommée signale qu'une décision finale n'adosse qu'UNE facture : demandez une nouvelle décision pour une nouvelle facture."
      },
      "PaginatedList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "url": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {}
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InvoiceStatus": {
        "type": "string",
        "enum": [
          "draft",
          "finalized",
          "sending",
          "deposited",
          "transmitted",
          "rejected",
          "available",
          "received",
          "approved",
          "refused",
          "suspended",
          "partially_paid",
          "paid",
          "overdue"
        ]
      },
      "LifecycleEntry": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "readOnly": true
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "source": {
            "type": "string",
            "enum": [
              "user",
              "system",
              "pa"
            ],
            "readOnly": true
          },
          "details": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "code": {
            "type": "string",
            "readOnly": true,
            "description": "Clé de message structurée ; absente des lignes historiques."
          },
          "params": {
            "type": "object",
            "readOnly": true,
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            },
            "description": "Paramètres du message, valeurs textuelles ou numériques."
          }
        },
        "readOnly": true
      },
      "LineItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Préfixe `li_`"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "quantity": {
            "type": "string",
            "description": "Decimal string"
          },
          "unit": {
            "type": "string",
            "enum": [
              "unit",
              "hour",
              "day",
              "month",
              "flat_rate",
              "kg",
              "m",
              "m2",
              "m3",
              "l"
            ]
          },
          "unitPrice": {
            "type": "integer",
            "description": "Prix unitaire en centimes entiers (ex: 15000 = 150,00 €)"
          },
          "discountPercent": {
            "type": "integer"
          },
          "lineAmount": {
            "type": "integer",
            "description": "Montant HT de la ligne"
          },
          "vatRate": {
            "type": "integer",
            "description": "Taux de TVA en centièmes de pourcent (ex: 2000 = 20,00 %)"
          },
          "vatCode": {
            "type": "string",
            "enum": [
              "S",
              "Z",
              "E",
              "AE",
              "G",
              "IC",
              "K",
              "O",
              "VATEX-FR-FRANCHISE"
            ]
          },
          "vatexCode": {
            "type": "string",
            "description": "Code VATEX (BT-121) précisant le fondement réel de l'exonération quand il diffère du défaut de la catégorie (ex. formation Qualiopi art. 261-4-4°a, régime de marge, DOM). Sinon, le défaut de la catégorie s'applique.",
            "enum": [
              "VATEX-EU-AE",
              "VATEX-EU-D",
              "VATEX-EU-F",
              "VATEX-EU-G",
              "VATEX-EU-IC",
              "VATEX-EU-O",
              "VATEX-FR-FRANCHISE",
              "VATEX-FR-CNWVAT",
              "VATEX-FR-AUTOLIQ",
              "VATEX-FR-261"
            ]
          },
          "vatAmount": {
            "type": "integer"
          },
          "lineTotal": {
            "type": "integer",
            "description": "Montant TTC de la ligne"
          },
          "product": {
            "type": "string",
            "nullable": true,
            "description": "ID produit (préfixe `prod_`)"
          },
          "taxLineRef": {
            "type": "string",
            "description": "Référence de la ligne décidée que cette ligne rend. Présente sur un document adossé à une décision."
          },
          "priceMode": {
            "$ref": "#/components/schemas/PriceMode"
          },
          "kind": {
            "type": "string",
            "enum": [
              "goods",
              "service"
            ],
            "description": "Nature du DOCUMENT, dérivée de la catégorie décidée — jamais affirmée par le client sur une ligne décidée."
          }
        }
      },
      "LineItemInput": {
        "type": "object",
        "required": [
          "description",
          "quantity",
          "unitPrice",
          "vatRate",
          "vatCode",
          "unit"
        ],
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "quantity": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d{1,4})?$",
            "description": "Decimal string (e.g., '5', '2.5000')"
          },
          "unitPrice": {
            "type": "integer",
            "description": "Prix unitaire en centimes d'euros"
          },
          "vatRate": {
            "type": "integer",
            "description": "Taux TVA en centièmes de pourcent (2000 = 20%)"
          },
          "vatCode": {
            "type": "string",
            "enum": [
              "S",
              "Z",
              "E",
              "AE",
              "G",
              "IC",
              "K",
              "O",
              "VATEX-FR-FRANCHISE"
            ]
          },
          "vatexCode": {
            "type": "string",
            "description": "Optionnel. Code VATEX (BT-121) précisant le fondement réel de l'exonération quand il diffère du défaut de la catégorie (ex. formation Qualiopi art. 261-4-4°a, régime de marge, DOM, sous-traitance BTP).",
            "enum": [
              "VATEX-EU-AE",
              "VATEX-EU-D",
              "VATEX-EU-F",
              "VATEX-EU-G",
              "VATEX-EU-IC",
              "VATEX-EU-O",
              "VATEX-FR-FRANCHISE",
              "VATEX-FR-CNWVAT",
              "VATEX-FR-AUTOLIQ",
              "VATEX-FR-261"
            ]
          },
          "unit": {
            "type": "string",
            "enum": [
              "unit",
              "hour",
              "day",
              "month",
              "flat_rate",
              "kg",
              "m",
              "m2",
              "m3",
              "l"
            ]
          },
          "discountPercent": {
            "type": "integer",
            "default": 0,
            "description": "Remise en centièmes de pourcent"
          },
          "product": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Totals": {
        "type": "object",
        "properties": {
          "totalHT": {
            "type": "integer"
          },
          "discountAmount": {
            "type": "integer"
          },
          "vatBreakdown": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rate": {
                  "type": "integer"
                },
                "code": {
                  "type": "string"
                },
                "base": {
                  "type": "integer"
                },
                "amount": {
                  "type": "integer"
                }
              }
            }
          },
          "totalVAT": {
            "type": "integer"
          },
          "totalTTC": {
            "type": "integer"
          },
          "amountDue": {
            "type": "integer"
          },
          "amountPaid": {
            "type": "integer"
          }
        }
      },
      "WebhookEventData": {
        "type": "object",
        "readOnly": true,
        "additionalProperties": true,
        "description": "Vue additive des champs de data. La présence, les champs obligatoires et leur sens dépendent du type de l’événement : consulter les variantes discriminées de WebhookEvent. Les montants des événements sont des chaînes décimales en euros.",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "previous_status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé précédent lors d’une transition."
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "number": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro du document ; null avant numérotation."
          },
          "documentStatus": {
            "$ref": "#/components/schemas/DocumentStatus",
            "readOnly": true
          },
          "transmissionStatus": {
            "$ref": "#/components/schemas/TransmissionStatus",
            "readOnly": true
          },
          "transmissionDetail": {
            "$ref": "#/components/schemas/TransmissionDetail",
            "readOnly": true
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus",
            "readOnly": true
          },
          "paErrorCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du dernier échec de dépôt : contrôle local ou réponse de la plateforme.",
            "readOnly": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true,
            "description": "Motif brut de la plateforme ou de l’acheteur, verbatim ; constat local pour la source facturino. Ne contient pas de code ou de note reconstruits. Null si absent.",
            "readOnly": true
          },
          "rejectionCategory": {
            "type": "string",
            "nullable": true,
            "enum": [
              "buyer_not_in_directory",
              "addressing_error",
              "other",
              "format_invalid",
              "semantic_error",
              "duplicate",
              "platform_auth",
              "platform_unavailable",
              "refused_by_buyer",
              "suspended",
              "unknown",
              null
            ],
            "description": "Lecture stable parmi onze catégories. Priorité : code local, code DGFiP, code HTTP ; seul le pré-contrôle annuaire Super PDP dispose d’un repli textuel. REJ_ADR = addressing_error, REJ_SEMAN = semantic_error, REJ_SYNT/REJ_XSD = format_invalid, REJ_DOUBLON = duplicate, AUTRE = other (explication dans rejectionNote). Le statut fr:213 seul ne classe pas en sémantique. Null au renvoi.",
            "readOnly": true
          },
          "rejectionCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du verdict enregistré ; null hors rejet, refus ou suspension, ou si aucun code n’a été enregistré.",
            "readOnly": true
          },
          "rejectionSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "buyer",
              "facturino",
              null
            ],
            "description": "Auteur du verdict enregistré ; null hors rejet, refus ou suspension, ou si la source manque sur un document ancien.",
            "readOnly": true
          },
          "relatedInvoiceId": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Identifiant de la facture liée, sur les événements d’avoir."
          },
          "relatedInvoiceNumber": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro de la facture liée, figé à la création de l’avoir ; null si absent sur un avoir ancien."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "readOnly": true
          },
          "amount": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : amount, en euros décimaux (chaîne)."
          },
          "total_paid": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : total_paid, en euros décimaux (chaîne)."
          },
          "total_due": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : total de la facture (nom historique), en euros décimaux."
          },
          "total": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : total, en euros décimaux (chaîne)."
          },
          "amountDue": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : amountDue, en euros décimaux (chaîne)."
          },
          "invoiceId": {
            "type": "string",
            "readOnly": true
          },
          "paymentId": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Identifiant du versement lié à cette révision du grand livre. Null si la variation ancienne ou agrégée ne permet pas une attribution certaine ; absent sur les anciens événements."
          },
          "method": {
            "type": "string",
            "readOnly": true
          },
          "pa_invoice_id": {
            "type": "string",
            "readOnly": true
          },
          "sender_siret": {
            "type": "string",
            "readOnly": true
          },
          "sender_name": {
            "type": "string",
            "readOnly": true
          },
          "total_ht": {
            "type": "string",
            "readOnly": true
          },
          "total_tva": {
            "type": "string",
            "readOnly": true
          },
          "total_ttc": {
            "type": "string",
            "readOnly": true
          },
          "recurringInvoiceId": {
            "type": "string",
            "readOnly": true
          },
          "error": {
            "type": "string",
            "readOnly": true
          },
          "count": {
            "type": "integer",
            "readOnly": true
          },
          "plan": {
            "type": "string",
            "readOnly": true
          },
          "stripeSubscriptionId": {
            "type": "string",
            "readOnly": true
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "pausedUntil": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "period": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "type": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "attempt": {
            "type": "integer",
            "readOnly": true
          },
          "paStatus": {
            "type": "string",
            "readOnly": true
          },
          "paInvoiceId": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "fr212": {
            "type": "object",
            "nullable": true,
            "description": "Où en est le statut « Encaissée » (fr:212) de cet encaissement auprès de la plateforme. Présent quand la décision fiscale du document impose ce statut ; absent sinon. `pending` : en file ; `awaiting_deposit` : attend un dépôt accepté de la facture ; `sent` : transmis (`sentAt`) ; `blocked` : ne peut pas être transmis (`lastErrorCode`) ; `reconciliation_required` / `failed` : à rapprocher.",
            "required": [
              "state",
              "sentAt",
              "lastErrorCode",
              "updatedAt"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "pending",
                  "awaiting_deposit",
                  "sent",
                  "blocked",
                  "reconciliation_required",
                  "failed"
                ],
                "readOnly": true
              },
              "sentAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time",
                "readOnly": true
              },
              "lastErrorCode": {
                "type": "string",
                "nullable": true,
                "readOnly": true
              },
              "lastErrorReason": {
                "type": "string",
                "description": "Motif brut de la plateforme lorsque le statut est refusé définitivement (pa_lifecycle_rejected) ou attend un état encaissable (pa_lifecycle_not_ready). Absent si aucun motif n’est fourni.",
                "readOnly": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "readOnly": true
              },
              "obligation": {
                "$ref": "#/components/schemas/ObligationFollowUp"
              }
            },
            "readOnly": true
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          },
          "paRejectionCode": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Structured rejection code returned by the platform, preserved without deriving it from text."
          },
          "paRejectionReason": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Original platform rejection reason, verbatim."
          }
        }
      },
      "WebhookAttempt": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "httpStatus": {
            "type": "integer",
            "nullable": true,
            "readOnly": true
          },
          "error": {
            "type": "string",
            "readOnly": true
          },
          "duration": {
            "type": "number",
            "readOnly": true
          },
          "endpointId": {
            "type": "string",
            "readOnly": true
          }
        },
        "readOnly": true
      },
      "EndpointDelivery": {
        "type": "object",
        "properties": {
          "delivered": {
            "type": "boolean",
            "readOnly": true
          },
          "attempts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookAttempt",
              "readOnly": true
            },
            "readOnly": true
          },
          "nextRetry": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "readOnly": true
          },
          "attemptCount": {
            "type": "integer",
            "minimum": 0,
            "readOnly": true,
            "description": "Compteur de la livraison courante, distinct de l’historique conservé lors d’un rejeu ciblé."
          },
          "generation": {
            "type": "integer",
            "minimum": 0,
            "readOnly": true,
            "description": "Compteur de la livraison courante, distinct de l’historique conservé lors d’un rejeu ciblé."
          }
        },
        "readOnly": true
      },
      "WebhookEvent": {
        "type": "object",
        "example": {
          "id": "evt_invoice_rejected",
          "object": "event",
          "type": "invoice.rejected",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "data": {
            "id": "inv_00090",
            "object": "invoice",
            "number": "FAC2026-00090",
            "status": "rejected",
            "previous_status": "deposited",
            "livemode": true,
            "documentStatus": "finalized",
            "transmissionStatus": "rejected",
            "transmissionDetail": null,
            "paymentStatus": "unpaid",
            "paErrorCode": null,
            "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
            "rejectionCategory": "addressing_error",
            "rejectionCode": "REJ_ADR",
            "rejectionSource": "platform",
            "metadata": {}
          },
          "request": null
        },
        "required": [
          "id",
          "object",
          "type",
          "apiVersion",
          "data",
          "livemode",
          "created"
        ],
        "description": "Enveloppe signée et ressource GET /v1/events. Les états de livraison (delivered, attempts, nextRetry, deliveries, updated) sont des champs REST absents du corps signé envoyé à votre endpoint.",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "event"
            ],
            "readOnly": true
          },
          "type": {
            "type": "string",
            "readOnly": true,
            "enum": [
              "invoice.created",
              "invoice.finalized",
              "invoice.sending",
              "invoice.sent",
              "invoice.deposited",
              "invoice.transmitted",
              "invoice.available",
              "invoice.received",
              "invoice.approved",
              "invoice.refused",
              "invoice.rejected",
              "invoice.suspended",
              "invoice.paid",
              "invoice.partially_paid",
              "invoice.overdue",
              "invoice.incoming.received",
              "quote.created",
              "quote.sent",
              "quote.viewed",
              "quote.accepted",
              "quote.refused",
              "quote.expired",
              "quote.converted",
              "credit_note.created",
              "credit_note.finalized",
              "credit_note.credit_deposited",
              "credit_note.credit_transmitted",
              "credit_note.credit_approved",
              "credit_note.credit_refused",
              "credit_note.sent",
              "customer.created",
              "customer.updated",
              "customer.deleted",
              "payment.created",
              "payment.received",
              "ereporting.submitted",
              "recurring_invoice.generated",
              "recurring_invoice.failed",
              "export.ready",
              "subscription.created",
              "subscription.cancelled",
              "subscription.renewed",
              "subscription.paused",
              "ereporting.accepted",
              "ereporting.rejected",
              "ereporting.obligation_updated",
              "invoice.obligation_updated",
              "credit_note.obligation_updated",
              "payment.obligation_updated"
            ]
          },
          "apiVersion": {
            "type": "string",
            "example": "2026-09-01",
            "readOnly": true
          },
          "data": {
            "$ref": "#/components/schemas/WebhookEventData",
            "readOnly": true
          },
          "request": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "readOnly": true
              },
              "idempotencyKey": {
                "type": "string",
                "readOnly": true
              }
            },
            "readOnly": true
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "created": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "companyId": {
            "type": "string",
            "readOnly": true
          },
          "endpointId": {
            "type": "string",
            "readOnly": true
          },
          "delivered": {
            "type": "boolean",
            "readOnly": true
          },
          "attempts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookAttempt",
              "readOnly": true
            },
            "readOnly": true
          },
          "nextRetry": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "readOnly": true
          },
          "deliveries": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/EndpointDelivery"
            },
            "readOnly": true
          },
          "expireAt": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "readOnly": true,
        "oneOf": [
          {
            "$ref": "#/components/schemas/InvoiceWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/IncomingInvoiceWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/QuoteWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/CreditNoteWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/CustomerWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/PaymentCreatedWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/PaymentReceivedWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/EreportingWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/RecurringGeneratedWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/RecurringFailedWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/ExportWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/SubscriptionWebhookEvent"
          },
          {
            "$ref": "#/components/schemas/ObligationWebhookEvent"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "invoice.created": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.finalized": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.sending": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.sent": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.deposited": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.transmitted": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.available": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.received": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.approved": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.refused": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.rejected": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.suspended": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.paid": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.partially_paid": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.overdue": "#/components/schemas/InvoiceWebhookEvent",
            "invoice.incoming.received": "#/components/schemas/IncomingInvoiceWebhookEvent",
            "quote.created": "#/components/schemas/QuoteWebhookEvent",
            "quote.sent": "#/components/schemas/QuoteWebhookEvent",
            "quote.viewed": "#/components/schemas/QuoteWebhookEvent",
            "quote.accepted": "#/components/schemas/QuoteWebhookEvent",
            "quote.refused": "#/components/schemas/QuoteWebhookEvent",
            "quote.expired": "#/components/schemas/QuoteWebhookEvent",
            "quote.converted": "#/components/schemas/QuoteWebhookEvent",
            "credit_note.created": "#/components/schemas/CreditNoteWebhookEvent",
            "credit_note.finalized": "#/components/schemas/CreditNoteWebhookEvent",
            "credit_note.credit_deposited": "#/components/schemas/CreditNoteWebhookEvent",
            "credit_note.credit_transmitted": "#/components/schemas/CreditNoteWebhookEvent",
            "credit_note.credit_approved": "#/components/schemas/CreditNoteWebhookEvent",
            "credit_note.credit_refused": "#/components/schemas/CreditNoteWebhookEvent",
            "credit_note.sent": "#/components/schemas/CreditNoteWebhookEvent",
            "customer.created": "#/components/schemas/CustomerWebhookEvent",
            "customer.updated": "#/components/schemas/CustomerWebhookEvent",
            "customer.deleted": "#/components/schemas/CustomerWebhookEvent",
            "payment.created": "#/components/schemas/PaymentCreatedWebhookEvent",
            "payment.received": "#/components/schemas/PaymentReceivedWebhookEvent",
            "ereporting.submitted": "#/components/schemas/EreportingWebhookEvent",
            "recurring_invoice.generated": "#/components/schemas/RecurringGeneratedWebhookEvent",
            "recurring_invoice.failed": "#/components/schemas/RecurringFailedWebhookEvent",
            "export.ready": "#/components/schemas/ExportWebhookEvent",
            "subscription.created": "#/components/schemas/SubscriptionWebhookEvent",
            "subscription.cancelled": "#/components/schemas/SubscriptionWebhookEvent",
            "subscription.renewed": "#/components/schemas/SubscriptionWebhookEvent",
            "subscription.paused": "#/components/schemas/SubscriptionWebhookEvent",
            "ereporting.accepted": "#/components/schemas/EreportingWebhookEvent",
            "ereporting.rejected": "#/components/schemas/EreportingWebhookEvent",
            "ereporting.obligation_updated": "#/components/schemas/ObligationWebhookEvent",
            "invoice.obligation_updated": "#/components/schemas/ObligationWebhookEvent",
            "credit_note.obligation_updated": "#/components/schemas/ObligationWebhookEvent",
            "payment.obligation_updated": "#/components/schemas/ObligationWebhookEvent"
          }
        }
      },
      "Invoice": {
        "type": "object",
        "example": {
          "id": "inv_00090",
          "object": "invoice",
          "number": "FAC2026-00090",
          "status": "rejected",
          "documentStatus": "finalized",
          "transmissionStatus": "rejected",
          "transmissionDetail": null,
          "paymentStatus": "unpaid",
          "type": "standard",
          "currency": "eur",
          "customer": {
            "ref": "cus_example",
            "snapshot": {
              "name": "INTEK CENTER",
              "address": {
                "line1": "1 rue Exemple",
                "postalCode": "75001",
                "city": "Paris",
                "country": "FR"
              }
            }
          },
          "items": [],
          "totals": {
            "totalHT": 990,
            "totalTVA": 198,
            "totalTTC": 1188,
            "amountPaid": 0,
            "amountDue": 1188
          },
          "einvoicing": {
            "paId": "dep_current",
            "paStatus": "rejected",
            "paStatusCode": "fr:213",
            "paTransactionId": null,
            "paErrorCode": null,
            "rejectionCode": "REJ_ADR",
            "rejectionSource": "platform",
            "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
            "rejectionNote": null,
            "rejectionCategory": "addressing_error",
            "refusalReason": null,
            "paIdempotencyKey": "submission_same_attempt",
            "peppolDeliveryId": null,
            "ereportingId": null,
            "ereportingPaymentId": null,
            "sentAt": "2026-09-07T12:36:00.000Z",
            "trackingId": null,
            "routingIdentifier": "0225:73282932000074",
            "buyerReachableAt": null,
            "directoryCheckedAt": null,
            "submissionArtefact": {
              "kind": "cii",
              "path": "invoices/inv_00090/submission.xml",
              "generatedAt": "2026-09-07T12:35:00.000Z",
              "correctedRules": [],
              "routingIdentifier": "0225:73282932000074"
            },
            "previousSubmissions": [
              {
                "paId": "dep_previous",
                "paTransactionId": null,
                "paIdempotencyKey": "submission_previous",
                "paStatus": "rejected",
                "paStatusCode": "fr:213",
                "paErrorCode": null,
                "rejectionReason": "AUTRE — Ce motif nécessite une explication en note de CDV.",
                "rejectionCode": "AUTRE",
                "rejectionSource": "platform",
                "rejectionNote": "L’adresse de réception doit être confirmée.",
                "rejectionCategory": "other",
                "sentAt": "2026-09-07T08:00:00.000Z",
                "closedAt": "2026-09-07T12:35:00.000Z"
              }
            ]
          },
          "livemode": true,
          "created": "2026-09-07T12:00:00.000Z",
          "updated": "2026-09-07T12:36:00.000Z"
        },
        "properties": {
          "id": {
            "type": "string",
            "example": "inv_abc123"
          },
          "object": {
            "type": "string",
            "const": "invoice"
          },
          "type": {
            "type": "string",
            "enum": [
              "standard",
              "deposit",
              "corrective",
              "self_billing"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/InvoiceStatus",
            "description": "Statut RÉSUMÉ : une projection dérivée des trois axes ci-dessous — un seul mot cohérent pour les consommateurs qui n'en veulent qu'un. Les trois axes font foi ; le résumé n'est jamais écrit seul."
          },
          "number": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "const": "eur"
          },
          "customer": {
            "type": "object",
            "properties": {
              "ref": {
                "type": "string"
              },
              "snapshot": {
                "type": "object"
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/Totals"
          },
          "dates": {
            "type": "object",
            "description": "Dates du document. `paidAt` porte la date RÉELLE du règlement — celle de l'encaissement qui a soldé la facture, jamais l'échéance ni l'heure du serveur. Absente tant qu'un solde reste dû, et effacée si une annulation en rouvre un.",
            "properties": {
              "paidAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "issued": {
                "type": "string",
                "format": "date"
              },
              "due": {
                "type": "string",
                "format": "date"
              },
              "serviceStart": {
                "type": "string",
                "nullable": true,
                "format": "date"
              },
              "serviceEnd": {
                "type": "string",
                "nullable": true,
                "format": "date"
              },
              "finalizedAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time"
              },
              "sentAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time"
              }
            }
          },
          "deposits": {
            "type": "array",
            "description": "Snapshots des factures d'acompte intégralement réglées et liées (386, BT-113). Présent uniquement sur une facture de solde.",
            "items": {
              "type": "object",
              "properties": {
                "invoiceId": {
                  "type": "string"
                },
                "number": {
                  "type": "string"
                },
                "issued": {
                  "type": "string",
                  "format": "date"
                },
                "totalTTC": {
                  "type": "integer",
                  "description": "TTC de l'acompte, en centimes"
                }
              }
            }
          },
          "paymentSchedule": {
            "type": "array",
            "description": "Échéancier de règlement calculé (présent si un `schedule` a été fourni).",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "amount": {
                  "type": "integer",
                  "description": "Montant de l'échéance, en centimes"
                },
                "dueDate": {
                  "type": "string",
                  "format": "date"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "taxDecisionId": {
            "type": "string",
            "description": "Lien IMMUABLE vers la décision fiscale qui détermine la TVA de cette facture. Une décision finale n'adosse qu'une seule facture."
          },
          "taxSource": {
            "$ref": "#/components/schemas/TaxSource"
          },
          "commercialDraft": {
            "type": "object",
            "nullable": true,
            "description": "Opération décrite par un brouillon commercial non décidé (`taxSource: null`), avant toute décision. Absent dès que la facture est adossée à une décision.",
            "properties": {
              "priceMode": {
                "type": "string",
                "enum": [
                  "tax_inclusive",
                  "tax_exclusive"
                ]
              },
              "totalCents": {
                "type": "integer",
                "description": "Somme des lignes dans la base de prix du brouillon, en centimes entiers. Montant COMMERCIAL : ni HT ni TTC décidé."
              },
              "lines": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "deposits": {
                "type": "array",
                "description": "Factures d'acompte (386) intégralement réglées que l'utilisateur demande à déduire. Références seules : les montants sont lus sur les acomptes eux-mêmes, et la déduction est réglée contre le montant DÉCIDÉ à la finalisation.",
                "items": {
                  "type": "object",
                  "properties": {
                    "invoiceId": {
                      "type": "string"
                    }
                  }
                }
              },
              "schedule": {
                "type": "array",
                "description": "Échéances proposées, en centimes entiers. Vérifiées côté serveur APRÈS la décision, contre le restant dû (montant décidé moins acomptes) : somme exacte, dates strictement croissantes, dernière échéance à la date d'échéance.",
                "items": {
                  "type": "object",
                  "properties": {
                    "amount": {
                      "type": "integer"
                    },
                    "dueDate": {
                      "type": "string",
                      "format": "date"
                    },
                    "label": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "taxSnapshot": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxSnapshot"
              }
            ],
            "description": "Position fiscale figée, écrite depuis la décision. Présente sur tout document décidé."
          },
          "documentStatus": {
            "$ref": "#/components/schemas/DocumentStatus"
          },
          "transmissionStatus": {
            "$ref": "#/components/schemas/TransmissionStatus"
          },
          "transmissionDetail": {
            "$ref": "#/components/schemas/TransmissionDetail"
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus"
          },
          "einvoicing": {
            "type": "object",
            "readOnly": true,
            "description": "Suivi de transmission PA. Les trois axes de la facture font autorité ; ces champs conservent les identifiants et les derniers retours reçus. Les motifs et l’artefact peuvent être absents sur les documents antérieurs.",
            "properties": {
              "paId": {
                "type": "string",
                "nullable": true,
                "description": "Identifiant de la facture sur la plateforme.",
                "readOnly": true
              },
              "paStatus": {
                "type": "string",
                "nullable": true,
                "description": "Dernier état communiqué par la plateforme ; accepted à la soumission signifie fichier accepté, dépôt encore à confirmer.",
                "readOnly": true
              },
              "paStatusCode": {
                "type": "string",
                "nullable": true,
                "description": "Code brut du statut plateforme, par exemple fr:200.",
                "readOnly": true
              },
              "paTransactionId": {
                "type": "string",
                "nullable": true,
                "description": "Identifiant de la transaction de soumission.",
                "readOnly": true
              },
              "paErrorCode": {
                "type": "string",
                "nullable": true,
                "description": "Code du dernier échec de dépôt : contrôle local ou réponse de la plateforme.",
                "readOnly": true
              },
              "rejectionReason": {
                "type": "string",
                "nullable": true,
                "description": "Motif brut de la plateforme ou de l’acheteur, verbatim ; constat local pour la source facturino. Ne contient pas de code ou de note reconstruits. Null si absent.",
                "readOnly": true
              },
              "rejectionCategory": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "buyer_not_in_directory",
                  "addressing_error",
                  "other",
                  "format_invalid",
                  "semantic_error",
                  "duplicate",
                  "platform_auth",
                  "platform_unavailable",
                  "refused_by_buyer",
                  "suspended",
                  "unknown",
                  null
                ],
                "description": "Lecture stable parmi onze catégories. Priorité : code local, code DGFiP, code HTTP ; seul le pré-contrôle annuaire Super PDP dispose d’un repli textuel. REJ_ADR = addressing_error, REJ_SEMAN = semantic_error, REJ_SYNT/REJ_XSD = format_invalid, REJ_DOUBLON = duplicate, AUTRE = other (explication dans rejectionNote). Le statut fr:213 seul ne classe pas en sémantique. Null au renvoi.",
                "readOnly": true
              },
              "refusalReason": {
                "type": "string",
                "nullable": true,
                "description": "Motif de refus de l’acheteur, lorsqu’il est fourni dans ce champ.",
                "readOnly": true
              },
              "paIdempotencyKey": {
                "type": "string",
                "nullable": true,
                "description": "Clé de dédoublonnage de la soumission.",
                "readOnly": true
              },
              "peppolDeliveryId": {
                "type": "string",
                "nullable": true,
                "description": "Identifiant de livraison Peppol.",
                "readOnly": true
              },
              "ereportingId": {
                "type": "string",
                "nullable": true,
                "description": "Déclaration e-reporting de transactions associée.",
                "readOnly": true
              },
              "sentAt": {
                "type": "string",
                "nullable": true,
                "description": "Horodatage de soumission à la plateforme.",
                "format": "date-time",
                "readOnly": true
              },
              "trackingId": {
                "type": "string",
                "nullable": true,
                "description": "Identifiant de suivi du flux plateforme.",
                "readOnly": true
              },
              "submissionArtefact": {
                "type": "object",
                "nullable": true,
                "readOnly": true,
                "description": "CII préparé pour la soumission après correction des règles CIUS-FR connues ; l’original archivé reste intact. Sa présence ne confirme pas l’acceptation par la plateforme.",
                "required": [
                  "kind",
                  "path",
                  "generatedAt",
                  "correctedRules"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "cii"
                    ],
                    "readOnly": true
                  },
                  "path": {
                    "type": "string",
                    "description": "Chemin de stockage du CII préparé.",
                    "readOnly": true
                  },
                  "generatedAt": {
                    "type": "string",
                    "format": "date-time",
                    "readOnly": true
                  },
                  "correctedRules": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "readOnly": true
                    },
                    "example": [
                      "BR-FR-08"
                    ],
                    "readOnly": true
                  },
                  "routingIdentifier": {
                    "type": "string",
                    "description": "Active directory address carried by this regenerated CII (BT-49), including its scheme.",
                    "example": "0225:12345678200002",
                    "readOnly": true
                  },
                  "sellerRoutingIdentifier": {
                    "type": "string",
                    "readOnly": true,
                    "description": "Adresse vendeur employée pour régénérer BT-34 dans cet artefact ; absente sans correction de cette adresse.",
                    "example": "0225:908456726_90845672600029"
                  }
                }
              },
              "previousSubmissions": {
                "type": "array",
                "readOnly": true,
                "description": "Tentatives closes de ce document sur la plateforme, de la plus ancienne à la plus récente : un dépôt rejeté renvoyé sous le même numéro ouvre une nouvelle tentative, dont les identifiants courants figurent ci-dessus. Absent tant qu’aucun renvoi n’a eu lieu.",
                "items": {
                  "type": "object",
                  "required": [
                    "paId",
                    "paTransactionId",
                    "paIdempotencyKey",
                    "paStatus",
                    "paStatusCode",
                    "paErrorCode",
                    "rejectionReason",
                    "sentAt",
                    "closedAt"
                  ],
                  "properties": {
                    "paId": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paTransactionId": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paIdempotencyKey": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paStatus": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paStatusCode": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paErrorCode": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "rejectionReason": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "rejectionCategory": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "buyer_not_in_directory",
                        "addressing_error",
                        "other",
                        "format_invalid",
                        "semantic_error",
                        "duplicate",
                        "platform_auth",
                        "platform_unavailable",
                        "refused_by_buyer",
                        "suspended",
                        "unknown",
                        null
                      ],
                      "description": "Lecture du rejet de cette tentative close.",
                      "readOnly": true
                    },
                    "sentAt": {
                      "type": "string",
                      "nullable": true,
                      "format": "date-time",
                      "readOnly": true
                    },
                    "closedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Ouverture de la tentative suivante.",
                      "readOnly": true
                    },
                    "rejectionCode": {
                      "type": "string",
                      "nullable": true,
                      "description": "Valeur archivée de einvoicing.rejectionCode pour cette tentative close.",
                      "readOnly": true
                    },
                    "rejectionSource": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "platform",
                        "buyer",
                        "facturino",
                        null
                      ],
                      "description": "Valeur archivée de einvoicing.rejectionSource pour cette tentative close.",
                      "readOnly": true
                    },
                    "rejectionNote": {
                      "type": "string",
                      "nullable": true,
                      "description": "Valeur archivée de einvoicing.rejectionNote pour cette tentative close.",
                      "readOnly": true
                    }
                  },
                  "readOnly": true
                }
              },
              "rejectionCode": {
                "type": "string",
                "nullable": true,
                "description": "Code normalisé du motif DGFiP (REJ_ADR, REJ_SEMAN, REJ_SYNT, REJ_XSD, REJ_DOUBLON, AUTRE), HTTP plateforme (SUPER_PDP_400…) ou local (buyer_not_in_directory). Null si aucun code de motif n’est fourni ; les codes de statut fr:* restent dans paStatusCode. Remis à null au renvoi.",
                "readOnly": true
              },
              "rejectionSource": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "platform",
                  "buyer",
                  "facturino",
                  null
                ],
                "description": "Auteur du verdict enregistré par le producteur : platform (fr:213, réponse HTTP), buyer (fr:210, fr:207, fr:208), facturino (contrôle avant dépôt). Null après un renvoi ou pour un document ancien sans source enregistrée. Jamais déduit du motif.",
                "readOnly": true
              },
              "rejectionNote": {
                "type": "string",
                "nullable": true,
                "description": "Notes jointes par la plateforme ou l’acheteur, verbatim, concaténées par un retour à la ligne. Remises à null au renvoi.",
                "readOnly": true
              },
              "routingIdentifier": {
                "type": "string",
                "nullable": true,
                "description": "Electronic reception address carried in the submitted CII (BT-49), including its scheme. When the directory returns a different active address, the CII is regenerated for the same submission and idempotency key; legal identifiers and the archived original remain unchanged.",
                "example": "0225:12345678200002",
                "readOnly": true
              },
              "buyerReachableAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time",
                "readOnly": true,
                "description": "Last daily check that found an active buyer reception address after a buyer_not_in_directory rejection. Cleared on resend; never triggers an automatic deposit."
              },
              "directoryCheckedAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time",
                "readOnly": true,
                "description": "Last daily directory consultation, including absent or indeterminate answers. Cleared on resend."
              },
              "ereportingPaymentId": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Identifiant de la déclaration d’encaissement e-reporting liée à cette facture."
              },
              "senderRoutingIdentifier": {
                "type": "string",
                "readOnly": true,
                "description": "Adresse électronique du vendeur résolue pour BT-34 dans le CII soumis (schéma:valeur). Absente si aucune adresse vendeur spécifique n’a été résolue. L’original archivé est conservé.",
                "example": "0225:908456726_90845672600029"
              },
              "obligation": {
                "$ref": "#/components/schemas/ObligationFollowUp"
              }
            }
          },
          "files": {
            "type": "object",
            "readOnly": true,
            "description": "Chemins de stockage, pas des URL de téléchargement ; un champ absent désigne un fichier non disponible. Les endpoints de téléchargement rendent des URL signées ou un job.",
            "properties": {
              "pdfPath": {
                "type": "string",
                "description": "Copie PDF de présentation ; elle peut suivre les encaissements."
              },
              "facturxPath": {
                "type": "string",
                "description": "Original Factur-X archivé."
              },
              "xmlPath": {
                "type": "string",
                "description": "XML de l’original."
              },
              "correctedXmlPath": {
                "type": "string",
                "description": "CII préparé pour le dépôt, distinct du XML original."
              }
            }
          },
          "lifecycle": {
            "type": "array",
            "readOnly": true,
            "items": {
              "$ref": "#/components/schemas/LifecycleEntry"
            },
            "description": "Historique append-only. Les nouvelles lignes portent code et params ; details reste rendu en français pour les lecteurs historiques."
          },
          "paymentInfo": {
            "$ref": "#/components/schemas/InvoicePaymentTerms"
          },
          "archive": {
            "$ref": "#/components/schemas/InvoiceArchive",
            "nullable": true,
            "readOnly": true
          },
          "portal": {
            "$ref": "#/components/schemas/InvoicePortal",
            "nullable": true,
            "readOnly": true
          },
          "legalMentions": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "purchaseOrderNumber": {
            "type": "string",
            "nullable": true
          },
          "reminderTaskIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "readOnly": true,
            "description": "Identifiants techniques des rappels planifiés ; ne pas utiliser comme clés métier."
          },
          "processed": {
            "type": "boolean",
            "readOnly": true
          },
          "anonymized": {
            "type": "boolean",
            "readOnly": true
          },
          "anonymizedAt": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "ereportingBlock": {
            "$ref": "#/components/schemas/EReportingBlock"
          }
        }
      },
      "CreateInvoiceInput": {
        "type": "object",
        "required": [
          "customerId",
          "buyer",
          "taxDecisionId",
          "decisionLines",
          "dates",
          "payment"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "standard",
              "deposit",
              "corrective",
              "self_billing"
            ],
            "default": "standard"
          },
          "customerId": {
            "type": "string",
            "description": "ID du client (`cus_...`)"
          },
          "buyer": {
            "$ref": "#/components/schemas/Buyer"
          },
          "dates": {
            "type": "object",
            "required": [
              "issued",
              "due"
            ],
            "properties": {
              "issued": {
                "type": "string",
                "format": "date"
              },
              "due": {
                "type": "string",
                "format": "date"
              },
              "serviceStart": {
                "type": "string",
                "format": "date"
              },
              "serviceEnd": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentTerms"
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "purchaseOrderNumber": {
            "type": "string",
            "maxLength": 100,
            "description": "Numéro de bon de commande (BT-13)"
          },
          "deposits": {
            "type": "array",
            "maxItems": 20,
            "description": "Factures d'acompte (386) intégralement réglées, déduites de cette facture de solde. Le total décidé (`amountToCharge`) est intact : les acomptes alimentent `amountPaid` (BT-113) et abaissent `amountDue` (BT-115), validés côté serveur dans la transaction de création.",
            "items": {
              "type": "object",
              "required": [
                "invoiceId"
              ],
              "properties": {
                "invoiceId": {
                  "type": "string",
                  "description": "ID de la facture d'acompte (`inv_...`)"
                }
              }
            }
          },
          "schedule": {
            "type": "array",
            "minItems": 2,
            "maxItems": 12,
            "description": "Échéancier. Validé côté serveur : il répartit EXACTEMENT le montant décidé restant dû — il ne modifie jamais le total.",
            "items": {
              "type": "object",
              "required": [
                "amount",
                "dueDate"
              ],
              "properties": {
                "amount": {
                  "type": "integer",
                  "description": "Montant de l'échéance, en centimes",
                  "minimum": 1
                },
                "dueDate": {
                  "type": "string",
                  "format": "date"
                },
                "label": {
                  "type": "string",
                  "maxLength": 80
                }
              }
            }
          },
          "autoFinalize": {
            "type": "boolean",
            "description": "One-shot : finalise la facture (attribution du numéro) dans le même appel."
          },
          "autoSend": {
            "type": "object",
            "description": "One-shot : finalise puis émet la facture dans le même appel (implique `autoFinalize`). Le flux granulaire (POST puis `/finalize` puis `/send`) reste disponible.",
            "properties": {
              "email": {
                "type": "boolean",
                "description": "Envoie la facture au client par e-mail."
              },
              "pa": {
                "type": "boolean",
                "description": "Dépose la facture sur la PA connectée (asynchrone, statut `sending`)."
              }
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "taxDecisionId": {
            "type": "string",
            "description": "Lien immuable vers la décision fiscale FINALE qui détermine la TVA de cette facture, quelle que soit sa source (`facturino` ou `integration`). Requis : une facture fiscalisée n'existe pas sans décision, et une décision finale adosse exactement UNE facture."
          },
          "decisionLines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/DecisionBackedLineInput"
            },
            "description": "Présentation de chaque ligne décidée (unité, produit catalogue), une par ligne de la décision, appariée par `taxLineRef`. Le contenu fiscal d'une ligne (taux, catégorie, VATEX, montants) vient de la décision seule."
          }
        },
        "description": "Toute facture naît d'une décision fiscale FINALE : `taxDecisionId` + `decisionLines` sont requis, quelle que soit la source (`facturino` ou `integration`). `deposits` et `schedule` se règlent contre le montant décidé, dans la transaction de création : le total décidé n'est jamais modifié."
      },
      "UpdateInvoiceInput": {
        "type": "object",
        "description": "PATCH d'un brouillon. Tout brouillon est adossé à une décision : ses lignes, son acheteur et ses champs fiscaux sont figés — seuls les champs non fiscaux restent modifiables (et `dates` est restreint à la date d'échéance). Pour changer l'opération, créez une nouvelle décision et un nouveau brouillon.",
        "properties": {
          "dates": {
            "type": "object",
            "properties": {
              "issued": {
                "type": "string",
                "format": "date"
              },
              "due": {
                "type": "string",
                "format": "date"
              },
              "serviceStart": {
                "type": "string",
                "format": "date"
              },
              "serviceEnd": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentTerms"
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "purchaseOrderNumber": {
            "type": "string",
            "maxLength": 100,
            "description": "Numéro de bon de commande (BT-13)"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "InvoiceList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedList"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          }
        ]
      },
      "BuyerNatureWarning": {
        "type": "object",
        "required": [
          "code",
          "message",
          "param"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "buyer_nature_suspect"
            ],
            "readOnly": true
          },
          "message": {
            "type": "string",
            "description": "Non-blocking explanation in English. Customer creation and the tax decision proceed unchanged.",
            "readOnly": true
          },
          "param": {
            "type": "string",
            "description": "Input field to review: name on customers, customerId on tax decisions.",
            "readOnly": true
          }
        },
        "readOnly": true
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "cus_abc123"
          },
          "object": {
            "type": "string",
            "const": "customer"
          },
          "warnings": {
            "type": "array",
            "readOnly": true,
            "items": {
              "$ref": "#/components/schemas/BuyerNatureWarning"
            },
            "description": "Non-blocking identity hints, persisted and returned on creation/update. Empty when no hint applies; optional on historical customers."
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "company",
              "individual"
            ]
          },
          "siret": {
            "type": "string",
            "nullable": true
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "legalForm": {
            "$ref": "#/components/schemas/LegalForm"
          },
          "naf": {
            "$ref": "#/components/schemas/NafCode"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/Address"
          },
          "contacts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "firstName": {
                  "type": "string"
                },
                "lastName": {
                  "type": "string"
                },
                "email": {
                  "type": "string",
                  "format": "email"
                },
                "phone": {
                  "type": "string"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "billing",
                    "technical",
                    "main"
                  ]
                }
              }
            }
          },
          "active": {
            "type": "boolean"
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "companyId": {
            "type": "string",
            "readOnly": true
          },
          "balance": {
            "type": "integer",
            "readOnly": true,
            "description": "Solde en centimes."
          },
          "currency": {
            "type": "string",
            "enum": [
              "eur"
            ],
            "readOnly": true
          },
          "siretVerified": {
            "type": "boolean",
            "readOnly": true
          },
          "vatVerified": {
            "type": "boolean",
            "readOnly": true
          }
        }
      },
      "CreateCustomerInput": {
        "type": "object",
        "required": [
          "name",
          "type",
          "address"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "company",
              "individual"
            ]
          },
          "siret": {
            "type": "string"
          },
          "siren": {
            "type": "string"
          },
          "vatNumber": {
            "type": "string"
          },
          "legalForm": {
            "$ref": "#/components/schemas/LegalFormInput"
          },
          "naf": {
            "$ref": "#/components/schemas/NafInput"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/Address"
          },
          "contacts": {
            "type": "array",
            "description": "Contacts additionnels. Le contact de rôle `billing` reçoit les factures et relances par défaut (sinon l'email principal du client).",
            "items": {
              "type": "object",
              "properties": {
                "firstName": {
                  "type": "string"
                },
                "lastName": {
                  "type": "string"
                },
                "email": {
                  "type": "string",
                  "format": "email"
                },
                "phone": {
                  "type": "string"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "billing",
                    "technical",
                    "main"
                  ]
                }
              }
            }
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "E-mail principal du client (destinataire des factures si aucun contact `billing`)."
          },
          "phone": {
            "type": "string",
            "maxLength": 30
          },
          "website": {
            "type": "string",
            "format": "uri",
            "maxLength": 200
          },
          "paymentTerms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "description": "Délai de paiement par défaut, en jours"
          },
          "defaultPaymentMethod": {
            "type": "string",
            "description": "Moyen de paiement. De nouvelles valeurs peuvent être ajoutées ; les clients doivent tolérer les valeurs inconnues.",
            "enum": [
              "transfer",
              "card",
              "check",
              "cash",
              "direct_debit",
              "sepa",
              "paypal"
            ]
          },
          "internalReference": {
            "type": "string",
            "maxLength": 100,
            "description": "Référence interne libre (non affichée sur la facture)"
          },
          "tags": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 50
            }
          },
          "notes": {
            "type": "string",
            "maxLength": 1000
          },
          "paIdentifier": {
            "type": "string",
            "maxLength": 100,
            "description": "Identifiant de routage explicite du client sur la PA (prioritaire sur le SIRET pour l'adresse BT-49)."
          },
          "preferredFormat": {
            "type": "string",
            "enum": [
              "facturx",
              "ubl",
              "cii"
            ],
            "description": "Format e-invoicing préféré du client (le cas échéant)."
          },
          "receivingPaId": {
            "type": "string",
            "maxLength": 100,
            "description": "Identifiant de la PA de réception du client (annuaire), si connu."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "UpdateCustomerInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "siret": {
            "type": "string"
          },
          "vatNumber": {
            "type": "string"
          },
          "siren": {
            "type": "string"
          },
          "legalForm": {
            "$ref": "#/components/schemas/LegalFormInput"
          },
          "naf": {
            "$ref": "#/components/schemas/NafInput"
          },
          "type": {
            "type": "string",
            "enum": [
              "company",
              "individual"
            ]
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "deliveryAddress": {
            "$ref": "#/components/schemas/Address"
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "phone": {
            "type": "string",
            "maxLength": 30
          },
          "website": {
            "type": "string",
            "format": "uri",
            "maxLength": 200
          },
          "contacts": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "properties": {
                "firstName": {
                  "type": "string"
                },
                "lastName": {
                  "type": "string"
                },
                "email": {
                  "type": "string",
                  "format": "email"
                },
                "phone": {
                  "type": "string"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "billing",
                    "technical",
                    "main"
                  ]
                }
              }
            }
          },
          "paymentTerms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365
          },
          "defaultPaymentMethod": {
            "type": "string",
            "description": "Moyen de paiement. De nouvelles valeurs peuvent être ajoutées ; les clients doivent tolérer les valeurs inconnues.",
            "enum": [
              "transfer",
              "card",
              "check",
              "cash",
              "direct_debit",
              "sepa",
              "paypal"
            ]
          },
          "internalReference": {
            "type": "string",
            "maxLength": 100
          },
          "tags": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 50
            }
          },
          "notes": {
            "type": "string",
            "maxLength": 1000
          },
          "paIdentifier": {
            "type": "string",
            "maxLength": 100
          },
          "preferredFormat": {
            "type": "string",
            "enum": [
              "facturx",
              "ubl",
              "cii"
            ]
          },
          "receivingPaId": {
            "type": "string",
            "maxLength": 100
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "CustomerList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedList"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          }
        ]
      },
      "Address": {
        "type": "object",
        "properties": {
          "line1": {
            "type": "string"
          },
          "line2": {
            "type": "string"
          },
          "postalCode": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "default": "FR"
          }
        }
      },
      "LegalForm": {
        "type": "object",
        "nullable": true,
        "description": "Forme juridique INSEE niveau III. Renvoyée comme sous-objet par le serveur (résolution canonique). En entrée, accepte `{ code }` (4 chiffres INSEE) ou `{ sigle }` (ex: `SAS`). Liste publique : `GET /v1/reference/legal-forms`.",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^\\d{4}$",
            "example": "5710"
          },
          "sigle": {
            "type": "string",
            "example": "SAS"
          },
          "label": {
            "type": "string",
            "example": "Société par actions simplifiée"
          }
        }
      },
      "LegalFormInput": {
        "type": "object",
        "nullable": true,
        "description": "Hybrid : envoyer `code` OU `sigle` — le serveur résout la forme canonique. `null` efface le champ.",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^\\d{4}$"
          },
          "sigle": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20
          }
        }
      },
      "NafCode": {
        "type": "object",
        "nullable": true,
        "description": "Code NAF Rev. 2 INSEE. Renvoyé comme sous-objet par le serveur. En entrée, accepte `{ code }` au format `XX.XXY` ou `XXXXY` (le point est facultatif). Liste publique : `GET /v1/reference/naf-codes`.",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^\\d{2}\\.\\d{2}[A-Z]$",
            "example": "62.01Z"
          },
          "label": {
            "type": "string",
            "example": "Programmation informatique"
          }
        }
      },
      "NafInput": {
        "type": "object",
        "nullable": true,
        "description": "`null` efface le champ.",
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^\\d{2}\\.?\\d{2}[A-Z]$"
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "prod_abc123"
          },
          "object": {
            "type": "string",
            "const": "product"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "unitPrice": {
            "type": "integer",
            "description": "Prix en euros (Decimal string)"
          },
          "vatRate": {
            "type": "integer"
          },
          "vatCode": {
            "type": "string",
            "enum": [
              "S",
              "Z",
              "E",
              "AE",
              "G",
              "IC",
              "K",
              "O",
              "VATEX-FR-FRANCHISE"
            ]
          },
          "vatexCode": {
            "type": "string",
            "description": "Optionnel. Code VATEX (BT-121) précisant le fondement réel de l'exonération quand il diffère du défaut de la catégorie (ex. formation Qualiopi art. 261-4-4°a, régime de marge, DOM, sous-traitance BTP).",
            "enum": [
              "VATEX-EU-AE",
              "VATEX-EU-D",
              "VATEX-EU-F",
              "VATEX-EU-G",
              "VATEX-EU-IC",
              "VATEX-EU-O",
              "VATEX-FR-FRANCHISE",
              "VATEX-FR-CNWVAT",
              "VATEX-FR-AUTOLIQ",
              "VATEX-FR-261"
            ]
          },
          "unit": {
            "type": "string",
            "enum": [
              "unit",
              "hour",
              "day",
              "month",
              "flat_rate",
              "kg",
              "m",
              "m2",
              "m3",
              "l"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateProductInput": {
        "type": "object",
        "required": [
          "name",
          "unitPrice",
          "vatRate",
          "vatCode",
          "unit"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "unitPrice": {
            "type": "integer"
          },
          "vatRate": {
            "type": "integer"
          },
          "vatCode": {
            "type": "string",
            "enum": [
              "S",
              "Z",
              "E",
              "AE",
              "G",
              "IC",
              "K",
              "O",
              "VATEX-FR-FRANCHISE"
            ]
          },
          "vatexCode": {
            "type": "string",
            "description": "Optionnel. Code VATEX (BT-121) précisant le fondement réel de l'exonération quand il diffère du défaut de la catégorie (ex. formation Qualiopi art. 261-4-4°a, régime de marge, DOM, sous-traitance BTP).",
            "enum": [
              "VATEX-EU-AE",
              "VATEX-EU-D",
              "VATEX-EU-F",
              "VATEX-EU-G",
              "VATEX-EU-IC",
              "VATEX-EU-O",
              "VATEX-FR-FRANCHISE",
              "VATEX-FR-CNWVAT",
              "VATEX-FR-AUTOLIQ",
              "VATEX-FR-261"
            ]
          },
          "unit": {
            "type": "string",
            "enum": [
              "unit",
              "hour",
              "day",
              "month",
              "flat_rate",
              "kg",
              "m",
              "m2",
              "m3",
              "l"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "UpdateProductInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "unitPrice": {
            "type": "integer"
          },
          "vatRate": {
            "type": "integer"
          },
          "vatCode": {
            "type": "string",
            "enum": [
              "S",
              "Z",
              "E",
              "AE",
              "G",
              "IC",
              "K",
              "O",
              "VATEX-FR-FRANCHISE"
            ]
          },
          "vatexCode": {
            "type": "string",
            "description": "Optionnel. Code VATEX (BT-121) précisant le fondement réel de l'exonération quand il diffère du défaut de la catégorie (ex. formation Qualiopi art. 261-4-4°a, régime de marge, DOM, sous-traitance BTP).",
            "enum": [
              "VATEX-EU-AE",
              "VATEX-EU-D",
              "VATEX-EU-F",
              "VATEX-EU-G",
              "VATEX-EU-IC",
              "VATEX-EU-O",
              "VATEX-FR-FRANCHISE",
              "VATEX-FR-CNWVAT",
              "VATEX-FR-AUTOLIQ",
              "VATEX-FR-261"
            ]
          },
          "unit": {
            "type": "string",
            "enum": [
              "unit",
              "hour",
              "day",
              "month",
              "flat_rate",
              "kg",
              "m",
              "m2",
              "m3",
              "l"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ProductList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedList"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          }
        ]
      },
      "Quote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "quo_abc123"
          },
          "object": {
            "type": "string",
            "const": "quote"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "viewed",
              "accepted",
              "refused",
              "expired",
              "converted",
              "deleted"
            ]
          },
          "number": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "const": "eur"
          },
          "customer": {
            "type": "object"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/Totals"
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateQuoteInput": {
        "type": "object",
        "required": [
          "customerId",
          "lines"
        ],
        "description": "Créer un devis. La validité doit être fournie : soit `dates.validUntil`, soit `validityDays` (exclusivement l'un ou l'autre).",
        "anyOf": [
          {
            "required": [
              "validityDays"
            ]
          },
          {
            "required": [
              "dates"
            ],
            "properties": {
              "dates": {
                "required": [
                  "validUntil"
                ]
              }
            }
          }
        ],
        "properties": {
          "customerId": {
            "type": "string",
            "description": "ID du client (`cus_...`)"
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "dates": {
            "type": "object",
            "properties": {
              "issued": {
                "type": "string",
                "format": "date",
                "description": "Date d'émission (défaut : aujourd'hui)"
              },
              "validUntil": {
                "type": "string",
                "format": "date",
                "description": "Date de fin de validité du devis"
              }
            }
          },
          "validityDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "description": "Alternative à dates.validUntil : durée de validité en jours"
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "UpdateQuoteInput": {
        "type": "object",
        "description": "Champs modifiables d’un devis brouillon (PATCH partiel).",
        "properties": {
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "dates": {
            "type": "object",
            "properties": {
              "issued": {
                "type": "string",
                "format": "date"
              },
              "validUntil": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "validityDays": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "CreditNote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "crn_abc123"
          },
          "object": {
            "type": "string",
            "const": "credit_note"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "finalized",
              "credit_deposited",
              "credit_transmitted",
              "credit_approved",
              "credit_refused"
            ]
          },
          "number": {
            "type": "string",
            "nullable": true
          },
          "relatedInvoiceId": {
            "type": "string"
          },
          "relatedInvoiceNumber": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Original invoice number frozen when the credit note is created. Also included in credit-note event data; optional on historical credit notes."
          },
          "creditNoteType": {
            "type": "string"
          },
          "reasonCode": {
            "type": "string",
            "enum": [
              "defective_goods",
              "duplicate",
              "quality",
              "other"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/Totals"
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "originalInvoiceId": {
            "type": "string",
            "description": "Facture corrigée, sous le nom qu'emploie le modèle fiscal. `relatedInvoiceId` porte la même valeur et reste servi."
          },
          "originalTaxDecisionId": {
            "type": "string",
            "description": "Décision de la facture d'origine. Un avoir ne prend jamais de décision propre."
          },
          "taxSource": {
            "$ref": "#/components/schemas/TaxSource"
          },
          "taxSnapshot": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxSnapshot"
              }
            ],
            "description": "Position héritée, restreinte aux lignes créditées."
          },
          "documentStatus": {
            "$ref": "#/components/schemas/DocumentStatus"
          },
          "transmissionStatus": {
            "$ref": "#/components/schemas/TransmissionStatus"
          },
          "transmissionDetail": {
            "$ref": "#/components/schemas/TransmissionDetail"
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus"
          },
          "einvoicing": {
            "type": "object",
            "readOnly": true,
            "properties": {
              "paErrorCode": {
                "type": "string",
                "nullable": true,
                "description": "Code du dernier échec de dépôt : contrôle local ou réponse de la plateforme.",
                "readOnly": true
              },
              "paStatusCode": {
                "type": "string",
                "nullable": true,
                "description": "Code brut du statut plateforme, par exemple fr:200.",
                "readOnly": true
              },
              "rejectionCode": {
                "type": "string",
                "nullable": true,
                "description": "Code normalisé du motif DGFiP (REJ_ADR, REJ_SEMAN, REJ_SYNT, REJ_XSD, REJ_DOUBLON, AUTRE), HTTP plateforme (SUPER_PDP_400…) ou local (buyer_not_in_directory). Null si aucun code de motif n’est fourni ; les codes de statut fr:* restent dans paStatusCode. Remis à null au renvoi.",
                "readOnly": true
              },
              "rejectionSource": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "platform",
                  "buyer",
                  "facturino",
                  null
                ],
                "description": "Auteur du verdict enregistré par le producteur : platform (fr:213, réponse HTTP), buyer (fr:210, fr:207, fr:208), facturino (contrôle avant dépôt). Null après un renvoi ou pour un document ancien sans source enregistrée. Jamais déduit du motif.",
                "readOnly": true
              },
              "rejectionReason": {
                "type": "string",
                "nullable": true,
                "description": "Motif brut de la plateforme ou de l’acheteur, verbatim ; constat local pour la source facturino. Ne contient pas de code ou de note reconstruits. Null si absent.",
                "readOnly": true
              },
              "rejectionNote": {
                "type": "string",
                "nullable": true,
                "description": "Notes jointes par la plateforme ou l’acheteur, verbatim, concaténées par un retour à la ligne. Remises à null au renvoi.",
                "readOnly": true
              },
              "rejectionCategory": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "buyer_not_in_directory",
                  "addressing_error",
                  "other",
                  "format_invalid",
                  "semantic_error",
                  "duplicate",
                  "platform_auth",
                  "platform_unavailable",
                  "refused_by_buyer",
                  "suspended",
                  "unknown",
                  null
                ],
                "description": "Lecture stable parmi onze catégories. Priorité : code local, code DGFiP, code HTTP ; seul le pré-contrôle annuaire Super PDP dispose d’un repli textuel. REJ_ADR = addressing_error, REJ_SEMAN = semantic_error, REJ_SYNT/REJ_XSD = format_invalid, REJ_DOUBLON = duplicate, AUTRE = other (explication dans rejectionNote). Le statut fr:213 seul ne classe pas en sémantique. Null au renvoi.",
                "readOnly": true
              },
              "previousSubmissions": {
                "type": "array",
                "readOnly": true,
                "description": "Tentatives closes de ce document sur la plateforme, de la plus ancienne à la plus récente : un dépôt rejeté renvoyé sous le même numéro ouvre une nouvelle tentative, dont les identifiants courants figurent ci-dessus. Absent tant qu’aucun renvoi n’a eu lieu.",
                "items": {
                  "type": "object",
                  "required": [
                    "paId",
                    "paTransactionId",
                    "paIdempotencyKey",
                    "paStatus",
                    "paStatusCode",
                    "paErrorCode",
                    "rejectionReason",
                    "sentAt",
                    "closedAt"
                  ],
                  "properties": {
                    "paId": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paTransactionId": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paIdempotencyKey": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paStatus": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paStatusCode": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "paErrorCode": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "rejectionReason": {
                      "type": "string",
                      "nullable": true,
                      "readOnly": true
                    },
                    "rejectionCategory": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "buyer_not_in_directory",
                        "addressing_error",
                        "other",
                        "format_invalid",
                        "semantic_error",
                        "duplicate",
                        "platform_auth",
                        "platform_unavailable",
                        "refused_by_buyer",
                        "suspended",
                        "unknown",
                        null
                      ],
                      "description": "Lecture du rejet de cette tentative close.",
                      "readOnly": true
                    },
                    "sentAt": {
                      "type": "string",
                      "nullable": true,
                      "format": "date-time",
                      "readOnly": true
                    },
                    "closedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Ouverture de la tentative suivante.",
                      "readOnly": true
                    },
                    "rejectionCode": {
                      "type": "string",
                      "nullable": true,
                      "description": "Valeur archivée de einvoicing.rejectionCode pour cette tentative close.",
                      "readOnly": true
                    },
                    "rejectionSource": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "platform",
                        "buyer",
                        "facturino",
                        null
                      ],
                      "description": "Valeur archivée de einvoicing.rejectionSource pour cette tentative close.",
                      "readOnly": true
                    },
                    "rejectionNote": {
                      "type": "string",
                      "nullable": true,
                      "description": "Valeur archivée de einvoicing.rejectionNote pour cette tentative close.",
                      "readOnly": true
                    }
                  },
                  "readOnly": true
                }
              },
              "paId": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Identifiant du dépôt courant de cet avoir."
              },
              "paStatus": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Statut courant communiqué par la plateforme."
              },
              "depositedAt": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Date de dépôt de cet avoir.",
                "format": "date-time"
              },
              "paIdempotencyKey": {
                "type": "string",
                "readOnly": true,
                "description": "Clé de dédoublonnage du dépôt courant de cet avoir."
              },
              "ereportingId": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Identifiant de la déclaration e-reporting de cet avoir."
              },
              "obligation": {
                "$ref": "#/components/schemas/ObligationFollowUp"
              },
              "routingIdentifier": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Read-only routingIdentifier for the current credit-note deposit attempt."
              },
              "senderRoutingIdentifier": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Read-only senderRoutingIdentifier for the current credit-note deposit attempt."
              },
              "buyerReachableAt": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Read-only buyerReachableAt for the current credit-note deposit attempt."
              },
              "directoryCheckedAt": {
                "type": "string",
                "nullable": true,
                "readOnly": true,
                "description": "Read-only directoryCheckedAt for the current credit-note deposit attempt."
              }
            }
          },
          "companyId": {
            "type": "string",
            "readOnly": true
          },
          "customer": {
            "type": "object",
            "properties": {
              "ref": {
                "type": "string"
              },
              "snapshot": {
                "type": "object"
              }
            }
          },
          "currency": {
            "type": "string",
            "const": "eur"
          },
          "reason": {
            "type": "string"
          },
          "dates": {
            "$ref": "#/components/schemas/CreditNoteDates"
          },
          "archive": {
            "$ref": "#/components/schemas/InvoiceArchive",
            "nullable": true,
            "readOnly": true
          },
          "lifecycle": {
            "type": "array",
            "readOnly": true,
            "items": {
              "$ref": "#/components/schemas/LifecycleEntry"
            },
            "description": "Historique append-only. Les nouvelles lignes portent code et params ; details reste rendu en français pour les lecteurs historiques."
          },
          "processed": {
            "type": "boolean",
            "readOnly": true
          },
          "updated": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "ereportingBlock": {
            "$ref": "#/components/schemas/EReportingBlock"
          }
        }
      },
      "CreateCreditNoteInput": {
        "type": "object",
        "required": [
          "relatedInvoiceId",
          "creditNoteType",
          "reasonCode",
          "creditedLines",
          "dates"
        ],
        "properties": {
          "customerId": {
            "type": "string",
            "description": "Facultatif : l'acheteur est imposé par la facture d'origine. Fourni, il doit correspondre — un avoir émis à un tiers est refusé."
          },
          "relatedInvoiceId": {
            "type": "string",
            "description": "Facture d’origine (`inv_...`)"
          },
          "creditNoteType": {
            "type": "string",
            "enum": [
              "total",
              "partial",
              "commercial",
              "financial"
            ],
            "description": "Type d’avoir"
          },
          "reasonCode": {
            "type": "string",
            "enum": [
              "defective_goods",
              "duplicate",
              "quality",
              "other"
            ]
          },
          "reason": {
            "type": "string",
            "maxLength": 500,
            "description": "Motif libre (optionnel)"
          },
          "dates": {
            "type": "object",
            "required": [
              "issued"
            ],
            "properties": {
              "issued": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "creditedLines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/DecidedCreditLineInput"
            },
            "description": "Quelles lignes originales sont créditées, et pour combien chacune. Un avoir hérite strictement de la position fiscale de la facture qu'il corrige — source, snapshot et lignes — et ne réénonce jamais de TVA."
          }
        },
        "description": "Un avoir référence les lignes originales de la facture décidée qu'il corrige (`creditedLines`) et hérite strictement de sa position fiscale — source, snapshot et lignes."
      },
      "UpdateCreditNoteInput": {
        "type": "object",
        "description": "Champs modifiables d’un avoir brouillon (PATCH partiel).",
        "properties": {
          "creditedLines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/DecidedCreditLineInput"
            },
            "description": "Lignes créditées d'une facture décidée. Mutuellement exclusif avec `items`."
          },
          "reason": {
            "type": "string",
            "maxLength": 500
          },
          "notes": {
            "type": "string",
            "maxLength": 2000
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pay_abc123"
          },
          "object": {
            "type": "string",
            "const": "payment"
          },
          "type": {
            "type": "string",
            "enum": [
              "payment",
              "refund"
            ],
            "description": "payment = encaissement ; refund = remboursement (montant négatif). Un refund ne peut pas être annulé via /payments/{id}/cancel — émettez un avoir rectificatif."
          },
          "status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "cancelled"
            ],
            "description": "Présent uniquement lorsque le paiement a été annulé."
          },
          "amount": {
            "type": "integer",
            "description": "Montant en centimes entiers (négatif pour un refund)"
          },
          "method": {
            "type": "string",
            "description": "Moyen de paiement. De nouvelles valeurs peuvent être ajoutées ; les clients doivent tolérer les valeurs inconnues.",
            "enum": [
              "transfer",
              "card",
              "check",
              "cash",
              "direct_debit",
              "sepa",
              "paypal",
              "other"
            ]
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "paidAt": {
            "type": "string",
            "format": "date-time"
          },
          "recorded_by": {
            "type": "string",
            "enum": [
              "api",
              "app",
              "system"
            ],
            "readOnly": true,
            "description": "Origine de l’enregistrement : api (clé API), app (compte connecté), system (automatisation). Le stockage interne recordedBy n’est pas exposé."
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "fr212": {
            "type": "object",
            "nullable": true,
            "description": "Où en est le statut « Encaissée » (fr:212) de cet encaissement auprès de la plateforme. Présent quand la décision fiscale du document impose ce statut ; absent sinon. `pending` : en file ; `awaiting_deposit` : attend un dépôt accepté de la facture ; `sent` : transmis (`sentAt`) ; `blocked` : ne peut pas être transmis (`lastErrorCode`) ; `reconciliation_required` / `failed` : à rapprocher.",
            "required": [
              "state",
              "sentAt",
              "lastErrorCode",
              "updatedAt"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "pending",
                  "awaiting_deposit",
                  "sent",
                  "blocked",
                  "reconciliation_required",
                  "failed"
                ],
                "readOnly": true
              },
              "sentAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time",
                "readOnly": true
              },
              "lastErrorCode": {
                "type": "string",
                "nullable": true,
                "readOnly": true
              },
              "lastErrorReason": {
                "type": "string",
                "description": "Motif brut de la plateforme lorsque le statut est refusé définitivement (pa_lifecycle_rejected) ou attend un état encaissable (pa_lifecycle_not_ready). Absent si aucun motif n’est fourni.",
                "readOnly": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "readOnly": true
              },
              "obligation": {
                "$ref": "#/components/schemas/ObligationFollowUp"
              }
            },
            "readOnly": true
          },
          "companyId": {
            "type": "string",
            "readOnly": true
          },
          "invoiceId": {
            "type": "string",
            "readOnly": true
          },
          "creditNoteId": {
            "type": "string",
            "readOnly": true
          },
          "cancelledAt": {
            "type": "string",
            "readOnly": true,
            "format": "date-time"
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "reportingFollowUp": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          }
        }
      },
      "CreatePaymentInput": {
        "type": "object",
        "required": [
          "amount",
          "method",
          "paidAt"
        ],
        "properties": {
          "amount": {
            "type": "integer",
            "description": "Montant en centimes d'euros"
          },
          "method": {
            "type": "string",
            "description": "Moyen de paiement. De nouvelles valeurs peuvent être ajoutées ; les clients doivent tolérer les valeurs inconnues.",
            "enum": [
              "transfer",
              "card",
              "check",
              "cash",
              "direct_debit",
              "sepa",
              "paypal",
              "other"
            ]
          },
          "reference": {
            "type": "string"
          },
          "paidAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FinalizeInvoiceInput": {
        "type": "object",
        "description": "Corps FACULTATIF de la finalisation. Absent ou vide, la facture est simplement numérotée.",
        "properties": {
          "payment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreatePaymentInput"
              }
            ],
            "description": "Encaissement reçu AVANT l'émission. Appliqué dans la transaction de finalisation, donc l'original émis est déjà acquitté. Mêmes champs et mêmes bornes que `POST /v1/invoices/{id}/payments`."
          }
        },
        "additionalProperties": false
      },
      "PaymentList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaginatedList"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          }
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "job_abc123"
          },
          "object": {
            "type": "string",
            "const": "job"
          },
          "type": {
            "type": "string",
            "enum": [
              "pdf",
              "facturx",
              "fec",
              "quote_pdf",
              "audit_trail_pdf",
              "csv_import_customers",
              "csv_import_products",
              "csv_export_customers",
              "csv_export_products",
              "rgpd_export",
              "invoice_export"
            ]
          },
          "status": {
            "type": "string",
            "description": "`superseded` : état TERMINAL sans livrable — le rendu a été produit pour une révision du grand livre qu'un encaissement, une annulation ou un remboursement a dépassée depuis. Ce n'est pas un échec ; redemandez `GET /v1/invoices/{id}/pdf`, qui publiera un rendu à jour. De nouvelles valeurs peuvent être ajoutées : tolérez les valeurs inconnues.",
            "enum": [
              "pending",
              "processing",
              "completed",
              "completed_with_errors",
              "failed",
              "superseded"
            ]
          },
          "superseded": {
            "type": "object",
            "nullable": true,
            "description": "Présent sur un job `superseded` : la révision pour laquelle le rendu avait été publié, et celle que porte le document.",
            "properties": {
              "revision": {
                "type": "integer"
              },
              "currentRevision": {
                "type": "integer"
              }
            }
          },
          "hint": {
            "type": "string",
            "nullable": true,
            "description": "Marche à suivre quand le job n'a pas de livrable."
          },
          "progress": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "result": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "download_url": {
            "type": "string",
            "nullable": true,
            "description": "URL signée de téléchargement (15 min) — présente quand le job est `completed` ou `completed_with_errors` et qu'un livrable existe."
          },
          "expires_in": {
            "type": "integer",
            "nullable": true,
            "description": "Durée de validité de `download_url` en secondes."
          },
          "url": {
            "type": "string",
            "nullable": true,
            "description": "Alias de `download_url` (compatibilité SDK)."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Alias ISO 8601 de l'expiration de `download_url`."
          }
        }
      },
      "CreateWebhookEndpointInput": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Liste des types d'événements à recevoir (ex: `invoice.created`, `invoice.paid`)"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "UpdateWebhookEndpointInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "description": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "CreateRecurringInvoiceInput": {
        "type": "object",
        "required": [
          "customerId",
          "frequency",
          "startDate",
          "nextGenerationDate",
          "templateInvoice",
          "taxInputs"
        ],
        "properties": {
          "customerId": {
            "type": "string"
          },
          "customer": {
            "type": "object",
            "description": "Identité acheteur figée et normalisée au moment de la décision.",
            "properties": {
              "customerId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "nature": {
                "type": "string",
                "enum": [
                  "business",
                  "consumer"
                ]
              },
              "natureBasis": {
                "type": "string"
              },
              "territoryId": {
                "type": "string"
              },
              "territoryKind": {
                "type": "string"
              },
              "declaredCountry": {
                "type": "string"
              },
              "declaredPostalCode": {
                "type": "string",
                "nullable": true
              },
              "legalRegistrationId": {
                "type": "string",
                "nullable": true
              },
              "vatNumber": {
                "type": "string",
                "nullable": true
              },
              "crossBorderTaxableStatus": {
                "type": "string"
              },
              "businessStatusBasis": {
                "type": "string"
              }
            }
          },
          "sellerProfileId": {
            "type": "string"
          },
          "sellerProfileRevision": {
            "type": "integer"
          },
          "sellerProfile": {
            "$ref": "#/components/schemas/SellerTaxProfile"
          },
          "frequency": {
            "type": "string",
            "enum": [
              "weekly",
              "monthly",
              "quarterly",
              "yearly",
              "custom"
            ]
          },
          "customInterval": {
            "type": "integer"
          },
          "customUnit": {
            "type": "string",
            "enum": [
              "day",
              "week",
              "month",
              "year"
            ]
          },
          "startDate": {
            "type": "string",
            "format": "date"
          },
          "nextGenerationDate": {
            "type": "string",
            "format": "date"
          },
          "endDate": {
            "type": "string",
            "format": "date"
          },
          "templateInvoice": {
            "type": "object"
          },
          "autoFinalize": {
            "type": "boolean",
            "default": false
          },
          "autoSend": {
            "type": "boolean",
            "default": false
          },
          "taxInputs": {
            "type": "object",
            "description": "Entrées commerciales des décisions par occurrence. Une récurrence ne stocke JAMAIS de décision : une décision est datée, et la réutiliser d'une occurrence à l'autre appliquerait les règles du trimestre précédent. Chaque occurrence prend une NOUVELLE décision, sous la source fiscale unique de la récurrence (`taxSource`), avec sa propre date d'effet. Requis : une occurrence indécidable ne génère aucune facture.",
            "required": [
              "taxSource",
              "priceMode",
              "lines"
            ],
            "properties": {
              "taxSource": {
                "$ref": "#/components/schemas/TaxSource"
              },
              "priceMode": {
                "$ref": "#/components/schemas/PriceMode"
              },
              "lines": {
                "type": "array",
                "minItems": 1,
                "maxItems": 250,
                "items": {
                  "description": "Ligne d'opération, plus sa présentation (`unit`, `product`). Source `facturino` : mêmes champs qu'une ligne de `FacturinoTaxDecisionInput`. Source `integration` : mêmes champs qu'une ligne de `IntegrationTaxDecisionLineInput` (la TVA fournie est revalidée à CHAQUE occurrence).",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/TaxDecisionLineInput"
                        }
                      ]
                    },
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/IntegrationTaxDecisionLineInput"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "UpdateRecurringInvoiceInput": {
        "type": "object",
        "properties": {
          "frequency": {
            "type": "string"
          },
          "endDate": {
            "type": "string",
            "format": "date"
          },
          "autoFinalize": {
            "type": "boolean"
          },
          "autoSend": {
            "type": "boolean"
          },
          "taxInputs": {
            "type": "object",
            "description": "Entrées commerciales des décisions par occurrence. Une récurrence ne stocke JAMAIS de décision : une décision est datée, et la réutiliser d'une occurrence à l'autre appliquerait les règles du trimestre précédent. Chaque occurrence prend une NOUVELLE décision, sous la source fiscale unique de la récurrence (`taxSource`), avec sa propre date d'effet. Requis : une occurrence indécidable ne génère aucune facture.",
            "required": [
              "taxSource",
              "priceMode",
              "lines"
            ],
            "properties": {
              "taxSource": {
                "$ref": "#/components/schemas/TaxSource"
              },
              "priceMode": {
                "$ref": "#/components/schemas/PriceMode"
              },
              "lines": {
                "type": "array",
                "minItems": 1,
                "maxItems": 250,
                "items": {
                  "description": "Ligne d'opération, plus sa présentation (`unit`, `product`). Source `facturino` : mêmes champs qu'une ligne de `FacturinoTaxDecisionInput`. Source `integration` : mêmes champs qu'une ligne de `IntegrationTaxDecisionLineInput` (la TVA fournie est revalidée à CHAQUE occurrence).",
                  "oneOf": [
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/TaxDecisionLineInput"
                        }
                      ]
                    },
                    {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/IntegrationTaxDecisionLineInput"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "CreateEreportingInput": {
        "type": "object",
        "required": [
          "type",
          "period",
          "lines"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "b2c",
              "international",
              "intra_eu",
              "payment_b2c",
              "payment_international"
            ]
          },
          "period": {
            "type": "string",
            "description": "Période de déclaration (ex: `2026-01`)"
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "category",
                "amount",
                "vatRate",
                "vatAmount"
              ],
              "properties": {
                "category": {
                  "type": "string"
                },
                "amount": {
                  "type": "integer",
                  "description": "Montant HT en centimes"
                },
                "vatRate": {
                  "type": "integer",
                  "description": "Taux TVA en centièmes de pourcent"
                },
                "vatAmount": {
                  "type": "integer",
                  "description": "Montant TVA en centimes"
                },
                "vatCategoryCode": {
                  "type": "string",
                  "enum": [
                    "S",
                    "Z",
                    "E",
                    "AE",
                    "K",
                    "G",
                    "O"
                  ],
                  "description": "Catégorie de TVA UNTDID 5305 figée par la décision. Optionnelle sur les déclarations anciennes."
                },
                "vatexCode": {
                  "type": "string",
                  "nullable": true,
                  "minLength": 1,
                  "maxLength": 30,
                  "description": "Code VATEX figé par la décision ; null signifie aucun code."
                },
                "partnerName": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "Raison sociale figée de l’acheteur ; requise pour produire son identifiant hors UE (G2.19, TT-36/TT-37)."
                },
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "issueDate": {
                  "type": "string",
                  "format": "date"
                },
                "invoiceNumber": {
                  "type": "string"
                },
                "country": {
                  "type": "string"
                },
                "partnerVat": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "minimum": 1
                },
                "documentType": {
                  "type": "string",
                  "enum": [
                    "380",
                    "381"
                  ],
                  "description": "Lignes unitaires : type du document source (BT-3), `380` facture ou `381` avoir. Absent sur les déclarations antérieures."
                },
                "originalInvoiceNumber": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "description": "Avoir (`381`) uniquement : numéro de la facture corrigée (BT-25, TT-30). Requis, avec `originalInvoiceDate`, pour transmettre un avoir unitaire (G1.32) ; son absence entraîne un refus avant tout appel à la plateforme."
                },
                "originalInvoiceDate": {
                  "type": "string",
                  "format": "date",
                  "description": "Avoir (`381`) uniquement : date d’émission de la facture corrigée, `YYYY-MM-DD` (BT-26, TT-31). Requise, avec `originalInvoiceNumber`, pour transmettre un avoir unitaire (G1.32)."
                }
              }
            }
          }
        }
      },
      "Buyer": {
        "type": "object",
        "required": [
          "companyName",
          "address"
        ],
        "properties": {
          "companyName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "siret": {
            "type": "string",
            "description": "SIRET acheteur (14 chiffres, BT-46). Optionnel pour l’international."
          },
          "vatNumber": {
            "type": "string",
            "description": "Numéro de TVA intracommunautaire"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "deliveryAddress": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Address"
              }
            ],
            "description": "Adresse de livraison (BG-7)"
          }
        }
      },
      "PaymentTerms": {
        "type": "object",
        "required": [
          "terms",
          "termsDays",
          "method",
          "latePaymentRate",
          "collectionFee"
        ],
        "properties": {
          "terms": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Conditions de paiement (texte libre). Cap aligné sur la limite CII BT-20 (peut contenir un échéancier complet)."
          },
          "termsDays": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "description": "Délai de paiement en jours"
          },
          "method": {
            "type": "string",
            "description": "Moyen de paiement. De nouvelles valeurs peuvent être ajoutées ; les clients doivent tolérer les valeurs inconnues.",
            "enum": [
              "transfer",
              "card",
              "check",
              "cash",
              "direct_debit",
              "sepa",
              "paypal"
            ]
          },
          "iban": {
            "type": "string",
            "minLength": 15,
            "maxLength": 34
          },
          "bic": {
            "type": "string",
            "minLength": 8,
            "maxLength": 11
          },
          "earlyPaymentDiscount": {
            "type": "string",
            "maxLength": 200,
            "description": "Mention escompte (BT-20)"
          },
          "latePaymentRate": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "description": "Taux de pénalités de retard"
          },
          "collectionFee": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "description": "Indemnité forfaitaire de recouvrement"
          }
        }
      },
      "Einvoicing": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "facturx",
              "ubl",
              "cii"
            ],
            "default": "facturx"
          },
          "profile": {
            "type": "string",
            "enum": [
              "EN16931",
              "BASIC",
              "EXTENDED"
            ],
            "default": "EN16931"
          }
        }
      },
      "EReporting": {
        "type": "object",
        "description": "Déclaration e-reporting (art. 290 CGI). Montants en centimes, taux en centièmes de pourcent.",
        "properties": {
          "id": {
            "type": "string",
            "example": "rep_1a2b3c"
          },
          "object": {
            "type": "string",
            "enum": [
              "ereporting"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "submitted",
              "accepted",
              "rejected",
              "skipped"
            ],
            "description": "Projection RÉSUMÉ de `state` : `reserved`→`draft`, `blocked`→`skipped`, `acknowledged`→`submitted` (accusé technique de la plateforme, pas une acceptation). `accepted` = acceptation par l'administration, rapportée par la plateforme ; `rejected` = refus de la plateforme ou de l'administration, motif dans `paRejectionReason`. Dérivée, jamais une autorité propre."
          },
          "type": {
            "type": "string",
            "enum": [
              "b2c",
              "international",
              "intra_eu",
              "domestic_b2b",
              "payment_b2c",
              "payment_international",
              "payment_intra_eu",
              "payment_domestic_b2b"
            ]
          },
          "period": {
            "type": "string",
            "example": "2026-01"
          },
          "periodStart": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "periodEnd": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "totalHT": {
            "type": "integer",
            "description": "Total HT en centimes"
          },
          "totalTVA": {
            "type": "integer",
            "description": "Total TVA en centimes"
          },
          "totalTTC": {
            "type": "integer",
            "description": "Total TTC en centimes"
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "category": {
                  "type": "string"
                },
                "amount": {
                  "type": "integer",
                  "description": "Base HT en centimes"
                },
                "vatRate": {
                  "type": "integer",
                  "description": "Taux en centièmes de pourcent (2000 = 20,00 %)"
                },
                "vatAmount": {
                  "type": "integer",
                  "description": "TVA en centimes"
                },
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "issueDate": {
                  "type": "string",
                  "format": "date"
                },
                "invoiceNumber": {
                  "type": "string"
                },
                "country": {
                  "type": "string"
                },
                "partnerVat": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "minimum": 1
                },
                "vatCategoryCode": {
                  "type": "string",
                  "enum": [
                    "S",
                    "Z",
                    "E",
                    "AE",
                    "K",
                    "G",
                    "O"
                  ],
                  "description": "Catégorie de TVA UNTDID 5305 figée par la décision. Optionnelle sur les déclarations anciennes."
                },
                "vatexCode": {
                  "type": "string",
                  "nullable": true,
                  "minLength": 1,
                  "maxLength": 30,
                  "description": "Code VATEX figé par la décision ; null signifie aucun code."
                },
                "partnerName": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "Raison sociale figée de l’acheteur ; requise pour produire son identifiant hors UE (G2.19, TT-36/TT-37)."
                },
                "documentType": {
                  "type": "string",
                  "enum": [
                    "380",
                    "381"
                  ],
                  "description": "Lignes unitaires : type du document source (BT-3), `380` facture ou `381` avoir. Absent sur les déclarations antérieures."
                },
                "originalInvoiceNumber": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100,
                  "description": "Avoir (`381`) uniquement : numéro de la facture corrigée (BT-25, TT-30). Requis, avec `originalInvoiceDate`, pour transmettre un avoir unitaire (G1.32) ; son absence entraîne un refus avant tout appel à la plateforme."
                },
                "originalInvoiceDate": {
                  "type": "string",
                  "format": "date",
                  "description": "Avoir (`381`) uniquement : date d’émission de la facture corrigée, `YYYY-MM-DD` (BT-26, TT-31). Requise, avec `originalInvoiceNumber`, pour transmettre un avoir unitaire (G1.32)."
                }
              }
            }
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "state": {
            "type": "string",
            "enum": [
              "reserved",
              "submitting",
              "queued",
              "submitted",
              "acknowledged",
              "accepted",
              "rejected",
              "blocked",
              "reconciliation_required"
            ],
            "description": "État réel. `reserved` = constituée, RIEN n'a été transmis — ce n'est jamais « déclarée ». `blocked` = intransmissible ; l'obligation reste due et n'est jamais considérée comme traitée."
          },
          "volet": {
            "type": "string",
            "enum": [
              "transaction",
              "payment"
            ]
          },
          "blockedReason": {
            "type": "string",
            "nullable": true,
            "description": "Reason the declaration cannot currently be sent (for example pa_not_supported, reporting_route_confirmation_required or reporting_precondition_failed). Extensible technical code; use obligation.owner and obligation.action for responsibility.",
            "examples": [
              "ereporting_not_enabled",
              "pa_not_supported",
              "reporting_precondition_failed",
              "reporting_route_confirmation_required",
              "reporting_transport_incomplete",
              "pa_connection_unavailable",
              "tax_decision_required",
              "tax_snapshot_inconsistent"
            ]
          },
          "reconciliationReason": {
            "type": "string",
            "nullable": true,
            "description": "Motif borné d'un résultat distant ambigu. Aucun renvoi aveugle n'est effectué tant que le rapprochement n'est pas résolu."
          },
          "paRejectionReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Motif de refus transmis par la plateforme. Identique quel que soit le canal par lequel le refus est arrivé (réponse synchrone, transmission planifiée, webhook plateforme)."
          },
          "attempt": {
            "type": "integer",
            "minimum": 1,
            "description": "Numéro de tentative de ces faits. `1` pour la première déclaration ; incrémenté uniquement par `POST /v1/ereporting/declarations/{id}/retry`."
          },
          "supersedesDeclarationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Déclaration refusée que cette tentative remplace, le cas échéant."
          },
          "supersededByDeclarationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tentative créée à partir de cette déclaration refusée. Une déclaration refusée n'est remplacée qu'une seule fois."
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          },
          "paRejectionCode": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Structured rejection code returned by the platform, preserved without deriving it from text."
          }
        }
      },
      "SupplyCategory": {
        "type": "string",
        "enum": [
          "goods",
          "services",
          "electronically_supplied_services",
          "deposit",
          "ancillary_costs"
        ],
        "description": "Nature fiscale de la ligne. `electronically_supplied_services` porte des règles de territorialité propres et ne se confond pas avec une prestation ordinaire. `deposit` et `ancillary_costs` suivent l'opération principale."
      },
      "PriceMode": {
        "type": "string",
        "enum": [
          "tax_exclusive",
          "tax_inclusive"
        ],
        "description": "Base des montants transmis. `tax_inclusive` : les montants incluent la TVA, qui est extraite ; `tax_exclusive` : la TVA est ajoutée."
      },
      "RateCategory": {
        "type": "string",
        "enum": [
          "standard",
          "intermediate",
          "reduced",
          "super_reduced",
          "specific"
        ]
      },
      "TaxDecisionDiscount": {
        "type": "object",
        "description": "Remise explicite. `percent` en centièmes de pourcent (2500 = 25,00 %), `amount` en centimes entiers.",
        "required": [
          "type",
          "value"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "percent",
              "amount"
            ]
          },
          "value": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "TaxDecisionLineInput": {
        "type": "object",
        "required": [
          "reference",
          "description",
          "category",
          "rateCategory",
          "unitAmount",
          "quantity"
        ],
        "properties": {
          "reference": {
            "type": "string",
            "maxLength": 64,
            "description": "Référence stable côté appelant, reprise sur la ligne décidée."
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "category": {
            "$ref": "#/components/schemas/SupplyCategory"
          },
          "relatedCategory": {
            "type": "string",
            "enum": [
              "goods",
              "services",
              "electronically_supplied_services"
            ],
            "description": "Nature de l'opération principale d'un acompte ou de frais accessoires."
          },
          "rateCategory": {
            "$ref": "#/components/schemas/RateCategory"
          },
          "placeOfSupplyRule": {
            "type": "string",
            "description": "Règle de territorialité revendiquée. Seul `general` est implémenté ; toute dérogation est signalée comme non prise en charge plutôt qu'approximée."
          },
          "goodsMovement": {
            "type": "string",
            "enum": [
              "stays_in_seller_territory",
              "dispatched_to_buyer_territory",
              "unknown"
            ]
          },
          "unitAmount": {
            "type": "integer",
            "description": "Montant unitaire en centimes entiers, dans le `priceMode` de la demande."
          },
          "quantity": {
            "type": "string",
            "description": "Quantité décimale transmise en CHAÎNE (jamais un flottant) — jusqu'à 6 décimales, strictement positive."
          },
          "discount": {
            "$ref": "#/components/schemas/TaxDecisionDiscount"
          }
        }
      },
      "LocationEvidenceInput": {
        "type": "object",
        "description": "Un élément de preuve de localisation (règlement d'exécution UE 282/2011). Le SIGNAL territorial est transmis, jamais le signal brut : un pays, pas une adresse IP ni un IBAN.",
        "required": [
          "kind",
          "country",
          "thirdParty",
          "source",
          "collectedAt"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "billing_address",
              "ip_geolocation",
              "bank_details",
              "sim_mobile_country",
              "fixed_line",
              "other_commercial"
            ]
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2
          },
          "postalCode": {
            "type": "string",
            "maxLength": 16,
            "description": "Requis pour billing_address et other_commercial lorsque le pays comporte plusieurs territoires de TVA (FR, ES, IT, DE, FI, GR, PT). Facultatif pour les preuves réseau (ip_geolocation, bank_details, sim_mobile_country, fixed_line), résolues au niveau du pays lorsqu'il est absent."
          },
          "thirdParty": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "psp",
              "network",
              "bank",
              "declared",
              "other"
            ]
          },
          "collectedAt": {
            "type": "string",
            "format": "date"
          },
          "reference": {
            "type": "string",
            "maxLength": 100,
            "description": "Référence OPAQUE bornée (identifiant de charge PSP, lot de géolocalisation…), conservée avec la décision. Jamais un signal brut : une adresse IP ou un IBAN placé ici est refusé (422) ; tout autre contenu relève de la responsabilité de l'appelant."
          }
        }
      },
      "ViesResult": {
        "type": "object",
        "nullable": true,
        "description": "Résultat de la consultation VIES. Le statut est conservé, jamais la réponse brute.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "valid",
              "invalid",
              "unavailable",
              "invalid_format"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "normalizedVatNumber": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "vies"
            ]
          },
          "returnedName": {
            "type": "string",
            "nullable": true
          },
          "consultationNumber": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ReportingObligations": {
        "type": "object",
        "description": "Les trois axes d'obligation, indépendants : une opération peut les cumuler.",
        "properties": {
          "invoiceChannel": {
            "type": "string",
            "enum": [
              "einvoicing",
              "none"
            ],
            "description": "`einvoicing` = la facture est destinée à une plateforme agréée. C'est la SEULE condition d'un dépôt PA."
          },
          "transactionReporting": {
            "type": "string",
            "enum": [
              "ereporting",
              "none",
              "outside_scope"
            ]
          },
          "paymentReporting": {
            "type": "string",
            "enum": [
              "fr212",
              "ereporting",
              "none"
            ],
            "description": "`fr212` : les encaissements voyagent par le statut « Encaissée » de la plateforme. `ereporting` : volet paiement autonome."
          }
        }
      },
      "TaxDecisionStatus": {
        "type": "string",
        "enum": [
          "final",
          "pending_verification",
          "unsupported"
        ],
        "description": "Seule une décision `final` porte des montants. Sur toute autre valeur, `totals` et `amountToCharge` valent `null` — jamais `0` : absent ne signifie pas que le montant est nul."
      },
      "TaxDecision": {
        "type": "object",
        "properties": {
          "warnings": {
            "type": "array",
            "readOnly": true,
            "items": {
              "$ref": "#/components/schemas/BuyerNatureWarning"
            },
            "description": "Non-blocking identity hints derived from the frozen buyer. The decision is created even when a hint is present."
          },
          "id": {
            "type": "string",
            "description": "Préfixe `taxdec_`"
          },
          "object": {
            "type": "string",
            "enum": [
              "tax_decision"
            ]
          },
          "taxSource": {
            "$ref": "#/components/schemas/TaxSource"
          },
          "companyId": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/TaxDecisionStatus"
          },
          "customerId": {
            "type": "string"
          },
          "customer": {
            "type": "object",
            "description": "Identité et qualification acheteur figées au moment de la décision.",
            "properties": {
              "customerId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "nature": {
                "type": "string",
                "enum": [
                  "business",
                  "consumer"
                ]
              },
              "natureBasis": {
                "type": "string"
              },
              "territoryId": {
                "type": "string"
              },
              "territoryKind": {
                "type": "string"
              },
              "declaredCountry": {
                "type": "string"
              },
              "declaredPostalCode": {
                "type": "string",
                "nullable": true
              },
              "legalRegistrationId": {
                "type": "string",
                "nullable": true
              },
              "vatNumber": {
                "type": "string",
                "nullable": true
              },
              "crossBorderTaxableStatus": {
                "type": "string"
              },
              "businessStatusBasis": {
                "type": "string"
              }
            }
          },
          "sellerProfileId": {
            "type": "string"
          },
          "sellerProfileRevision": {
            "type": "integer"
          },
          "sellerProfile": {
            "$ref": "#/components/schemas/SellerTaxProfile"
          },
          "currency": {
            "type": "string"
          },
          "priceMode": {
            "$ref": "#/components/schemas/PriceMode"
          },
          "effectiveAt": {
            "type": "string",
            "format": "date",
            "description": "Date civile à laquelle l'opération prend effet."
          },
          "decidedAt": {
            "type": "string",
            "format": "date-time"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Au-delà, la décision ne peut plus ouvrir un paiement. Elle reste lisible et immuable."
          },
          "checkoutValidityPolicy": {
            "type": "string",
            "enum": [
              "checkout-validity-v1"
            ]
          },
          "expired": {
            "type": "boolean",
            "description": "Dérivé de l'horloge serveur au moment de la lecture, jamais stocké."
          },
          "rulesVersion": {
            "type": "string"
          },
          "reportingCalendar": {
            "type": "string"
          },
          "roundingPolicy": {
            "type": "string",
            "description": "Politique d'arrondi appliquée : ligne par ligne au demi supérieur, puis somme. Aucune re-agrégation ultérieure."
          },
          "requestFingerprint": {
            "type": "string",
            "description": "Empreinte SHA-256 de la requête canonique, sans clé d'idempotence brute."
          },
          "operationFingerprint": {
            "type": "string",
            "description": "Empreinte SHA-256 de l'opération commerciale, utilisée pour contrôler les reprises."
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaxDecisionLine"
            }
          },
          "totals": {
            "nullable": true,
            "type": "object",
            "properties": {
              "totalHT": {
                "type": "integer"
              },
              "totalVAT": {
                "type": "integer"
              },
              "totalTTC": {
                "type": "integer"
              }
            }
          },
          "vatBreakdown": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rateCentipercent": {
                  "type": "integer"
                },
                "categoryCode": {
                  "type": "string"
                },
                "vatexCode": {
                  "type": "string",
                  "nullable": true
                },
                "base": {
                  "type": "integer"
                },
                "amount": {
                  "type": "integer"
                }
              }
            }
          },
          "amountToCharge": {
            "type": "integer",
            "nullable": true,
            "description": "Montant TTC exact de l'opération, à facturer et éventuellement à percevoir, en centimes entiers. `null` hors décision finale."
          },
          "invoiceChannel": {
            "type": "string",
            "enum": [
              "einvoicing",
              "none"
            ],
            "nullable": true
          },
          "transactionReporting": {
            "type": "string",
            "enum": [
              "ereporting",
              "none",
              "outside_scope"
            ],
            "nullable": true
          },
          "paymentReporting": {
            "type": "string",
            "enum": [
              "fr212",
              "ereporting",
              "none"
            ],
            "nullable": true
          },
          "settledObligations": {
            "nullable": true,
            "type": "object",
            "description": "Axes que le droit français tranche MALGRÉ une décision non finale. `null` sur une décision finale : les trois axes de premier niveau sont alors les axes tranchés, et rien ne les duplique. Sur une décision `pending_verification` ou `unsupported`, chaque axe vaut soit la valeur tranchée, soit `null` lorsqu'il dépend réellement du traitement qui n'a pas pu être conclu. Cet objet n'autorise RIEN : une décision non finale n'est pas facturable, n'est jamais déposée sur une plateforme agréée et n'ouvre aucun paiement.",
            "properties": {
              "invoiceChannel": {
                "type": "string",
                "enum": [
                  "einvoicing",
                  "none"
                ],
                "nullable": true
              },
              "transactionReporting": {
                "type": "string",
                "enum": [
                  "ereporting",
                  "none",
                  "outside_scope"
                ],
                "nullable": true
              },
              "paymentReporting": {
                "type": "string",
                "enum": [
                  "fr212",
                  "ereporting",
                  "none"
                ],
                "nullable": true
              }
            }
          },
          "euB2cDestination": {
            "type": "object",
            "nullable": true,
            "description": "Ce que la règle B2C UE à destination a conclu, FIGÉ comme donnée. `null` — et absent sur un document écrit avant l'existence de ce champ — pour toute opération que la règle n'atteint pas. Présent dès qu'elle couvre une ligne, y compris sur une décision NON finale : il dit alors exactement ce qui est tranché et ce qui manque. Une chaîne `reference` ne s'audite pas : le verdict, les chiffres du seuil, le mécanisme déclaratif et l'entrée de taux — version, source, date de vérification, période et région — sont donc lisibles séparément.",
            "properties": {
              "coveredLineIds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Références des lignes que la règle couvre."
              },
              "ruleKinds": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "tbe_services",
                    "intra_eu_distance_sale"
                  ]
                },
                "description": "Familles concernées : service fourni par voie électronique (art. 58) et vente à distance intracommunautaire (art. 33, a)."
              },
              "destinationMemberState": {
                "type": "string",
                "description": "État membre du consommateur, ISO 3166-1 alpha-2."
              },
              "destinationTerritoryId": {
                "type": "string",
                "description": "Territoire canonique du consommateur, région comprise lorsque l'État en publie une (ex. `PT-MA`)."
              },
              "place": {
                "type": "string",
                "enum": [
                  "origin",
                  "destination",
                  null
                ],
                "nullable": true,
                "description": "Lieu d'imposition des lignes couvertes. `null` tant que la règle ne l'a pas tranché."
              },
              "basis": {
                "type": "string",
                "enum": [
                  "multi_member_state_establishment",
                  "destination_option",
                  "oss_union_registration",
                  "threshold_exceeded",
                  "below_threshold",
                  null
                ],
                "nullable": true,
                "description": "Ce qui a tranché le lieu. `oss_union_registration` : le vendeur détient une inscription ACTIVE au guichet unique UE. Pour un vendeur français, s'y inscrire est la manière d'exercer l'option de l'art. 59 quater §3 — le seuil n'a alors plus rien à trancher, et `threshold` reste `null` comme sur l'option explicite. Sourcé pour la France seulement : la manière d'exercer l'option est fixée par l'État membre où elle est exercée."
              },
              "reference": {
                "type": "string",
                "description": "Référence normative du verdict."
              },
              "detail": {
                "type": "string",
                "description": "Justification 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."
              },
              "threshold": {
                "type": "object",
                "nullable": true,
                "description": "La tranche du REGISTRE annuel que cette opération a occupée. Le total courant n'est pas une photographie posée sur le profil fiscal : c'est un registre transactionnel par société, par mode et par année, et la décision fige la tranche qu'elle y a prise. Sous un prix TTC la contribution est un INTERVALLE — la valeur HT dépend du taux que le seuil doit justement décider — donc les deux bornes sont figées ; sous un prix HT elles coïncident.",
                "properties": {
                  "decidedOn": {
                    "type": "string",
                    "enum": [
                      "previous_year",
                      "ledger_cumulative"
                    ],
                    "description": "Quel fait a tranché. Sur `previous_year`, l'année précédente ferme la question à elle seule et le cumul courant est reporté sans rien décider."
                  },
                  "capCents": {
                    "type": "integer",
                    "description": "Plafond de l'art. 59 quater §1, en centimes entiers (`1 000 000`)."
                  },
                  "stateId": {
                    "type": "string",
                    "description": "Registre annuel d'où viennent les chiffres (`2026_live`, `2026_test`)."
                  },
                  "year": {
                    "type": "string"
                  },
                  "stateVersion": {
                    "type": "integer",
                    "description": "Version du registre au moment où la tranche a été prise."
                  },
                  "sequence": {
                    "type": "integer",
                    "description": "Position de l'opération dans l'ordre total des mouvements du registre."
                  },
                  "reservationId": {
                    "type": "string",
                    "description": "Réservation sous laquelle la tranche a été tenue — la clé d'idempotence durable de la décision."
                  },
                  "coverageMode": {
                    "type": "string",
                    "enum": [
                      "facturino_only",
                      "mixed_channels"
                    ],
                    "description": "Ce que Facturino voit des opérations couvertes : toutes, ou une partie complétée par des ajustements."
                  },
                  "previousYearAmountCents": {
                    "type": "integer"
                  },
                  "currentYearOpeningCents": {
                    "type": "integer"
                  },
                  "openingDeclaredAt": {
                    "type": "string",
                    "format": "date"
                  },
                  "externalCompleteThroughDate": {
                    "type": "string",
                    "format": "date",
                    "description": "Jour jusqu'auquel les canaux autres que Facturino sont déclarés complets."
                  },
                  "adjustmentTotalCents": {
                    "type": "integer"
                  },
                  "adjustmentCount": {
                    "type": "integer"
                  },
                  "cumulativeBeforeMinCents": {
                    "type": "integer",
                    "description": "Total certainement acquis avant l'opération : ouverture, ajustements, corrections et décisions déjà figées."
                  },
                  "cumulativeBeforeMaxCents": {
                    "type": "integer",
                    "description": "Le même total, augmenté des tranches tenues par les opérations décidées en même temps."
                  },
                  "pendingPredecessorCount": {
                    "type": "integer",
                    "description": "Nombre d'opérations décidées EN MÊME TEMPS dont la tranche est comptée dans la borne haute. L'écart entre les deux bornes EST cette concurrence : un verdict n'est figé que s'il tient aux deux, ce qui le rend insensible à l'abandon de l'une d'elles."
                  },
                  "operationValueMinCents": {
                    "type": "integer"
                  },
                  "operationValueMaxCents": {
                    "type": "integer"
                  },
                  "cumulativeAfterMinCents": {
                    "type": "integer"
                  },
                  "cumulativeAfterMaxCents": {
                    "type": "integer"
                  }
                }
              },
              "option": {
                "type": "object",
                "nullable": true,
                "description": "Option pour la taxation à destination, lorsque c'est elle qui a tranché.",
                "properties": {
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  }
                }
              },
              "mechanism": {
                "type": "object",
                "nullable": true,
                "description": "Comment la taxe due à destination est déclarée. Jamais une règle de lieu. L'inscription est DATÉE : un guichet ouvert en octobre ne déclare pas une vente de septembre.",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "oss_union",
                      "local_registration"
                    ]
                  },
                  "memberState": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "memberStateOfIdentification": {
                    "type": "string",
                    "nullable": true,
                    "description": "État membre d'identification du guichet unique ; `null` pour une immatriculation locale."
                  },
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  }
                }
              },
              "rate": {
                "type": "object",
                "nullable": true,
                "description": "Entrée de taux appliquée, avec sa provenance : un contrôle rejoue la décision sans relire le registre.",
                "properties": {
                  "registryVersion": {
                    "type": "string"
                  },
                  "memberState": {
                    "type": "string"
                  },
                  "territoryId": {
                    "type": "string"
                  },
                  "regionId": {
                    "type": "string",
                    "nullable": true
                  },
                  "centipercent": {
                    "type": "integer"
                  },
                  "validFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "validTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "source": {
                    "type": "string"
                  },
                  "verifiedAt": {
                    "type": "string",
                    "format": "date"
                  }
                }
              },
              "evidenceRelief": {
                "type": "object",
                "nullable": true,
                "description": "Comment l'allègement de preuves de localisation a été tranché, avec les chiffres sur lesquels il repose (règlement d'exécution UE 282/2011 art. 24 ter, 2e alinéa). Il est CALCULÉ par le moteur sur le compteur de 100 000 € du registre annuel — celui des services fournis par voie électronique, jamais celui de 10 000 € qui compte aussi les ventes à distance de biens. `undeterminable` est une réponse à part entière : deux preuves sont alors exigées, et l'anomalie dit quel fait manque.",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "available",
                      "unavailable",
                      "undeterminable"
                    ]
                  },
                  "capCents": {
                    "type": "integer",
                    "description": "Plafond de l'art. 24 ter, 2e alinéa (`10 000 000`)."
                  },
                  "stateId": {
                    "type": "string",
                    "nullable": true
                  },
                  "year": {
                    "type": "string",
                    "nullable": true
                  },
                  "previousYearAmountCents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "cumulativeAfterMinCents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "cumulativeAfterMaxCents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "undeterminedCode": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "ledger_not_consulted",
                      "ledger_unavailable",
                      "amount_interval_straddles_cap",
                      null
                    ]
                  }
                }
              }
            }
          },
          "foreignTaxReviewRequired": {
            "type": "boolean",
            "description": "Une fiscalité étrangère peut s'appliquer. Facturino traite la TVA française et les obligations françaises correspondantes ; ce cas doit être vérifié hors Facturino."
          },
          "vies": {
            "$ref": "#/components/schemas/ViesResult"
          },
          "locationEvidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LocationEvidenceResult"
            }
          },
          "nonEuBusinessEvidence": {
            "allOf": [
              {
                "$ref": "#/components/schemas/NonEuBusinessEvidenceResult"
              }
            ],
            "nullable": true
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "obligationReasons": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "axis": {
                  "type": "string",
                  "enum": [
                    "invoiceChannel",
                    "transactionReporting",
                    "paymentReporting"
                  ]
                },
                "code": {
                  "type": "string"
                },
                "reference": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "retryOfTaxDecisionId": {
            "type": "string",
            "nullable": true,
            "description": "Décision que celle-ci reprend après apport des éléments manquants."
          },
          "livemode": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TaxDecisionLine": {
        "type": "object",
        "properties": {
          "reference": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "category": {
            "$ref": "#/components/schemas/SupplyCategory"
          },
          "relatedCategory": {
            "type": "string",
            "nullable": true,
            "enum": [
              "goods",
              "services",
              "electronically_supplied_services",
              null
            ]
          },
          "effectiveCategory": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "string"
          },
          "unitAmount": {
            "type": "integer"
          },
          "discount": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxDecisionDiscount"
              }
            ],
            "nullable": true
          },
          "rateCategory": {
            "$ref": "#/components/schemas/RateCategory"
          },
          "placeOfSupplyRule": {
            "type": "string"
          },
          "goodsMovement": {
            "type": "string",
            "nullable": true,
            "enum": [
              "stays_in_seller_territory",
              "dispatched_to_buyer_territory",
              "unknown",
              null
            ]
          },
          "treatment": {
            "type": "string",
            "nullable": true
          },
          "vatCategoryCode": {
            "type": "string",
            "nullable": true
          },
          "vatexCode": {
            "type": "string",
            "nullable": true
          },
          "legalMention": {
            "type": "string",
            "nullable": true
          },
          "rateCentipercent": {
            "type": "integer",
            "nullable": true
          },
          "rateBasis": {
            "type": "string",
            "nullable": true
          },
          "placeOfSupply": {
            "type": "string",
            "nullable": true
          },
          "placeOfSupplyReference": {
            "type": "string",
            "nullable": true
          },
          "treatmentReference": {
            "type": "string",
            "nullable": true
          },
          "invoiceChannel": {
            "type": "string",
            "nullable": true,
            "enum": [
              "einvoicing",
              "none",
              null
            ]
          },
          "transactionReporting": {
            "type": "string",
            "nullable": true,
            "enum": [
              "ereporting",
              "none",
              "outside_scope",
              null
            ]
          },
          "paymentReporting": {
            "type": "string",
            "nullable": true,
            "enum": [
              "fr212",
              "ereporting",
              "none",
              null
            ]
          },
          "amountHT": {
            "type": "integer"
          },
          "amountVAT": {
            "type": "integer"
          },
          "amountTTC": {
            "type": "integer"
          }
        }
      },
      "CreateTaxDecisionInput": {
        "description": "Les deux parcours fiscaux du contrat stable, en union discriminée par `taxSource`. `taxSource` est requis : une décision énonce d'où vient sa TVA.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/FacturinoTaxDecisionInput"
          },
          {
            "$ref": "#/components/schemas/IntegrationTaxDecisionInput"
          }
        ],
        "discriminator": {
          "propertyName": "taxSource",
          "mapping": {
            "facturino": "#/components/schemas/FacturinoTaxDecisionInput",
            "integration": "#/components/schemas/IntegrationTaxDecisionInput"
          }
        }
      },
      "SellerTaxProfile": {
        "type": "object",
        "description": "Révision datée du profil fiscal vendeur utilisée par la décision. NOTE DE CONTRAT — la publication initiale du 2026-09-01 portait sur ce schéma deux champs, `oss` et `locationEvidenceRelief`, remplacés par `euB2cDestination`. `oss` ne portait qu'un mécanisme déclaratif isolé, sans l'option de lieu ni les immatriculations locales qui décident avec lui ; `locationEvidenceRelief` était une case à cocher là où l'allègement de l'art. 24 ter est un CHIFFRE, calculé par le moteur sur le compteur de 100 000 € du registre annuel `/v1/eu-threshold-ledgers`. Cette correction est antérieure à tout consommateur externe : la date de contrat `2026-09-01` est conservée, et aucun intégrateur n'a jamais reçu les deux champs retirés.",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "enum": [
              "seller_tax_profile"
            ]
          },
          "companyId": {
            "type": "string"
          },
          "livemode": {
            "type": "boolean"
          },
          "revision": {
            "type": "integer"
          },
          "establishment": {
            "type": "object",
            "properties": {
              "country": {
                "type": "string"
              },
              "postalCode": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "additionalEstablishments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string"
                },
                "postalCode": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "vatRegime": {
            "type": "string",
            "enum": [
              "normal_monthly",
              "normal_quarterly",
              "simplified",
              "franchise"
            ]
          },
          "franchiseEnBase": {
            "type": "boolean"
          },
          "vatOnDebits": {
            "type": "boolean"
          },
          "euB2cDestination": {
            "type": "object",
            "nullable": true,
            "description": "Faits STABLES déclarés de la règle B2C UE à destination (directive 2006/112/CE art. 59 quater) : option pour la taxation à destination, inscriptions au guichet unique et immatriculations TVA locales, toutes DATÉES. `null` signifie que le vendeur n'a rien déclaré. Les trois vivent ensemble : elles répondent à trois questions sur les mêmes opérations, et les séparer laissait exister une option sans guichet pour la déclarer. Le total courant des seuils n'est PAS ici — il vit dans le registre annuel `/v1/eu-threshold-ledgers`, transactionnel et append-only. L'allègement de preuves de l'art. 24 ter n'y est pas non plus : c'est un CHIFFRE, calculé par le moteur sur le compteur de 100 000 € de ce registre, jamais une case à cocher.",
            "properties": {
              "destinationOption": {
                "type": "object",
                "nullable": true,
                "description": "Option pour la taxation dans l'État membre de consommation. Elle engage l'année d'exercice et les deux années civiles complètes suivantes.",
                "properties": {
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "declaredAt": {
                    "type": "string",
                    "format": "date"
                  }
                }
              },
              "ossRegistrations": {
                "type": "array",
                "description": "Inscriptions au guichet unique, DATÉES. Le régime `union` est celui des art. 369 bis à 369 duodecies ; `non_union` (art. 358 bis) n'est modélisé que pour REFUSER explicitement sa déclaration par un vendeur établi dans l'Union. Le régime d'IMPORTATION (IOSS) est absent : aucune règle de ce moteur ne le lit, et stocker une inscription que rien ne consomme présenterait une case Facturino comme une formalité fiscale.",
                "items": {
                  "type": "object",
                  "properties": {
                    "scheme": {
                      "type": "string",
                      "enum": [
                        "union",
                        "non_union"
                      ]
                    },
                    "memberStateOfIdentification": {
                      "type": "string",
                      "description": "État membre d'identification, ISO 3166-1 alpha-2. Art. 369 bis : c'est celui du siège ou d'un établissement stable — une immatriculation locale n'est pas un établissement."
                    },
                    "reference": {
                      "type": "string",
                      "nullable": true
                    },
                    "effectiveFrom": {
                      "type": "string",
                      "format": "date"
                    },
                    "effectiveTo": {
                      "type": "string",
                      "format": "date",
                      "nullable": true
                    }
                  }
                }
              },
              "localVatRegistrations": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "memberState": {
                      "type": "string"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "effectiveFrom": {
                      "type": "string",
                      "format": "date"
                    },
                    "effectiveTo": {
                      "type": "string",
                      "format": "date",
                      "nullable": true
                    }
                  }
                }
              },
              "declaredAt": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "sizeCategory": {
            "type": "string",
            "enum": [
              "micro",
              "sme",
              "mid_cap",
              "large"
            ]
          },
          "voluntaryEarlyAdoption": {
            "type": "boolean"
          },
          "supportedSupplyCategories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupplyCategory"
            }
          },
          "priceMode": {
            "$ref": "#/components/schemas/PriceMode"
          },
          "rulesVersion": {
            "type": "string"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date"
          },
          "effectiveTo": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LocationEvidenceResult": {
        "type": "object",
        "description": "Preuve territoriale normalisée conservée avec la décision ; aucun signal brut n'est exposé.",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "billing_address",
              "ip_geolocation",
              "bank_details",
              "sim_mobile_country",
              "fixed_line",
              "other_commercial"
            ]
          },
          "territoryId": {
            "type": "string",
            "nullable": true,
            "description": "Territoire canonique désigné par la preuve, ou null lorsqu'elle n'est résolue qu'au niveau du pays (ip_geolocation, bank_details, sim_mobile_country, fixed_line transmis sans code postal)."
          },
          "declaredCountry": {
            "type": "string"
          },
          "declaredPostalCode": {
            "type": "string",
            "nullable": true
          },
          "thirdParty": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "psp",
              "network",
              "bank",
              "declared",
              "other"
            ]
          },
          "collectedAt": {
            "type": "string",
            "format": "date"
          },
          "reference": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "NonEuBusinessEvidenceResult": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "tax_authority_certificate",
              "vat_or_similar_number",
              "other_commercial_evidence"
            ]
          },
          "reference": {
            "type": "string"
          },
          "issuedByTerritoryId": {
            "type": "string"
          },
          "reasonableVerificationPerformed": {
            "type": "boolean"
          },
          "collectedAt": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "TaxSnapshot": {
        "type": "object",
        "description": "Position fiscale FIGÉE d'un document, autonome : elle justifie sa TVA sans relire la décision, le profil vendeur ni le jeu de règles. Écrite une fois, jamais mise à jour.",
        "properties": {
          "taxDecisionId": {
            "type": "string"
          },
          "taxSource": {
            "$ref": "#/components/schemas/TaxSource"
          },
          "decidedAt": {
            "type": "string",
            "format": "date-time"
          },
          "effectiveAt": {
            "type": "string",
            "format": "date"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "rulesVersion": {
            "type": "string"
          },
          "reportingCalendar": {
            "type": "string",
            "nullable": true
          },
          "roundingPolicy": {
            "type": "string"
          },
          "operationFingerprint": {
            "type": "string"
          },
          "priceMode": {
            "$ref": "#/components/schemas/PriceMode"
          },
          "currency": {
            "type": "string"
          },
          "seller": {
            "type": "object",
            "properties": {
              "sellerProfileId": {
                "type": "string"
              },
              "sellerProfileRevision": {
                "type": "integer"
              },
              "establishmentTerritoryId": {
                "type": "string"
              },
              "vatRegime": {
                "type": "string"
              },
              "franchiseEnBase": {
                "type": "boolean"
              },
              "vatOnDebits": {
                "type": "boolean"
              },
              "sizeCategory": {
                "type": "string"
              },
              "voluntaryEarlyAdoption": {
                "type": "boolean"
              }
            }
          },
          "buyer": {
            "type": "object",
            "properties": {
              "customerId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "nature": {
                "type": "string",
                "enum": [
                  "business",
                  "consumer"
                ]
              },
              "natureBasis": {
                "type": "string"
              },
              "territoryId": {
                "type": "string"
              },
              "territoryKind": {
                "type": "string"
              },
              "declaredCountry": {
                "type": "string"
              },
              "declaredPostalCode": {
                "type": "string",
                "nullable": true
              },
              "legalRegistrationId": {
                "type": "string",
                "nullable": true
              },
              "vatNumber": {
                "type": "string",
                "nullable": true
              },
              "crossBorderTaxableStatus": {
                "type": "string"
              },
              "businessStatusBasis": {
                "type": "string"
              },
              "viesStatus": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "valid",
                  "invalid",
                  "unavailable",
                  "invalid_format",
                  null
                ]
              },
              "viesCheckedAt": {
                "type": "string",
                "format": "date",
                "nullable": true
              }
            }
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "lineRef": {
                  "type": "string"
                },
                "documentLineId": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "category": {
                  "$ref": "#/components/schemas/SupplyCategory"
                },
                "effectiveCategory": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "goods",
                    "services",
                    "electronically_supplied_services",
                    null
                  ]
                },
                "quantity": {
                  "type": "string"
                },
                "unitAmountCents": {
                  "type": "integer"
                },
                "discount": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/TaxDecisionDiscount"
                    }
                  ],
                  "nullable": true
                },
                "rateCategory": {
                  "$ref": "#/components/schemas/RateCategory"
                },
                "treatment": {
                  "type": "string",
                  "nullable": true
                },
                "vatCategoryCode": {
                  "type": "string",
                  "nullable": true
                },
                "vatexCode": {
                  "type": "string",
                  "nullable": true
                },
                "legalMention": {
                  "type": "string",
                  "nullable": true
                },
                "rateCentipercent": {
                  "type": "integer",
                  "nullable": true
                },
                "rateBasis": {
                  "type": "string",
                  "nullable": true
                },
                "placeOfSupply": {
                  "type": "string",
                  "nullable": true
                },
                "amountHTCents": {
                  "type": "integer"
                },
                "amountVATCents": {
                  "type": "integer"
                },
                "amountTTCCents": {
                  "type": "integer"
                },
                "invoiceChannel": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "einvoicing",
                    "none",
                    null
                  ]
                },
                "transactionReporting": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "ereporting",
                    "none",
                    "outside_scope",
                    null
                  ]
                },
                "paymentReporting": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "fr212",
                    "ereporting",
                    "none",
                    null
                  ]
                }
              }
            }
          },
          "invoiceChannel": {
            "type": "string",
            "enum": [
              "einvoicing",
              "none"
            ],
            "nullable": true
          },
          "transactionReporting": {
            "type": "string",
            "enum": [
              "ereporting",
              "none",
              "outside_scope"
            ],
            "nullable": true
          },
          "paymentReporting": {
            "type": "string",
            "enum": [
              "fr212",
              "ereporting",
              "none"
            ],
            "nullable": true
          },
          "foreignTaxReviewRequired": {
            "type": "boolean"
          },
          "euB2cDestination": {
            "type": "object",
            "nullable": true,
            "description": "Ce que la règle B2C UE à destination a conclu, FIGÉ comme donnée. `null` — et absent sur un document écrit avant l'existence de ce champ — pour toute opération que la règle n'atteint pas. Présent dès qu'elle couvre une ligne, y compris sur une décision NON finale : il dit alors exactement ce qui est tranché et ce qui manque. Une chaîne `reference` ne s'audite pas : le verdict, les chiffres du seuil, le mécanisme déclaratif et l'entrée de taux — version, source, date de vérification, période et région — sont donc lisibles séparément.",
            "properties": {
              "coveredLineIds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Références des lignes que la règle couvre."
              },
              "ruleKinds": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "tbe_services",
                    "intra_eu_distance_sale"
                  ]
                },
                "description": "Familles concernées : service fourni par voie électronique (art. 58) et vente à distance intracommunautaire (art. 33, a)."
              },
              "destinationMemberState": {
                "type": "string",
                "description": "État membre du consommateur, ISO 3166-1 alpha-2."
              },
              "destinationTerritoryId": {
                "type": "string",
                "description": "Territoire canonique du consommateur, région comprise lorsque l'État en publie une (ex. `PT-MA`)."
              },
              "place": {
                "type": "string",
                "enum": [
                  "origin",
                  "destination",
                  null
                ],
                "nullable": true,
                "description": "Lieu d'imposition des lignes couvertes. `null` tant que la règle ne l'a pas tranché."
              },
              "basis": {
                "type": "string",
                "enum": [
                  "multi_member_state_establishment",
                  "destination_option",
                  "oss_union_registration",
                  "threshold_exceeded",
                  "below_threshold",
                  null
                ],
                "nullable": true,
                "description": "Ce qui a tranché le lieu. `oss_union_registration` : le vendeur détient une inscription ACTIVE au guichet unique UE. Pour un vendeur français, s'y inscrire est la manière d'exercer l'option de l'art. 59 quater §3 — le seuil n'a alors plus rien à trancher, et `threshold` reste `null` comme sur l'option explicite. Sourcé pour la France seulement : la manière d'exercer l'option est fixée par l'État membre où elle est exercée."
              },
              "reference": {
                "type": "string",
                "description": "Référence normative du verdict."
              },
              "detail": {
                "type": "string",
                "description": "Justification 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."
              },
              "threshold": {
                "type": "object",
                "nullable": true,
                "description": "La tranche du REGISTRE annuel que cette opération a occupée. Le total courant n'est pas une photographie posée sur le profil fiscal : c'est un registre transactionnel par société, par mode et par année, et la décision fige la tranche qu'elle y a prise. Sous un prix TTC la contribution est un INTERVALLE — la valeur HT dépend du taux que le seuil doit justement décider — donc les deux bornes sont figées ; sous un prix HT elles coïncident.",
                "properties": {
                  "decidedOn": {
                    "type": "string",
                    "enum": [
                      "previous_year",
                      "ledger_cumulative"
                    ],
                    "description": "Quel fait a tranché. Sur `previous_year`, l'année précédente ferme la question à elle seule et le cumul courant est reporté sans rien décider."
                  },
                  "capCents": {
                    "type": "integer",
                    "description": "Plafond de l'art. 59 quater §1, en centimes entiers (`1 000 000`)."
                  },
                  "stateId": {
                    "type": "string",
                    "description": "Registre annuel d'où viennent les chiffres (`2026_live`, `2026_test`)."
                  },
                  "year": {
                    "type": "string"
                  },
                  "stateVersion": {
                    "type": "integer",
                    "description": "Version du registre au moment où la tranche a été prise."
                  },
                  "sequence": {
                    "type": "integer",
                    "description": "Position de l'opération dans l'ordre total des mouvements du registre."
                  },
                  "reservationId": {
                    "type": "string",
                    "description": "Réservation sous laquelle la tranche a été tenue — la clé d'idempotence durable de la décision."
                  },
                  "coverageMode": {
                    "type": "string",
                    "enum": [
                      "facturino_only",
                      "mixed_channels"
                    ],
                    "description": "Ce que Facturino voit des opérations couvertes : toutes, ou une partie complétée par des ajustements."
                  },
                  "previousYearAmountCents": {
                    "type": "integer"
                  },
                  "currentYearOpeningCents": {
                    "type": "integer"
                  },
                  "openingDeclaredAt": {
                    "type": "string",
                    "format": "date"
                  },
                  "externalCompleteThroughDate": {
                    "type": "string",
                    "format": "date",
                    "description": "Jour jusqu'auquel les canaux autres que Facturino sont déclarés complets."
                  },
                  "adjustmentTotalCents": {
                    "type": "integer"
                  },
                  "adjustmentCount": {
                    "type": "integer"
                  },
                  "cumulativeBeforeMinCents": {
                    "type": "integer",
                    "description": "Total certainement acquis avant l'opération : ouverture, ajustements, corrections et décisions déjà figées."
                  },
                  "cumulativeBeforeMaxCents": {
                    "type": "integer",
                    "description": "Le même total, augmenté des tranches tenues par les opérations décidées en même temps."
                  },
                  "pendingPredecessorCount": {
                    "type": "integer",
                    "description": "Nombre d'opérations décidées EN MÊME TEMPS dont la tranche est comptée dans la borne haute. L'écart entre les deux bornes EST cette concurrence : un verdict n'est figé que s'il tient aux deux, ce qui le rend insensible à l'abandon de l'une d'elles."
                  },
                  "operationValueMinCents": {
                    "type": "integer"
                  },
                  "operationValueMaxCents": {
                    "type": "integer"
                  },
                  "cumulativeAfterMinCents": {
                    "type": "integer"
                  },
                  "cumulativeAfterMaxCents": {
                    "type": "integer"
                  }
                }
              },
              "option": {
                "type": "object",
                "nullable": true,
                "description": "Option pour la taxation à destination, lorsque c'est elle qui a tranché.",
                "properties": {
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  }
                }
              },
              "mechanism": {
                "type": "object",
                "nullable": true,
                "description": "Comment la taxe due à destination est déclarée. Jamais une règle de lieu. L'inscription est DATÉE : un guichet ouvert en octobre ne déclare pas une vente de septembre.",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "oss_union",
                      "local_registration"
                    ]
                  },
                  "memberState": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "memberStateOfIdentification": {
                    "type": "string",
                    "nullable": true,
                    "description": "État membre d'identification du guichet unique ; `null` pour une immatriculation locale."
                  },
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  }
                }
              },
              "rate": {
                "type": "object",
                "nullable": true,
                "description": "Entrée de taux appliquée, avec sa provenance : un contrôle rejoue la décision sans relire le registre.",
                "properties": {
                  "registryVersion": {
                    "type": "string"
                  },
                  "memberState": {
                    "type": "string"
                  },
                  "territoryId": {
                    "type": "string"
                  },
                  "regionId": {
                    "type": "string",
                    "nullable": true
                  },
                  "centipercent": {
                    "type": "integer"
                  },
                  "validFrom": {
                    "type": "string",
                    "format": "date"
                  },
                  "validTo": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "source": {
                    "type": "string"
                  },
                  "verifiedAt": {
                    "type": "string",
                    "format": "date"
                  }
                }
              },
              "evidenceRelief": {
                "type": "object",
                "nullable": true,
                "description": "Comment l'allègement de preuves de localisation a été tranché, avec les chiffres sur lesquels il repose (règlement d'exécution UE 282/2011 art. 24 ter, 2e alinéa). Il est CALCULÉ par le moteur sur le compteur de 100 000 € du registre annuel — celui des services fournis par voie électronique, jamais celui de 10 000 € qui compte aussi les ventes à distance de biens. `undeterminable` est une réponse à part entière : deux preuves sont alors exigées, et l'anomalie dit quel fait manque.",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "available",
                      "unavailable",
                      "undeterminable"
                    ]
                  },
                  "capCents": {
                    "type": "integer",
                    "description": "Plafond de l'art. 24 ter, 2e alinéa (`10 000 000`)."
                  },
                  "stateId": {
                    "type": "string",
                    "nullable": true
                  },
                  "year": {
                    "type": "string",
                    "nullable": true
                  },
                  "previousYearAmountCents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "cumulativeAfterMinCents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "cumulativeAfterMaxCents": {
                    "type": "integer",
                    "nullable": true
                  },
                  "undeterminedCode": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "ledger_not_consulted",
                      "ledger_unavailable",
                      "amount_interval_straddles_cap",
                      null
                    ]
                  }
                }
              }
            }
          },
          "totals": {
            "type": "object",
            "properties": {
              "totalHTCents": {
                "type": "integer"
              },
              "totalVATCents": {
                "type": "integer"
              },
              "totalTTCCents": {
                "type": "integer"
              }
            }
          },
          "vatBreakdown": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rateCentipercent": {
                  "type": "integer"
                },
                "categoryCode": {
                  "type": "string"
                },
                "vatexCode": {
                  "type": "string",
                  "nullable": true
                },
                "baseCents": {
                  "type": "integer"
                },
                "amountCents": {
                  "type": "integer"
                }
              }
            }
          },
          "amountToChargeCents": {
            "type": "integer"
          },
          "legalMentions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "TaxSource": {
        "type": "string",
        "enum": [
          "facturino",
          "integration"
        ],
        "description": "Source fiscale d'une décision et du document qu'elle adosse — les deux parcours du contrat stable, de même niveau. `facturino` : la TVA est déterminée par les moteurs fiscaux Facturino. `integration` : la TVA est fournie par l'intégration (`vatRate`, `vatCode`, `vatexCode` par ligne), validée en cohérence et jamais corrigée en silence. Dans les deux cas, les montants, les mentions légales et les trois axes d'obligation sont décidés côté serveur et figés. Un brouillon commercial non décidé porte `taxSource: null` — aucune conclusion fiscale n'existe encore."
      },
      "DocumentStatus": {
        "type": "string",
        "enum": [
          "draft",
          "finalized",
          "cancelled"
        ],
        "description": "Axe documentaire."
      },
      "TransmissionStatus": {
        "type": "string",
        "enum": [
          "not_applicable",
          "pending",
          "sending",
          "deposited",
          "transmitted",
          "approved",
          "rejected"
        ],
        "description": "Axe transmission. Un encaissement ne le modifie jamais."
      },
      "TransmissionDetail": {
        "type": "string",
        "nullable": true,
        "enum": [
          "available",
          "received",
          "suspended",
          "refused",
          null
        ],
        "description": "Précision DGFiP à l'intérieur de `transmitted` / `rejected`."
      },
      "PaymentStatus": {
        "type": "string",
        "enum": [
          "unpaid",
          "partially_paid",
          "paid",
          "partially_refunded",
          "refunded"
        ],
        "description": "Axe encaissement. Un remboursement n'efface pas l'encaissement qui a eu lieu."
      },
      "DecisionBackedLineInput": {
        "type": "object",
        "description": "Ligne de PRÉSENTATION d'un document adossé à une décision. La TVA vient de la décision : un `vatRate` ici serait une affirmation fiscale du client, et est refusé.",
        "required": [
          "taxLineRef",
          "unit"
        ],
        "properties": {
          "taxLineRef": {
            "type": "string",
            "maxLength": 64,
            "description": "Référence de la ligne décidée que cette ligne rend."
          },
          "unit": {
            "type": "string"
          },
          "product": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "DecidedCreditLineInput": {
        "type": "object",
        "description": "Ligne créditée d'une facture décidée. Taux, catégorie, code VATEX, mentions et unité sont HÉRITÉS. L'appelant indique quelle ligne et quelle part.",
        "required": [
          "taxLineRef"
        ],
        "properties": {
          "taxLineRef": {
            "type": "string",
            "maxLength": 64
          },
          "quantity": {
            "type": "string",
            "description": "Quantité créditée. Exclusive de `amountTTC`."
          },
          "amountTTC": {
            "type": "integer",
            "description": "Montant TTC crédité, en centimes entiers. Exclusif de `quantity`."
          }
        }
      },
      "FacturinoTaxDecisionInput": {
        "type": "object",
        "description": "Parcours `facturino` : le client décrit l'OPÉRATION (nature, catégorie de taux demandée, preuves territoriales) et ne décrit jamais la conclusion fiscale — taux, code, VATEX, mention, axe d'obligation et montant à encaisser sont décidés par les moteurs Facturino.",
        "required": [
          "taxSource",
          "customerId",
          "effectiveAt",
          "currency",
          "priceMode",
          "lines"
        ],
        "properties": {
          "taxSource": {
            "type": "string",
            "enum": [
              "facturino"
            ],
            "description": "TVA déterminée par Facturino."
          },
          "customerId": {
            "type": "string",
            "description": "Préfixe `cus_`"
          },
          "effectiveAt": {
            "type": "string",
            "format": "date",
            "description": "Date civile existante (`YYYY-MM-DD`). Un horodatage est refusé : la conversion en date civile est une décision de fuseau qui appartient à l'appelant."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "`eur` uniquement dans ce jeu de règles. Toute autre devise est refusée, jamais convertie."
          },
          "priceMode": {
            "$ref": "#/components/schemas/PriceMode"
          },
          "locationEvidence": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/LocationEvidenceInput"
            }
          },
          "nonEuBusinessEvidence": {
            "type": "object",
            "description": "Preuve de qualité d'assujetti hors UE (282/2011 art. 18-3).",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "tax_authority_certificate",
                  "vat_or_similar_number",
                  "other_commercial_evidence"
                ]
              },
              "reference": {
                "type": "string",
                "maxLength": 100
              },
              "issuedByCountry": {
                "type": "string",
                "minLength": 2,
                "maxLength": 2
              },
              "issuedByPostalCode": {
                "type": "string",
                "maxLength": 16
              },
              "reasonableVerificationPerformed": {
                "type": "boolean"
              },
              "collectedAt": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "retryOfTaxDecisionId": {
            "type": "string",
            "description": "Décision précédente que celle-ci reprend."
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 250,
            "items": {
              "$ref": "#/components/schemas/TaxDecisionLineInput"
            }
          }
        }
      },
      "IntegrationTaxDecisionInput": {
        "type": "object",
        "description": "Parcours `integration` : les mêmes données commerciales et territoriales que le parcours `facturino`, plus la TVA conclue par le moteur de l'intégration. Facturino valide la cohérence des valeurs fournies, refuse explicitement toute contradiction, et décide lui-même les montants, les mentions légales et les trois axes d'obligation.",
        "required": [
          "taxSource",
          "customerId",
          "effectiveAt",
          "currency",
          "priceMode",
          "lines"
        ],
        "properties": {
          "taxSource": {
            "type": "string",
            "enum": [
              "integration"
            ],
            "description": "TVA fournie par l’intégration, validée en cohérence."
          },
          "customerId": {
            "type": "string",
            "description": "Préfixe `cus_`"
          },
          "effectiveAt": {
            "type": "string",
            "format": "date",
            "description": "Date civile existante (`YYYY-MM-DD`). Un horodatage est refusé : la conversion en date civile est une décision de fuseau qui appartient à l'appelant."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "`eur` uniquement dans ce jeu de règles. Toute autre devise est refusée, jamais convertie."
          },
          "priceMode": {
            "$ref": "#/components/schemas/PriceMode"
          },
          "locationEvidence": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/LocationEvidenceInput"
            }
          },
          "nonEuBusinessEvidence": {
            "type": "object",
            "description": "Preuve de qualité d'assujetti hors UE (282/2011 art. 18-3).",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "tax_authority_certificate",
                  "vat_or_similar_number",
                  "other_commercial_evidence"
                ]
              },
              "reference": {
                "type": "string",
                "maxLength": 100
              },
              "issuedByCountry": {
                "type": "string",
                "minLength": 2,
                "maxLength": 2
              },
              "issuedByPostalCode": {
                "type": "string",
                "maxLength": 16
              },
              "reasonableVerificationPerformed": {
                "type": "boolean"
              },
              "collectedAt": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "retryOfTaxDecisionId": {
            "type": "string",
            "description": "Décision précédente que celle-ci reprend."
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 250,
            "items": {
              "$ref": "#/components/schemas/IntegrationTaxDecisionLineInput"
            }
          }
        }
      },
      "IntegrationTaxDecisionLineInput": {
        "type": "object",
        "description": "Ligne d'une décision `integration` : les mêmes données commerciales, plus la TVA conclue par le moteur de l'intégration. Facturino valide la cohérence taux/code/VATEX et refuse toute contradiction détectable (`integration_vat_incoherent`) ; il ne corrige jamais un taux en silence.",
        "required": [
          "reference",
          "description",
          "category",
          "unitAmount",
          "quantity",
          "vatRate",
          "vatCode"
        ],
        "properties": {
          "reference": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Référence stable côté appelant, réémise sur la ligne décidée."
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "category": {
            "$ref": "#/components/schemas/SupplyCategory"
          },
          "relatedCategory": {
            "type": "string",
            "enum": [
              "goods",
              "services",
              "electronically_supplied_services"
            ],
            "description": "Nature sous-jacente d’un acompte ou de frais accessoires."
          },
          "goodsMovement": {
            "type": "string",
            "enum": [
              "stays_in_seller_territory",
              "dispatched_to_buyer_territory",
              "unknown"
            ],
            "description": "Mouvement physique des biens. **Obligatoire** lorsque l'acheteur est un particulier établi dans un autre État membre : ce mouvement décide si la vente à distance intracommunautaire s'applique (directive 2006/112/CE art. 33, a), et il n'est jamais supposé — sinon `422 integration_vat_incoherent`. Sans objet sur une prestation de services."
          },
          "unitAmount": {
            "type": "integer",
            "description": "Prix unitaire en centimes, dans le mode de prix du profil vendeur."
          },
          "quantity": {
            "type": "string",
            "description": "Quantité décimale canonique, transportée en chaîne (jamais un flottant)."
          },
          "discount": {
            "$ref": "#/components/schemas/TaxDecisionDiscount"
          },
          "vatRate": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "description": "Taux de TVA fourni, en centièmes de pourcent (2000 = 20,00 %). Jamais corrigé."
          },
          "vatCode": {
            "type": "string",
            "enum": [
              "S",
              "Z",
              "E",
              "AE",
              "K",
              "G",
              "O"
            ],
            "description": "Code de catégorie TVA EN16931 (BT-151) fourni."
          },
          "vatexCode": {
            "type": "string",
            "pattern": "^VATEX-[A-Z]{2}-[A-Z0-9-]{1,24}$",
            "description": "Code VATEX (BT-121). Requis pour les catégories E, AE, K, G, O ; refusé pour S et Z ; contrôlé en admissibilité avec la catégorie fournie."
          },
          "placeOfSupply": {
            "type": "string",
            "minLength": 2,
            "maxLength": 8,
            "description": "Lieu d'imposition déclaré (identifiant de territoire canonique, ex. `FR-MET`, `DE`). Fait territorial qui alimente les axes d'obligation, jamais un calcul de TVA. **Obligatoire** lorsque l'acheteur est établi dans une collectivité d'outre-mer ou aux TAAF (PM, BL, MF, PF, NC, WF, TF) : c'est le lieu de l'opération qui dit si la taxe indirecte de la collectivité est en jeu, et il n'est jamais supposé — sinon `422 integration_vat_incoherent`. Un lieu situé dans l'une de ces sept rend la décision NON FINALE, sauf le cas sourcé de la Nouvelle-Calédonie en B2B (art. Lp. 507-1) : ce contrat n'a ni juridiction ni schéma de taxe locale, et `vatRate`/`vatCode`/`vatexCode` ne décrivent que la TVA française."
          }
        }
      },
      "BindTaxDecisionInput": {
        "type": "object",
        "required": [
          "taxDecisionId",
          "decisionLines"
        ],
        "additionalProperties": false,
        "properties": {
          "taxDecisionId": {
            "type": "string",
            "description": "Décision fiscale FINALE à adosser au brouillon commercial. Une décision finale adosse exactement UNE facture ; la rejouer sur la même facture renvoie la même facture.",
            "example": "taxdec_abc123"
          },
          "decisionLines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/DecisionBackedLineInput"
            },
            "description": "Présentation de chaque ligne décidée (unité, produit catalogue), une par ligne de la décision, appariée par `taxLineRef`. Le contenu fiscal d'une ligne (taux, catégorie, VATEX, montants) vient de la décision seule."
          }
        }
      },
      "EuThresholdCoverageMode": {
        "type": "string",
        "enum": [
          "facturino_only",
          "mixed_channels"
        ],
        "description": "Ce que Facturino voit des opérations couvertes. `facturino_only` est une DÉCLARATION du vendeur — toutes passent par Facturino — jamais une hypothèse ; `mixed_channels` dit que d'autres canaux existent, et leur chiffre entre par ajustement."
      },
      "EuThresholdLedger": {
        "type": "object",
        "description": "Registre annuel des seuils B2C UE, par société, par mode (test/live) et par année. Il porte DEUX compteurs strictement séparés : celui du seuil commun de 10 000 € HT (art. 59 quater §1 — ventes à distance de biens ET services transfrontières à des particuliers) et celui de 100 000 € des preuves de localisation (règlement 282/2011 art. 24 ter, 2e alinéa — les seuls services fournis par voie électronique). Une vente à distance de biens alimente le premier et jamais le second. C'est sur lui que les seuils sont évalués : la décision y RÉSERVE la tranche de l'opération dans une transaction, puis la CONSOMME avec l'écriture de la décision. Les mouvements sont append-only ; une ouverture n'est jamais réécrite.",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "eu_threshold_ledger"
            ]
          },
          "id": {
            "type": "string",
            "description": "`{année}_{live|test}`."
          },
          "companyId": {
            "type": "string"
          },
          "livemode": {
            "type": "boolean"
          },
          "year": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "description": "Incrémentée à chaque écriture ; figée sur les décisions."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "review_required"
            ],
            "description": "`review_required` bloque toute nouvelle réservation : le total courant est reconnu faux, et une décision est figée sur les chiffres qu'elle lit. C'est la sortie honnête quand un montant doit sortir sans qu'une correction qualifiée puisse nommer le mouvement corrigé."
          },
          "review": {
            "type": "object",
            "nullable": true,
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "consumed_slice_missing",
                  "correction_not_qualifiable",
                  "declared_by_administrator"
                ]
              },
              "detail": {
                "type": "string"
              },
              "openedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "capCents": {
            "type": "integer",
            "description": "Plafond de l'art. 59 quater §1 (`1 000 000`)."
          },
          "evidenceCapCents": {
            "type": "integer",
            "description": "Plafond de l'art. 24 ter, 2e alinéa (`10 000 000`)."
          },
          "opening": {
            "type": "object",
            "description": "Déclaration d'ouverture de l'année. Jamais réécrite : des décisions y ont été figées.",
            "properties": {
              "previousYearAmount": {
                "type": "integer"
              },
              "currentYearOpening": {
                "type": "integer"
              },
              "previousYearEvidenceAmount": {
                "type": "integer",
                "description": "Total, sur la même période, des services fournis par voie électronique à des particuliers de l'Union, ventes en France comprises : le compteur du seuil de preuve de 100 000 € (règlement d'exécution (UE) 282/2011 art. 24 ter). INDÉPENDANT du total ci-dessus, dans les deux sens : il peut lui être supérieur."
              },
              "currentYearEvidenceOpening": {
                "type": "integer"
              },
              "coverageMode": {
                "$ref": "#/components/schemas/EuThresholdCoverageMode"
              },
              "externalCompleteThroughDate": {
                "type": "string",
                "format": "date"
              },
              "declaredAt": {
                "type": "string",
                "format": "date-time",
                "description": "INSTANT réel de la déclaration, jamais le jour qu'elle déclare complet."
              }
            }
          },
          "externalCompleteThroughDate": {
            "type": "string",
            "format": "date",
            "description": "Jour jusqu'auquel les canaux autres que Facturino sont complets, avancé par les ajustements."
          },
          "adjustmentTotal": {
            "type": "integer"
          },
          "adjustmentEvidenceTotal": {
            "type": "integer"
          },
          "adjustmentCount": {
            "type": "integer"
          },
          "correctionTotal": {
            "type": "integer",
            "description": "Total POSITIF des corrections qualifiées, soustrait du cumul."
          },
          "correctionEvidenceTotal": {
            "type": "integer"
          },
          "correctionCount": {
            "type": "integer"
          },
          "acquiredMin": {
            "type": "integer",
            "description": "Total déjà ACQUIS : ouverture + ajustements − corrections + décisions figées."
          },
          "acquiredMax": {
            "type": "integer"
          },
          "acquiredEvidenceMin": {
            "type": "integer"
          },
          "acquiredEvidenceMax": {
            "type": "integer"
          },
          "reservedMin": {
            "type": "integer",
            "description": "Tranches tenues MAINTENANT par des opérations en cours de décision. Elles ne sont pas acquises : publiées à part, jamais additionnées au total, parce qu'elles peuvent encore disparaître."
          },
          "reservedMax": {
            "type": "integer"
          },
          "reservedEvidenceMin": {
            "type": "integer"
          },
          "reservedEvidenceMax": {
            "type": "integer"
          },
          "remainingMin": {
            "type": "integer",
            "description": "Ce qui reste sous le plafond de 10 000 €, sur les chiffres acquis."
          },
          "evidenceRemainingMin": {
            "type": "integer",
            "description": "Ce qui reste sous le plafond de 100 000 €, sur les chiffres acquis."
          },
          "settledCount": {
            "type": "integer"
          },
          "lastConsumedEffectiveAt": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "reservations": {
            "type": "array",
            "description": "Tranches actuellement tenues par des décisions en cours.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "sequence": {
                  "type": "integer"
                },
                "effectiveAt": {
                  "type": "string",
                  "format": "date"
                },
                "contributionMin": {
                  "type": "integer"
                },
                "contributionMax": {
                  "type": "integer"
                },
                "evidenceContributionMin": {
                  "type": "integer"
                },
                "evidenceContributionMax": {
                  "type": "integer"
                },
                "reservedAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "expiresAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "entries": {
            "type": "array",
            "description": "Première page des mouvements, du plus récent au plus ancien. Append-only.",
            "items": {
              "$ref": "#/components/schemas/EuThresholdEntry"
            }
          },
          "entriesHasMore": {
            "type": "boolean"
          },
          "entriesNextCursor": {
            "type": "string",
            "nullable": true,
            "description": "Identifiant du dernier mouvement rendu — le curseur de la page suivante."
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OpenEuThresholdLedgerInput": {
        "type": "object",
        "required": [
          "year",
          "previousYearAmount",
          "currentYearOpening",
          "previousYearEvidenceAmount",
          "currentYearEvidenceOpening",
          "coverageMode",
          "externalCompleteThroughDate"
        ],
        "properties": {
          "year": {
            "type": "string",
            "pattern": "^\\d{4}$"
          },
          "previousYearAmount": {
            "type": "integer",
            "minimum": 0,
            "description": "Total HT des ventes couvertes de l'année précédente, en centimes."
          },
          "currentYearOpening": {
            "type": "integer",
            "minimum": 0,
            "description": "Total HT déjà réalisé cette année à l'ouverture du registre, en centimes."
          },
          "previousYearEvidenceAmount": {
            "type": "integer",
            "minimum": 0,
            "description": "Part de l'année précédente faite de services fournis par voie électronique — le périmètre du seuil de 100 000 € de l'art. 24 ter. INDÉPENDANTE du total ci-dessus, dans les deux sens : le seuil commun ne compte que les opérations TRANSFRONTIÈRES, celui des preuves compte aussi les opérations domestiques. Un vendeur qui sert surtout la France déclare donc légitimement plus ici que là."
          },
          "currentYearEvidenceOpening": {
            "type": "integer",
            "minimum": 0,
            "description": "Part correspondante du total déjà réalisé cette année. Indépendante de lui, pour la même raison."
          },
          "coverageMode": {
            "$ref": "#/components/schemas/EuThresholdCoverageMode"
          },
          "externalCompleteThroughDate": {
            "type": "string",
            "format": "date",
            "description": "Jour jusqu'auquel les autres canaux sont complets. Doit appartenir à l'année du registre et ne jamais être dans le futur : sinon `eu_threshold_date_outside_year` / `eu_threshold_date_in_future`."
          }
        }
      },
      "EuThresholdAdjustmentInput": {
        "type": "object",
        "required": [
          "reference",
          "amount",
          "evidenceAmount",
          "externalCompleteThroughDate",
          "reason"
        ],
        "properties": {
          "reference": {
            "type": "string",
            "maxLength": 64,
            "description": "Votre propre référence. Elle identifie l'écriture : un rejeu du MÊME corps n'ajoute rien, et la même référence portant un corps différent répond `eu_threshold_entry_conflict`. Elle n'est jamais un identifiant de document — elle est hachée, et conservée telle quelle comme donnée."
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "description": "Chiffre HT à ajouter, en centimes. JAMAIS négatif : rendre un montant est une CORRECTION qualifiée."
          },
          "evidenceAmount": {
            "type": "integer",
            "minimum": 0,
            "description": "Part de `amount` faite de services fournis par voie électronique. INDÉPENDANTE de `amount` : un service électronique domestique alimente ce compteur et pas l'autre."
          },
          "externalCompleteThroughDate": {
            "type": "string",
            "format": "date"
          },
          "reason": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "EuThresholdEntry": {
        "type": "object",
        "description": "Un mouvement du registre. Écrit une fois, jamais réécrit. Son identifiant est DÉRIVÉ du mouvement : la référence du client est une donnée, jamais un identifiant, sans quoi un texte libre atteindrait un chemin Firestore.",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "eu_threshold_entry"
            ]
          },
          "id": {
            "type": "string"
          },
          "sequence": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "opening",
              "external_adjustment",
              "external_correction",
              "reservation_consumed",
              "reservation_released",
              "review_opened",
              "review_resolved"
            ]
          },
          "amountMin": {
            "type": "integer",
            "description": "Effet SIGNÉ sur le total : négatif pour une correction."
          },
          "amountMax": {
            "type": "integer"
          },
          "evidenceAmountMin": {
            "type": "integer",
            "description": "Le même effet, dans le périmètre de l'art. 24 ter."
          },
          "evidenceAmountMax": {
            "type": "integer"
          },
          "cumulativeMin": {
            "type": "integer"
          },
          "cumulativeMax": {
            "type": "integer"
          },
          "evidenceCumulativeMin": {
            "type": "integer"
          },
          "evidenceCumulativeMax": {
            "type": "integer"
          },
          "taxDecisionId": {
            "type": "string",
            "nullable": true
          },
          "effectiveAt": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "reference": {
            "type": "string",
            "nullable": true,
            "description": "Votre propre référence, conservée telle quelle."
          },
          "correction": {
            "type": "object",
            "nullable": true,
            "description": "Qualification d'une correction et ressource sur laquelle elle repose.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "credit_note",
                  "cancellation",
                  "refund"
                ]
              },
              "correctsEntryId": {
                "type": "string"
              },
              "relatedResourceType": {
                "type": "string"
              },
              "relatedResourceId": {
                "type": "string"
              },
              "evidenceReference": {
                "type": "string"
              }
            }
          },
          "reason": {
            "type": "string"
          },
          "recordedAt": {
            "type": "string",
            "format": "date-time"
          },
          "correctable": {
            "type": "boolean",
            "description": "Ce mouvement a-t-il apporté du chiffre, donc quelque chose à rendre ?"
          },
          "correctedMin": {
            "type": "integer",
            "description": "Ce qu'il a DÉJÀ rendu, toutes corrections confondues."
          },
          "correctedEvidenceMin": {
            "type": "integer"
          },
          "correctionCount": {
            "type": "integer"
          },
          "remainingMin": {
            "type": "integer",
            "description": "Ce qu'il reste à rendre sur ce mouvement. Un mouvement rend ce qu'il a apporté, une fois, quel que soit le nombre de corrections : le solde est tenu DANS la transaction, et une correction qui le dépasse répond `eu_threshold_correction_exceeds_counted`."
          },
          "remainingEvidenceMin": {
            "type": "integer"
          }
        }
      },
      "EuThresholdCorrectionInput": {
        "type": "object",
        "description": "Rendre un montant déjà compté. Ce n'est PAS un ajustement négatif : la directive 2006/112/CE art. 90 §1 réduit la base d'imposition d'une livraison en cas d'annulation, de résiliation ou de réduction de prix postérieure, et les seuils comptent la VALEUR des opérations. La correction nomme donc le mouvement qu'elle corrige, sa qualification, la ressource sur laquelle elle repose et sa preuve, et elle ne rend jamais plus que ce que ce mouvement avait apporté. Ce qui ne peut pas être qualifié ainsi n'est pas soustrait : le registre passe en revue (`POST /review`) et cesse de décider. Le montant déjà rendu sur le mouvement corrigé est calculé DANS la transaction : deux corrections de la totalité ne peuvent pas passer toutes les deux.",
        "required": [
          "reference",
          "correctsEntryId",
          "kind",
          "amount",
          "evidenceAmount",
          "relatedResourceType",
          "relatedResourceId",
          "evidenceReference",
          "reason"
        ],
        "properties": {
          "reference": {
            "type": "string",
            "maxLength": 64
          },
          "correctsEntryId": {
            "type": "string",
            "pattern": "^(opening|(?:adj|cor|con|rel|rev)_[0-9a-f]{32})$",
            "description": "Mouvement de CE registre dont la base est réduite. Contraint aux identifiants que le registre frappe lui-même : il atteint un chemin de document, et du texte libre ne doit jamais y arriver. Inconnu : `eu_threshold_correction_target_unknown` ; sans chiffre d'affaires apporté (tranche rendue, revue, autre correction) : `eu_threshold_correction_target_not_correctable`."
          },
          "kind": {
            "type": "string",
            "enum": [
              "credit_note",
              "cancellation",
              "refund"
            ]
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "description": "Montant HT rendu, en centimes. Le registre tient le SOLDE de chaque mouvement : la somme des corrections d'un même mouvement ne dépasse jamais ce qu'il avait apporté, sinon `eu_threshold_correction_exceeds_counted`."
          },
          "evidenceAmount": {
            "type": "integer",
            "minimum": 0,
            "description": "Part « services » de ce qui est rendu ; indépendante de `amount`."
          },
          "relatedResourceType": {
            "type": "string",
            "maxLength": 64
          },
          "relatedResourceId": {
            "type": "string",
            "maxLength": 128
          },
          "evidenceReference": {
            "type": "string",
            "maxLength": 200
          },
          "reason": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "EuThresholdReviewInput": {
        "type": "object",
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "maxLength": 500
          }
        },
        "description": "Mise en revue du registre : une déclaration, donc un motif énoncé suffit."
      },
      "EuThresholdReviewResolutionInput": {
        "type": "object",
        "description": "Clôture d'une revue — par RÉCONCILIATION, jamais par commentaire. Une revue dit que le cumul est reconnu faux ; rouvrir le registre sur une note libre remettrait ce même cumul devant la prochaine décision avec une phrase pour seule garantie. L'administrateur énonce donc les chiffres qu'il a vérifiés, et le serveur les compare aux siens : `reconciledVersion` épingle l'état contrôlé (un mouvement survenu depuis rend la réconciliation obsolète, `eu_threshold_reconciliation_stale`), et les deux totaux acquis doivent CONCORDER (`eu_threshold_reconciliation_mismatch`, qui rend les deux chiffres). Ce qui a été vérifié est écrit dans le mouvement immuable `review_resolved`, avec sa référence de preuve.",
        "required": [
          "reconciledVersion",
          "reconciledAcquiredMin",
          "reconciledAcquiredEvidenceMin",
          "evidenceReference",
          "reason"
        ],
        "properties": {
          "reconciledVersion": {
            "type": "integer",
            "minimum": 1,
            "description": "Version du registre contre laquelle le rapprochement a été fait."
          },
          "reconciledAcquiredMin": {
            "type": "integer",
            "minimum": 0,
            "description": "Total acquis du compteur commun, tel que vérifié."
          },
          "reconciledAcquiredEvidenceMin": {
            "type": "integer",
            "minimum": 0,
            "description": "Total acquis du compteur art. 24 ter, tel que vérifié."
          },
          "evidenceReference": {
            "type": "string",
            "maxLength": 200,
            "description": "Où le rapprochement lui-même est classé."
          },
          "reason": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "ErrorIssue": {
        "type": "object",
        "required": [
          "code",
          "param",
          "message"
        ],
        "description": "Une raison détaillée d'un refus.",
        "properties": {
          "code": {
            "type": "string",
            "description": "Code stable, sur lequel un client peut brancher."
          },
          "param": {
            "type": "string",
            "nullable": true,
            "description": "Champ en cause (chemin pointé), ou `null` quand la raison n'en nomme aucun."
          },
          "message": {
            "type": "string",
            "description": "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."
          }
        }
      },
      "InvoiceWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "invoice"
            ],
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "previous_status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé précédent lors d’une transition."
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "number": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro du document ; null avant numérotation."
          },
          "documentStatus": {
            "$ref": "#/components/schemas/DocumentStatus",
            "readOnly": true
          },
          "transmissionStatus": {
            "$ref": "#/components/schemas/TransmissionStatus",
            "readOnly": true
          },
          "transmissionDetail": {
            "$ref": "#/components/schemas/TransmissionDetail",
            "readOnly": true
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus",
            "readOnly": true
          },
          "paErrorCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du dernier échec de dépôt : contrôle local ou réponse de la plateforme.",
            "readOnly": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true,
            "description": "Motif brut de la plateforme ou de l’acheteur, verbatim ; constat local pour la source facturino. Ne contient pas de code ou de note reconstruits. Null si absent.",
            "readOnly": true
          },
          "rejectionCategory": {
            "type": "string",
            "nullable": true,
            "enum": [
              "buyer_not_in_directory",
              "addressing_error",
              "other",
              "format_invalid",
              "semantic_error",
              "duplicate",
              "platform_auth",
              "platform_unavailable",
              "refused_by_buyer",
              "suspended",
              "unknown",
              null
            ],
            "description": "Lecture stable parmi onze catégories. Priorité : code local, code DGFiP, code HTTP ; seul le pré-contrôle annuaire Super PDP dispose d’un repli textuel. REJ_ADR = addressing_error, REJ_SEMAN = semantic_error, REJ_SYNT/REJ_XSD = format_invalid, REJ_DOUBLON = duplicate, AUTRE = other (explication dans rejectionNote). Le statut fr:213 seul ne classe pas en sémantique. Null au renvoi.",
            "readOnly": true
          },
          "rejectionCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du verdict enregistré ; null hors rejet, refus ou suspension, ou si aucun code n’a été enregistré.",
            "readOnly": true
          },
          "rejectionSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "buyer",
              "facturino",
              null
            ],
            "description": "Auteur du verdict enregistré ; null hors rejet, refus ou suspension, ou si la source manque sur un document ancien.",
            "readOnly": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "readOnly": true
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          }
        },
        "required": [
          "id",
          "object",
          "status"
        ],
        "readOnly": true
      },
      "CreditNoteWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "credit_note"
            ],
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "previous_status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé précédent lors d’une transition."
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "number": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro du document ; null avant numérotation."
          },
          "documentStatus": {
            "$ref": "#/components/schemas/DocumentStatus",
            "readOnly": true
          },
          "transmissionStatus": {
            "$ref": "#/components/schemas/TransmissionStatus",
            "readOnly": true
          },
          "transmissionDetail": {
            "$ref": "#/components/schemas/TransmissionDetail",
            "readOnly": true
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus",
            "readOnly": true
          },
          "paErrorCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du dernier échec de dépôt : contrôle local ou réponse de la plateforme.",
            "readOnly": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true,
            "description": "Motif brut de la plateforme ou de l’acheteur, verbatim ; constat local pour la source facturino. Ne contient pas de code ou de note reconstruits. Null si absent.",
            "readOnly": true
          },
          "rejectionCategory": {
            "type": "string",
            "nullable": true,
            "enum": [
              "buyer_not_in_directory",
              "addressing_error",
              "other",
              "format_invalid",
              "semantic_error",
              "duplicate",
              "platform_auth",
              "platform_unavailable",
              "refused_by_buyer",
              "suspended",
              "unknown",
              null
            ],
            "description": "Lecture stable parmi onze catégories. Priorité : code local, code DGFiP, code HTTP ; seul le pré-contrôle annuaire Super PDP dispose d’un repli textuel. REJ_ADR = addressing_error, REJ_SEMAN = semantic_error, REJ_SYNT/REJ_XSD = format_invalid, REJ_DOUBLON = duplicate, AUTRE = other (explication dans rejectionNote). Le statut fr:213 seul ne classe pas en sémantique. Null au renvoi.",
            "readOnly": true
          },
          "rejectionCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du verdict enregistré ; null hors rejet, refus ou suspension, ou si aucun code n’a été enregistré.",
            "readOnly": true
          },
          "rejectionSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "buyer",
              "facturino",
              null
            ],
            "description": "Auteur du verdict enregistré ; null hors rejet, refus ou suspension, ou si la source manque sur un document ancien.",
            "readOnly": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "readOnly": true
          },
          "relatedInvoiceId": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Identifiant de la facture liée, sur les événements d’avoir."
          },
          "relatedInvoiceNumber": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro de la facture liée, figé à la création de l’avoir ; null si absent sur un avoir ancien."
          },
          "paStatus": {
            "type": "string",
            "readOnly": true
          },
          "paInvoiceId": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          }
        },
        "required": [
          "id",
          "object",
          "status"
        ],
        "readOnly": true
      },
      "QuoteWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "quote"
            ],
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "previous_status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé précédent lors d’une transition."
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "number": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro du document ; null avant numérotation."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "readOnly": true
          }
        },
        "required": [
          "id",
          "object",
          "status"
        ],
        "readOnly": true
      },
      "CustomerWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "customer"
            ],
            "readOnly": true
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          }
        },
        "required": [
          "id",
          "object",
          "livemode"
        ],
        "readOnly": true
      },
      "PaymentReceivedWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "invoice"
            ],
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "previous_status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé précédent lors d’une transition."
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          },
          "number": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro du document ; null avant numérotation."
          },
          "documentStatus": {
            "$ref": "#/components/schemas/DocumentStatus",
            "readOnly": true
          },
          "transmissionStatus": {
            "$ref": "#/components/schemas/TransmissionStatus",
            "readOnly": true
          },
          "transmissionDetail": {
            "$ref": "#/components/schemas/TransmissionDetail",
            "readOnly": true
          },
          "paymentStatus": {
            "$ref": "#/components/schemas/PaymentStatus",
            "readOnly": true
          },
          "paErrorCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du dernier échec de dépôt : contrôle local ou réponse de la plateforme.",
            "readOnly": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true,
            "description": "Motif brut de la plateforme ou de l’acheteur, verbatim ; constat local pour la source facturino. Ne contient pas de code ou de note reconstruits. Null si absent.",
            "readOnly": true
          },
          "rejectionCategory": {
            "type": "string",
            "nullable": true,
            "enum": [
              "buyer_not_in_directory",
              "addressing_error",
              "other",
              "format_invalid",
              "semantic_error",
              "duplicate",
              "platform_auth",
              "platform_unavailable",
              "refused_by_buyer",
              "suspended",
              "unknown",
              null
            ],
            "description": "Lecture stable parmi onze catégories. Priorité : code local, code DGFiP, code HTTP ; seul le pré-contrôle annuaire Super PDP dispose d’un repli textuel. REJ_ADR = addressing_error, REJ_SEMAN = semantic_error, REJ_SYNT/REJ_XSD = format_invalid, REJ_DOUBLON = duplicate, AUTRE = other (explication dans rejectionNote). Le statut fr:213 seul ne classe pas en sémantique. Null au renvoi.",
            "readOnly": true
          },
          "rejectionCode": {
            "type": "string",
            "nullable": true,
            "description": "Code du verdict enregistré ; null hors rejet, refus ou suspension, ou si aucun code n’a été enregistré.",
            "readOnly": true
          },
          "rejectionSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "buyer",
              "facturino",
              null
            ],
            "description": "Auteur du verdict enregistré ; null hors rejet, refus ou suspension, ou si la source manque sur un document ancien.",
            "readOnly": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "readOnly": true
          },
          "amount": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : amount, en euros décimaux (chaîne)."
          },
          "total_paid": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : total_paid, en euros décimaux (chaîne)."
          },
          "total_due": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : total de la facture (nom historique), en euros décimaux."
          },
          "total": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : total, en euros décimaux (chaîne)."
          },
          "amountDue": {
            "type": "string",
            "readOnly": true,
            "description": "Solde restant de la facture, en euros décimaux (chaîne)."
          },
          "paymentId": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Identifiant du versement lié à cette révision du grand livre. Null si la variation ancienne ou agrégée ne permet pas une attribution certaine ; absent sur les anciens événements."
          },
          "fr212": {
            "type": "object",
            "nullable": true,
            "description": "Où en est le statut « Encaissée » (fr:212) de cet encaissement auprès de la plateforme. Présent quand la décision fiscale du document impose ce statut ; absent sinon. `pending` : en file ; `awaiting_deposit` : attend un dépôt accepté de la facture ; `sent` : transmis (`sentAt`) ; `blocked` : ne peut pas être transmis (`lastErrorCode`) ; `reconciliation_required` / `failed` : à rapprocher.",
            "required": [
              "state",
              "sentAt",
              "lastErrorCode",
              "updatedAt"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "pending",
                  "awaiting_deposit",
                  "sent",
                  "blocked",
                  "reconciliation_required",
                  "failed"
                ],
                "readOnly": true
              },
              "sentAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time",
                "readOnly": true
              },
              "lastErrorCode": {
                "type": "string",
                "nullable": true,
                "readOnly": true
              },
              "lastErrorReason": {
                "type": "string",
                "description": "Motif brut de la plateforme lorsque le statut est refusé définitivement (pa_lifecycle_rejected) ou attend un état encaissable (pa_lifecycle_not_ready). Absent si aucun motif n’est fourni.",
                "readOnly": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "readOnly": true
              },
              "obligation": {
                "$ref": "#/components/schemas/ObligationFollowUp"
              }
            },
            "readOnly": true
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          }
        },
        "required": [
          "id",
          "object",
          "amount",
          "total_paid",
          "total_due"
        ],
        "readOnly": true,
        "description": "Variation du total encaissé : id identifie la facture ; paymentId identifie le versement corrélé, ou vaut null faute de preuve sur une variation ancienne/agrégée. fr212 est son état de collecte au moment de la publication. Les montants de cet événement sont des chaînes en euros décimaux, alors que les réponses REST utilisent des centimes entiers. total_due conserve le total de la facture ; amountDue est le solde.",
        "example": {
          "id": "inv_00090",
          "object": "invoice",
          "number": "FAC2026-00090",
          "status": "paid",
          "previous_status": "rejected",
          "livemode": true,
          "documentStatus": "finalized",
          "transmissionStatus": "rejected",
          "transmissionDetail": null,
          "paymentStatus": "paid",
          "paErrorCode": null,
          "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
          "rejectionCategory": "addressing_error",
          "rejectionCode": "REJ_ADR",
          "rejectionSource": "platform",
          "metadata": {},
          "amount": "11.88",
          "total_paid": "11.88",
          "total_due": "11.88",
          "total": "11.88",
          "amountDue": "0.00",
          "paymentId": "pay_example",
          "fr212": {
            "state": "pending",
            "sentAt": null,
            "lastErrorCode": "pa_lifecycle_not_ready",
            "lastErrorReason": "Invoice is not in a cashable state",
            "updatedAt": "2026-09-10T07:51:00Z"
          }
        }
      },
      "PaymentCreatedWebhookData": {
        "type": "object",
        "properties": {
          "invoiceId": {
            "type": "string",
            "readOnly": true
          },
          "paymentId": {
            "type": "string",
            "readOnly": true
          },
          "amount": {
            "type": "string",
            "readOnly": true,
            "description": "payment.received : amount, en euros décimaux (chaîne)."
          },
          "method": {
            "type": "string",
            "readOnly": true
          }
        },
        "required": [
          "invoiceId",
          "paymentId",
          "amount",
          "method"
        ],
        "readOnly": true
      },
      "IncomingInvoiceWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "received_invoice"
            ],
            "readOnly": true
          },
          "pa_invoice_id": {
            "type": "string",
            "readOnly": true
          },
          "sender_siret": {
            "type": "string",
            "readOnly": true
          },
          "sender_name": {
            "type": "string",
            "readOnly": true
          },
          "number": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Numéro du document ; null avant numérotation."
          },
          "total_ht": {
            "type": "string",
            "readOnly": true
          },
          "total_tva": {
            "type": "string",
            "readOnly": true
          },
          "total_ttc": {
            "type": "string",
            "readOnly": true
          }
        },
        "required": [
          "id",
          "object",
          "pa_invoice_id"
        ],
        "readOnly": true
      },
      "RecurringGeneratedWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "invoice"
            ],
            "readOnly": true
          },
          "recurringInvoiceId": {
            "type": "string",
            "readOnly": true
          },
          "livemode": {
            "type": "boolean",
            "readOnly": true
          }
        },
        "required": [
          "id",
          "object",
          "recurringInvoiceId"
        ],
        "readOnly": true
      },
      "RecurringFailedWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "recurring_invoice"
            ],
            "readOnly": true
          },
          "error": {
            "type": "string",
            "readOnly": true
          }
        },
        "required": [
          "id",
          "object",
          "error"
        ],
        "readOnly": true
      },
      "EreportingWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "ereporting"
            ],
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "type": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "period": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "attempt": {
            "type": "integer",
            "readOnly": true
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          },
          "paRejectionCode": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Structured rejection code returned by the platform, preserved without deriving it from text."
          },
          "paRejectionReason": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Original platform rejection reason, verbatim."
          }
        },
        "required": [
          "id",
          "object",
          "status"
        ],
        "readOnly": true
      },
      "ExportWebhookData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "export"
            ],
            "readOnly": true
          },
          "count": {
            "type": "integer",
            "readOnly": true
          }
        },
        "required": [
          "id",
          "object",
          "count"
        ],
        "readOnly": true
      },
      "SubscriptionWebhookData": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true,
            "description": "Résumé au moment de l’événement."
          },
          "stripeSubscriptionId": {
            "type": "string",
            "readOnly": true
          },
          "pausedUntil": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "reason": {
            "type": "string",
            "readOnly": true
          }
        },
        "required": [
          "status"
        ],
        "readOnly": true
      },
      "InvoiceWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "invoice.created",
              "invoice.finalized",
              "invoice.sending",
              "invoice.sent",
              "invoice.deposited",
              "invoice.transmitted",
              "invoice.available",
              "invoice.received",
              "invoice.approved",
              "invoice.refused",
              "invoice.rejected",
              "invoice.suspended",
              "invoice.paid",
              "invoice.partially_paid",
              "invoice.overdue"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/InvoiceWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "id": "evt_invoice_rejected",
          "object": "event",
          "type": "invoice.rejected",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "data": {
            "id": "inv_00090",
            "object": "invoice",
            "number": "FAC2026-00090",
            "status": "rejected",
            "previous_status": "deposited",
            "livemode": true,
            "documentStatus": "finalized",
            "transmissionStatus": "rejected",
            "transmissionDetail": null,
            "paymentStatus": "unpaid",
            "paErrorCode": null,
            "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
            "rejectionCategory": "addressing_error",
            "rejectionCode": "REJ_ADR",
            "rejectionSource": "platform",
            "metadata": {}
          },
          "request": null
        }
      },
      "IncomingInvoiceWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "invoice.incoming.received"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/IncomingInvoiceWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_invoice_incoming_received",
          "type": "invoice.incoming.received",
          "data": {
            "id": "rec_fixture",
            "object": "received_invoice",
            "pa_invoice_id": "pa_fixture",
            "sender_siret": "73282932000074",
            "sender_name": "INTEK CENTER",
            "number": "FAC2026-00112",
            "total_ht": "9.90",
            "total_tva": "1.98",
            "total_ttc": "11.88"
          }
        }
      },
      "QuoteWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "quote.created",
              "quote.sent",
              "quote.viewed",
              "quote.accepted",
              "quote.refused",
              "quote.expired",
              "quote.converted"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/QuoteWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_quote_created",
          "type": "quote.created",
          "data": {
            "id": "qt_x",
            "object": "quote",
            "status": "draft",
            "number": null,
            "metadata": {}
          }
        }
      },
      "CreditNoteWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "credit_note.created",
              "credit_note.finalized",
              "credit_note.credit_deposited",
              "credit_note.credit_transmitted",
              "credit_note.credit_approved",
              "credit_note.credit_refused",
              "credit_note.sent"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/CreditNoteWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "id": "evt_credit_note_credit_refused",
          "object": "event",
          "type": "credit_note.credit_refused",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "data": {
            "id": "cn_example",
            "object": "credit_note",
            "number": "AV2026-00001",
            "status": "credit_refused",
            "previous_status": "deposited",
            "livemode": true,
            "documentStatus": "finalized",
            "transmissionStatus": "rejected",
            "transmissionDetail": "refused",
            "paymentStatus": "unpaid",
            "paErrorCode": null,
            "rejectionReason": "Montant contesté.",
            "rejectionCategory": "refused_by_buyer",
            "rejectionCode": null,
            "rejectionSource": "buyer",
            "metadata": {},
            "relatedInvoiceId": "inv_00090",
            "relatedInvoiceNumber": "FAC2026-00090"
          },
          "request": null
        }
      },
      "CustomerWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "customer.created",
              "customer.updated",
              "customer.deleted"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/CustomerWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_customer_created",
          "type": "customer.created",
          "data": {
            "id": "cus_x",
            "object": "customer",
            "livemode": false
          }
        }
      },
      "PaymentCreatedWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "payment.created"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/PaymentCreatedWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_payment_created",
          "type": "payment.created",
          "data": {
            "invoiceId": "inv_00090",
            "paymentId": "pay_fixture",
            "amount": "11.88",
            "method": "card"
          }
        }
      },
      "PaymentReceivedWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "payment.received"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/PaymentReceivedWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "id": "evt_payment_received",
          "object": "event",
          "type": "payment.received",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "data": {
            "id": "inv_00090",
            "object": "invoice",
            "number": "FAC2026-00090",
            "status": "paid",
            "previous_status": "rejected",
            "livemode": true,
            "documentStatus": "finalized",
            "transmissionStatus": "rejected",
            "transmissionDetail": null,
            "paymentStatus": "paid",
            "paErrorCode": null,
            "rejectionReason": "REJ_ADR — Directory line matricule_plateforme (0037) does not match expected matricule (0022)",
            "rejectionCategory": "addressing_error",
            "rejectionCode": "REJ_ADR",
            "rejectionSource": "platform",
            "metadata": {},
            "amount": "11.88",
            "total_paid": "11.88",
            "total_due": "11.88",
            "total": "11.88",
            "amountDue": "0.00",
            "paymentId": "pay_example",
            "fr212": {
              "state": "pending",
              "sentAt": null,
              "lastErrorCode": "pa_lifecycle_not_ready",
              "lastErrorReason": "Invoice is not in a cashable state",
              "updatedAt": "2026-09-10T07:51:00Z"
            }
          },
          "request": null
        }
      },
      "EreportingWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "ereporting.submitted",
              "ereporting.accepted",
              "ereporting.rejected"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/EreportingWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_ereporting_submitted",
          "type": "ereporting.submitted",
          "data": {
            "id": "rep_fixture",
            "object": "ereporting",
            "status": "submitted",
            "type": "b2c",
            "period": "2026-09",
            "attempt": 2
          }
        }
      },
      "RecurringGeneratedWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "recurring_invoice.generated"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/RecurringGeneratedWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_recurring_invoice_generated",
          "type": "recurring_invoice.generated",
          "data": {
            "id": "inv_x",
            "object": "invoice",
            "recurringInvoiceId": "recurring_fixture",
            "livemode": false
          }
        }
      },
      "RecurringFailedWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "recurring_invoice.failed"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/RecurringFailedWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_recurring_invoice_failed",
          "type": "recurring_invoice.failed",
          "data": {
            "id": "recurring_fixture",
            "object": "recurring_invoice",
            "error": "Operation unavailable"
          }
        }
      },
      "ExportWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "export.ready"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/ExportWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_export_ready",
          "type": "export.ready",
          "data": {
            "id": "job_fixture",
            "object": "export",
            "count": 10
          }
        }
      },
      "SubscriptionWebhookEvent": {
        "type": "object",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "subscription.created",
              "subscription.cancelled",
              "subscription.renewed",
              "subscription.paused"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/SubscriptionWebhookData"
          }
        },
        "readOnly": true,
        "example": {
          "object": "event",
          "apiVersion": "2026-09-01",
          "created": "2026-09-07T12:36:00.000Z",
          "livemode": true,
          "request": null,
          "id": "evt_subscription_paused",
          "type": "subscription.paused",
          "data": {
            "status": "paused",
            "pausedUntil": null,
            "stripeSubscriptionId": "sub_fixture"
          }
        }
      },
      "InvoiceArchive": {
        "type": "object",
        "properties": {
          "hash": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "previousHash": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "archivedAt": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          },
          "archivedFilePath": {
            "type": "string",
            "readOnly": true
          }
        }
      },
      "InvoicePortal": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "readOnly": true
          },
          "expiresAt": {
            "type": "string",
            "readOnly": true
          },
          "firstViewedAt": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          }
        }
      },
      "InvoicePaymentTerms": {
        "type": "object",
        "properties": {
          "terms": {
            "type": "string"
          },
          "method": {
            "type": "string"
          },
          "iban": {
            "type": "string"
          },
          "bic": {
            "type": "string"
          },
          "earlyPaymentDiscount": {
            "type": "string"
          },
          "latePaymentRate": {
            "type": "string"
          },
          "collectionFee": {
            "type": "string"
          },
          "termsDays": {
            "type": "integer"
          }
        }
      },
      "CreditNoteDates": {
        "type": "object",
        "properties": {
          "issued": {
            "type": "string",
            "readOnly": true
          },
          "finalizedAt": {
            "type": "string",
            "readOnly": true
          },
          "sentAt": {
            "type": "string",
            "readOnly": true
          }
        }
      },
      "RegistryDisclosure": {
        "type": "object",
        "readOnly": true,
        "description": "Public registry disclosure only; not identity verification. Protected values are empty (Sirene) or null (VIES). Fields are paths relative to the lookup result.",
        "required": [
          "status",
          "withheldFields"
        ],
        "properties": {
          "status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "O",
              "P",
              "N",
              null
            ],
            "readOnly": true,
            "description": "O: public; P: partial; N: non-public; null: absent or unrecognized, including VIES."
          },
          "withheldFields": {
            "type": "array",
            "readOnly": true,
            "items": {
              "type": "string"
            },
            "description": "Paths of fields whose non-disclosure marker was removed, e.g. name or address.line1."
          }
        }
      },
      "RegistryCompany": {
        "type": "object",
        "readOnly": true,
        "required": [
          "name",
          "siret",
          "siren",
          "vatNumber",
          "address",
          "active",
          "disclosure"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "siret": {
            "type": "string"
          },
          "siren": {
            "type": "string"
          },
          "vatNumber": {
            "type": "string"
          },
          "creationDate": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "employeeCount": {
            "type": "string"
          },
          "legalForm": {
            "$ref": "#/components/schemas/LegalForm"
          },
          "naf": {
            "$ref": "#/components/schemas/NafCode"
          },
          "address": {
            "type": "object",
            "properties": {
              "line1": {
                "type": "string"
              },
              "line2": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string"
              }
            }
          },
          "active": {
            "type": "boolean"
          },
          "disclosure": {
            "$ref": "#/components/schemas/RegistryDisclosure"
          }
        }
      },
      "RegistryCompanyResult": {
        "type": "object",
        "readOnly": true,
        "required": [
          "name",
          "siret",
          "siren",
          "vatNumber",
          "address",
          "active",
          "disclosure"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "siret": {
            "type": "string"
          },
          "siren": {
            "type": "string"
          },
          "vatNumber": {
            "type": "string"
          },
          "creationDate": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "employeeCount": {
            "type": "string"
          },
          "legalForm": {
            "$ref": "#/components/schemas/LegalForm"
          },
          "naf": {
            "$ref": "#/components/schemas/NafCode"
          },
          "address": {
            "type": "object",
            "properties": {
              "line1": {
                "type": "string"
              },
              "line2": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "country": {
                "type": "string"
              }
            }
          },
          "active": {
            "type": "boolean"
          },
          "disclosure": {
            "$ref": "#/components/schemas/RegistryDisclosure"
          }
        },
        "nullable": true
      },
      "CustomerLookupResult": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "object",
              "found",
              "data"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "sirene_lookup"
                ]
              },
              "found": {
                "type": "boolean"
              },
              "data": {
                "$ref": "#/components/schemas/RegistryCompanyResult"
              },
              "warning": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "object",
              "results",
              "total"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "sirene_search"
                ]
              },
              "results": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/RegistryCompany"
                }
              },
              "total": {
                "type": "integer"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "object",
              "found",
              "data"
            ],
            "properties": {
              "object": {
                "type": "string",
                "enum": [
                  "vies_lookup"
                ]
              },
              "found": {
                "type": "boolean"
              },
              "warning": {
                "type": "string"
              },
              "data": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "valid": {
                    "type": "boolean"
                  },
                  "vatNumber": {
                    "type": "string"
                  },
                  "countryCode": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string",
                    "nullable": true
                  },
                  "address": {
                    "type": "string",
                    "nullable": true
                  },
                  "disclosure": {
                    "$ref": "#/components/schemas/RegistryDisclosure"
                  }
                }
              }
            }
          }
        ]
      },
      "ObligationFollowUp": {
        "type": "object",
        "nullable": true,
        "readOnly": true,
        "required": [
          "state",
          "reasonCode",
          "reasonSource",
          "owner",
          "action",
          "nextAttemptAt"
        ],
        "additionalProperties": false,
        "description": "Operational responsibility, separate from accounting, deposit and fiscal acceptance. Unknown refusals belong to Facturino; customer correction requires established source evidence. Existing resource and status fields retain their meaning.",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "waiting",
              "reconciling",
              "attention_required",
              "completed"
            ],
            "readOnly": true
          },
          "reasonCode": {
            "type": "string",
            "nullable": true,
            "description": "Machine-readable reason for this follow-up; the platform wording remains separate.",
            "readOnly": true
          },
          "reasonSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "buyer",
              "facturino",
              null
            ],
            "readOnly": true
          },
          "owner": {
            "type": "string",
            "nullable": true,
            "enum": [
              "facturino",
              "customer",
              null
            ],
            "description": "Who owns the next action. Null when completed; integrators need no recovery cron.",
            "readOnly": true
          },
          "action": {
            "type": "string",
            "nullable": true,
            "enum": [
              "submit",
              "follow_status",
              "retry",
              "reconcile",
              "watch_directory",
              "investigate",
              "correct_source",
              "review_buyer_refusal",
              "connect_platform",
              null
            ],
            "description": "Next action and who performs it. `connect_platform`: no platform can carry the obligation until the customer connects or reconfigures one; Facturino resumes automatically afterwards. `investigate`: a technical incident handled by Facturino; no customer action.",
            "readOnly": true
          },
          "nextAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Scheduled next technical attempt or review when known. Null does not assert fiscal acceptance.",
            "readOnly": true
          }
        }
      },
      "ObligationWebhookData": {
        "type": "object",
        "readOnly": true,
        "required": [
          "id",
          "object",
          "obligation",
          "reason"
        ],
        "properties": {
          "id": {
            "type": "string",
            "readOnly": true
          },
          "object": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note",
              "payment",
              "ereporting"
            ],
            "readOnly": true
          },
          "status": {
            "type": "string",
            "readOnly": true
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "readOnly": true,
            "description": "Original reason when available; no platform wording is invented."
          },
          "invoiceId": {
            "type": "string",
            "readOnly": true,
            "description": "Present for a payment obligation."
          },
          "fr212": {
            "type": "object",
            "nullable": true,
            "description": "Où en est le statut « Encaissée » (fr:212) de cet encaissement auprès de la plateforme. Présent quand la décision fiscale du document impose ce statut ; absent sinon. `pending` : en file ; `awaiting_deposit` : attend un dépôt accepté de la facture ; `sent` : transmis (`sentAt`) ; `blocked` : ne peut pas être transmis (`lastErrorCode`) ; `reconciliation_required` / `failed` : à rapprocher.",
            "required": [
              "state",
              "sentAt",
              "lastErrorCode",
              "updatedAt"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "pending",
                  "awaiting_deposit",
                  "sent",
                  "blocked",
                  "reconciliation_required",
                  "failed"
                ],
                "readOnly": true
              },
              "sentAt": {
                "type": "string",
                "nullable": true,
                "format": "date-time",
                "readOnly": true
              },
              "lastErrorCode": {
                "type": "string",
                "nullable": true,
                "readOnly": true
              },
              "lastErrorReason": {
                "type": "string",
                "description": "Motif brut de la plateforme lorsque le statut est refusé définitivement (pa_lifecycle_rejected) ou attend un état encaissable (pa_lifecycle_not_ready). Absent si aucun motif n’est fourni.",
                "readOnly": true
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "readOnly": true
              },
              "obligation": {
                "$ref": "#/components/schemas/ObligationFollowUp"
              }
            },
            "readOnly": true
          }
        }
      },
      "ObligationWebhookEvent": {
        "type": "object",
        "readOnly": true,
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "ereporting.obligation_updated",
              "invoice.obligation_updated",
              "credit_note.obligation_updated",
              "payment.obligation_updated"
            ]
          },
          "data": {
            "$ref": "#/components/schemas/ObligationWebhookData"
          }
        }
      },
      "EReportingBlock": {
        "type": "object",
        "nullable": true,
        "readOnly": true,
        "description": "An obligation blocked before declaration preparation; the frozen source document is preserved.",
        "properties": {
          "volet": {
            "type": "string",
            "enum": [
              "transaction",
              "payment"
            ]
          },
          "code": {
            "type": "string"
          },
          "remedy": {
            "type": "string",
            "description": "Historical remedy code; obligation is the current operational authority."
          },
          "obligation": {
            "$ref": "#/components/schemas/ObligationFollowUp"
          },
          "periodStart": {
            "type": "string",
            "format": "date"
          },
          "periodEnd": {
            "type": "string",
            "format": "date"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Plafond de la minute fixe ; not_applied si aucun compteur par clé n’a été évalué.",
        "schema": {
          "oneOf": [
            {
              "type": "integer",
              "minimum": 0
            },
            {
              "type": "string",
              "enum": [
                "not_applied"
              ]
            }
          ]
        }
      },
      "RateLimitRemaining": {
        "description": "Permis restants ; not_applied si aucun compteur par clé n’a été évalué.",
        "schema": {
          "oneOf": [
            {
              "type": "integer",
              "minimum": 0
            },
            {
              "type": "string",
              "enum": [
                "not_applied"
              ]
            }
          ]
        }
      },
      "RateLimitReset": {
        "description": "Début de la prochaine minute UTC, en secondes Unix.",
        "schema": {
          "type": "integer"
        }
      }
    }
  }
}
