{
    "openapi": "3.1.0",
    "info": {
        "title": "API FenuaCompta",
        "version": "1",
        "description": "Chaque requête s'authentifie par une clé API, dans l'entête `Authorization: Bearer <clé API>`. Les clés se créent dans le logiciel, **Réglages › Documentation API**, et ne sont affichées qu'une fois. Une clé a les droits de l'utilisateur qui l'a créée et reste valable jusqu'à sa révocation : révoquez-la au moindre doute.\n\n- **Format** : JSON en entrée et en sortie. Dates `AAAA-MM-JJ`. Les listes renvoient leurs éléments dans `data`, sans pagination (sauf le catalogue de la boutique) : 1 000 au plus, 200 pour les produits.\n- **Montants** : En XPF. Devis et factures : prix HT par ligne. Taux dans `tvaBucket` : `tva16`, `tva13`, `tva5`, `tva1`, `tva0`. Commandes de la boutique : prix TTC payés.\n- **Commandes** : Identifiées par `source` + `reference` : renvoyer une commande ne crée pas de doublon.\n- **Limite** : 90 requêtes par minute. Au-delà : `429`.\n\nErreurs, corps `{ code, message, fields }` :\n- `401` : Clé absente, invalide ou révoquée\n- `402` : Abonnement inactif\n- `403` : Permission insuffisante ou module non activé\n- `404` : Ressource introuvable\n- `409` : Opération impossible dans l'état actuel\n- `422` : Données invalides, détail dans `fields`\n- `429` : Trop de requêtes"
    },
    "servers": [
        {
            "url": "https://VOTRE-COMPTE.fenuacompta.com/api/v1"
        }
    ],
    "security": [
        {
            "cle": []
        }
    ],
    "tags": [
        {
            "name": "Compte"
        },
        {
            "name": "Clients"
        },
        {
            "name": "Devis"
        },
        {
            "name": "Factures"
        },
        {
            "name": "Paiements & dépenses"
        },
        {
            "name": "Produits"
        },
        {
            "name": "Boutique en ligne"
        }
    ],
    "paths": {
        "/me": {
            "get": {
                "operationId": "me",
                "tags": [
                    "Compte"
                ],
                "summary": "Vérifier la clé",
                "description": "L'utilisateur de la clé et le compte. Le bon appel pour tester une clé.",
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "user": {
                                        "id": 1,
                                        "firstName": "Teva",
                                        "lastName": "Exemple",
                                        "email": "client@example.com",
                                        "role": "Administrateur"
                                    },
                                    "account": {
                                        "name": "Ma société",
                                        "tvaEnabled": true,
                                        "decimals": 0
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/clients": {
            "get": {
                "operationId": "clientsList",
                "tags": [
                    "Clients"
                ],
                "summary": "Lister les clients",
                "description": "Les clients actifs, par ordre alphabétique (1 000 au plus).",
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "required": false,
                        "description": "Recherche dans le nom du client.",
                        "schema": {
                            "type": "string",
                            "examples": [
                                "acme"
                            ],
                            "description": "Recherche dans le nom du client."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 42,
                                            "name": "Acme Sarl",
                                            "email": "contact@example.com",
                                            "outstanding": 0,
                                            "dueCount": 0,
                                            "invoiceCount": 1,
                                            "totalBilled": 11600
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "clientsCreate",
                "tags": [
                    "Clients"
                ],
                "summary": "Créer un client",
                "description": "Crée la fiche client et son contact. Pour réutiliser un client existant, cherchez-le avec `GET /clients?q=`.\n\nDroit requis : clients (écriture).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "examples": [
                                            "Acme Sarl"
                                        ],
                                        "description": "Nom du client ou de la société."
                                    },
                                    "email": {
                                        "type": "string",
                                        "format": "email",
                                        "examples": [
                                            "contact@example.com"
                                        ],
                                        "description": "Email du contact, utilisé pour envoyer devis et factures."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "maxLength": 40,
                                        "examples": [
                                            "+689 00 00 00 00"
                                        ]
                                    }
                                },
                                "required": [
                                    "name"
                                ]
                            },
                            "example": {
                                "name": "Acme Sarl",
                                "email": "contact@example.com",
                                "phone": "+689 00 00 00 00"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 42,
                                    "name": "Acme Sarl",
                                    "email": "contact@example.com",
                                    "phone": "+689 00 00 00 00"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/clients/{id}": {
            "get": {
                "operationId": "clientsShow",
                "tags": [
                    "Clients"
                ],
                "summary": "Détail d'un client",
                "description": "La fiche, le reste dû, et l'historique des factures, devis et paiements.\n\nDroit requis : clients (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du client.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du client."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 42,
                                    "name": "Acme Sarl",
                                    "email": "contact@example.com",
                                    "phone": "+689 00 00 00 00",
                                    "website": null,
                                    "vat": null,
                                    "city": "Papeete",
                                    "zip": "98714",
                                    "country": null,
                                    "outstanding": 0,
                                    "invoices": [
                                        {
                                            "id": 318,
                                            "number": "FACT-318",
                                            "date": "2026-09-23",
                                            "total": 11600,
                                            "status": "paid"
                                        }
                                    ],
                                    "estimates": [
                                        {
                                            "id": 108,
                                            "number": "DEV-108",
                                            "date": "2026-09-20",
                                            "total": 11600,
                                            "status": "accepted"
                                        }
                                    ],
                                    "payments": [
                                        {
                                            "id": 77,
                                            "date": "2026-09-23",
                                            "amount": 11600,
                                            "method": "Virement",
                                            "invoiceNumber": "FACT-318"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Client introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "put": {
                "operationId": "clientsUpdate",
                "tags": [
                    "Clients"
                ],
                "summary": "Modifier un client",
                "description": "Seuls les champs envoyés changent ; un champ envoyé à `null` est effacé.\n\nDroit requis : clients (écriture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du client.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du client."
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "examples": [
                                            "Acme Sarl"
                                        ]
                                    },
                                    "email": {
                                        "type": "string",
                                        "format": "email",
                                        "description": "Email du contact principal."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "maxLength": 60
                                    },
                                    "city": {
                                        "type": "string",
                                        "maxLength": 120
                                    },
                                    "zip": {
                                        "type": "string",
                                        "maxLength": 30
                                    },
                                    "country": {
                                        "type": "string",
                                        "maxLength": 120
                                    }
                                },
                                "required": [
                                    "name"
                                ]
                            },
                            "example": {
                                "name": "Acme Sarl"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 42,
                                    "name": "Acme Sarl",
                                    "email": "contact@example.com",
                                    "phone": "+689 00 00 00 00",
                                    "website": null,
                                    "vat": null,
                                    "city": "Papeete",
                                    "zip": "98714",
                                    "country": null,
                                    "outstanding": 0,
                                    "invoices": [
                                        {
                                            "id": 318,
                                            "number": "FACT-318",
                                            "date": "2026-09-23",
                                            "total": 11600,
                                            "status": "paid"
                                        }
                                    ],
                                    "estimates": [
                                        {
                                            "id": 108,
                                            "number": "DEV-108",
                                            "date": "2026-09-20",
                                            "total": 11600,
                                            "status": "accepted"
                                        }
                                    ],
                                    "payments": [
                                        {
                                            "id": 77,
                                            "date": "2026-09-23",
                                            "amount": 11600,
                                            "method": "Virement",
                                            "invoiceNumber": "FACT-318"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Client introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "L'email n'a pas pu être enregistré pour ce client. Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/estimates": {
            "get": {
                "operationId": "estimatesList",
                "tags": [
                    "Devis"
                ],
                "summary": "Lister les devis",
                "description": "Les devis, du plus récent au plus ancien (1 000 au plus), sans leurs lignes.\n\nStatuts : `draft`, `sent`, `accepted`, `declined`, `revised`, `expired`.\n\nDroit requis : devis (lecture).",
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 108,
                                            "number": "DEV-108",
                                            "clientId": 42,
                                            "clientName": "Acme Sarl",
                                            "title": "",
                                            "status": "sent",
                                            "subtotal": 10000,
                                            "discount": {
                                                "type": null,
                                                "percentage": 0,
                                                "amount": 0
                                            },
                                            "tvaTotal": 1600,
                                            "total": 11600,
                                            "createdAt": "2026-09-28T08:16:30-10:00",
                                            "date": "2026-09-28",
                                            "expiryDate": "2026-10-28",
                                            "lines": []
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "estimatesCreate",
                "tags": [
                    "Devis"
                ],
                "summary": "Créer un devis",
                "description": "Droit requis : devis (écriture).",
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "clientId": {
                                        "type": "integer",
                                        "examples": [
                                            42
                                        ],
                                        "description": "Requis sans newClient. Id du client, renvoyé par `POST /clients`."
                                    },
                                    "newClient": {
                                        "type": "object",
                                        "properties": {
                                            "name": {
                                                "type": "string",
                                                "maxLength": 255
                                            },
                                            "email": {
                                                "type": "string",
                                                "format": "email"
                                            }
                                        },
                                        "required": [
                                            "name"
                                        ],
                                        "description": "Crée le client au passage, si `clientId` est absent."
                                    },
                                    "title": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "description": "Objet, affiché sous le numéro."
                                    },
                                    "lines": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "description": {
                                                    "type": "string",
                                                    "maxLength": 500,
                                                    "examples": [
                                                        "Prestation de conseil"
                                                    ],
                                                    "description": "Libellé de la ligne."
                                                },
                                                "unitPrice": {
                                                    "type": "number",
                                                    "maximum": 99999999.99,
                                                    "minimum": 0,
                                                    "default": 0,
                                                    "examples": [
                                                        10000
                                                    ],
                                                    "description": "Prix unitaire HT. Défaut : 0."
                                                },
                                                "quantity": {
                                                    "type": "number",
                                                    "maximum": 100000,
                                                    "minimum": 0.01,
                                                    "examples": [
                                                        1
                                                    ]
                                                },
                                                "tvaBucket": {
                                                    "type": "string",
                                                    "enum": [
                                                        "tva16",
                                                        "tva13",
                                                        "tva5",
                                                        "tva1",
                                                        "tva0"
                                                    ],
                                                    "default": "tva16",
                                                    "examples": [
                                                        "tva16"
                                                    ],
                                                    "description": "Taux de TVA de la ligne. Défaut : tva16."
                                                },
                                                "unit": {
                                                    "type": "string",
                                                    "maxLength": 60,
                                                    "default": "u",
                                                    "description": "Unité affichée : h, jour, kg... Défaut : u."
                                                },
                                                "productId": {
                                                    "type": "integer",
                                                    "description": "Produit du catalogue lié à la ligne (`GET /products`)."
                                                },
                                                "type": {
                                                    "type": "string",
                                                    "enum": [
                                                        "plain",
                                                        "title",
                                                        "text"
                                                    ],
                                                    "default": "plain",
                                                    "description": "`title` : titre de section, `text` : paragraphe de texte mis en forme. Leur prix, quantité et TVA sont ignorés. Défaut : plain."
                                                }
                                            },
                                            "required": [
                                                "description",
                                                "quantity"
                                            ]
                                        },
                                        "description": "Lignes du devis, dans l'ordre. Facultatif : sans ligne, le devis est créé vide ; sinon, la somme des lignes avant remise doit être supérieure à 0."
                                    },
                                    "date": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Défaut : aujourd'hui."
                                    },
                                    "expiryDate": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Date de validité. Défaut : aujourd'hui + délai des réglages (30 jours s'il est nul)."
                                    },
                                    "discount": {
                                        "type": "object",
                                        "properties": {
                                            "type": {
                                                "type": "string",
                                                "enum": [
                                                    "percentage",
                                                    "amount"
                                                ]
                                            },
                                            "value": {
                                                "type": "number",
                                                "minimum": 0,
                                                "description": "Pourcentage (100 au plus) ou montant HT (le total au plus)."
                                            }
                                        },
                                        "description": "Remise sur le total HT."
                                    },
                                    "terms": {
                                        "type": "string",
                                        "description": "Conditions affichées en bas du document, texte mis en forme (gras, italique, listes). `\"\"` les retire. Défaut : conditions des réglages."
                                    },
                                    "notes": {
                                        "type": "string",
                                        "description": "Note interne, jamais affichée au client."
                                    }
                                }
                            },
                            "example": {
                                "clientId": 42,
                                "lines": [
                                    {
                                        "description": "Prestation de conseil",
                                        "unitPrice": 10000,
                                        "quantity": 1,
                                        "tvaBucket": "tva16"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 108,
                                    "number": "DEV-108",
                                    "clientId": 42,
                                    "clientName": "Acme Sarl",
                                    "title": "",
                                    "status": "sent",
                                    "subtotal": 10000,
                                    "discount": {
                                        "type": null,
                                        "percentage": 0,
                                        "amount": 0
                                    },
                                    "tvaTotal": 1600,
                                    "total": 11600,
                                    "createdAt": "2026-09-28T08:16:30-10:00",
                                    "date": "2026-09-28",
                                    "expiryDate": "2026-10-28",
                                    "lines": [
                                        {
                                            "id": 19,
                                            "productId": null,
                                            "description": "Prestation de conseil",
                                            "unitPrice": 10000,
                                            "unitPriceExact": 10000,
                                            "quantity": 1,
                                            "unit": "u",
                                            "tvaBucket": "tva16",
                                            "lineTotal": 10000
                                        }
                                    ],
                                    "tvaBreakdown": [
                                        {
                                            "key": "tva16",
                                            "label": "TVA 16 %",
                                            "rate": 16,
                                            "amount": 1600
                                        }
                                    ],
                                    "terms": "<p>Mauruuru pour votre confiance.</p>",
                                    "hasSections": false,
                                    "emails": []
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Client introuvable, somme des lignes nulle, ou montant supérieur à 99 999 999 F. Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/estimates/{id}": {
            "get": {
                "operationId": "estimatesShow",
                "tags": [
                    "Devis"
                ],
                "summary": "Détail d'un devis",
                "description": "Le devis avec ses lignes, le détail de la TVA et les emails envoyés.\n\nDroit requis : devis (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du devis.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du devis."
                        }
                    },
                    {
                        "name": "sections",
                        "in": "query",
                        "required": false,
                        "description": "`1` : inclut aussi les lignes de titre et de texte.",
                        "schema": {
                            "type": "boolean",
                            "description": "`1` : inclut aussi les lignes de titre et de texte."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 108,
                                    "number": "DEV-108",
                                    "clientId": 42,
                                    "clientName": "Acme Sarl",
                                    "title": "",
                                    "status": "sent",
                                    "subtotal": 10000,
                                    "discount": {
                                        "type": null,
                                        "percentage": 0,
                                        "amount": 0
                                    },
                                    "tvaTotal": 1600,
                                    "total": 11600,
                                    "createdAt": "2026-09-28T08:16:30-10:00",
                                    "date": "2026-09-28",
                                    "expiryDate": "2026-10-28",
                                    "lines": [
                                        {
                                            "id": 19,
                                            "productId": null,
                                            "description": "Prestation de conseil",
                                            "unitPrice": 10000,
                                            "unitPriceExact": 10000,
                                            "quantity": 1,
                                            "unit": "u",
                                            "tvaBucket": "tva16",
                                            "lineTotal": 10000
                                        }
                                    ],
                                    "tvaBreakdown": [
                                        {
                                            "key": "tva16",
                                            "label": "TVA 16 %",
                                            "rate": 16,
                                            "amount": 1600
                                        }
                                    ],
                                    "terms": "<p>Mauruuru pour votre confiance.</p>",
                                    "hasSections": false,
                                    "emails": []
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Devis introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/estimates/{id}/pdf": {
            "get": {
                "operationId": "estimatesPdf",
                "tags": [
                    "Devis"
                ],
                "summary": "Télécharger le PDF",
                "description": "Le devis en PDF (`application/pdf`), tel que l'imprime le logiciel.\n\nDroit requis : devis (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du devis.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du devis."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Le fichier.",
                        "content": {
                            "application/pdf": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Devis introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/estimates/{id}/share": {
            "get": {
                "operationId": "estimatesShare",
                "tags": [
                    "Devis"
                ],
                "summary": "Obtenir le lien public",
                "description": "Un lien à envoyer au client pour voir et télécharger le devis, sans compte.\n\nDroit requis : devis (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du devis.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du devis."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "shareUrl": "https://VOTRE-COMPTE.fenuacompta.com/…"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Devis introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/estimates/{id}/send": {
            "post": {
                "operationId": "estimatesSend",
                "tags": [
                    "Devis"
                ],
                "summary": "Envoyer par email",
                "description": "Envoie le devis en PDF à l'email du client. Le statut du devis ne change pas.\n\nDroit requis : devis (écriture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du devis.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du devis."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true,
                                    "status": "sent"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Devis introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Le client n'a pas d'email (`no_recipient`).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/estimates/{id}/convert": {
            "post": {
                "operationId": "estimatesConvert",
                "tags": [
                    "Devis"
                ],
                "summary": "Convertir en facture",
                "description": "Crée la facture du devis, datée du jour ; le devis est conservé. Si le devis a déjà été converti, renvoie la même facture (`200`).\n\nDroit requis : factures (écriture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du devis.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du devis."
                        }
                    }
                ],
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 319,
                                    "number": "FACT-319"
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "Devis déjà converti : la facture existante.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 319,
                                    "number": "FACT-319"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Devis introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/invoices": {
            "get": {
                "operationId": "invoicesList",
                "tags": [
                    "Factures"
                ],
                "summary": "Lister les factures",
                "description": "Les factures, de la plus récente à la plus ancienne (1 000 au plus), avec le payé et le reste dû.\n\nStatuts : `draft`, `due`, `overdue`, `part_paid`, `paid`, `credit` (avoir).\n\nDroit requis : factures (lecture).",
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 318,
                                            "number": "FACT-318",
                                            "clientId": 42,
                                            "clientName": "Acme Sarl",
                                            "title": "",
                                            "status": "due",
                                            "subtotal": 10000,
                                            "discount": {
                                                "type": null,
                                                "percentage": 0,
                                                "amount": 0
                                            },
                                            "tvaTotal": 1600,
                                            "total": 11600,
                                            "createdAt": "2026-09-28T08:16:30-10:00",
                                            "date": "2026-09-28",
                                            "dueDate": "2026-10-28",
                                            "lines": [],
                                            "paid": 0,
                                            "balance": 11600
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "invoicesCreate",
                "tags": [
                    "Factures"
                ],
                "summary": "Créer une facture",
                "description": "Droit requis : factures (écriture).",
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "clientId": {
                                        "type": "integer",
                                        "examples": [
                                            42
                                        ],
                                        "description": "Requis sans newClient. Id du client, renvoyé par `POST /clients`."
                                    },
                                    "newClient": {
                                        "type": "object",
                                        "properties": {
                                            "name": {
                                                "type": "string",
                                                "maxLength": 255
                                            },
                                            "email": {
                                                "type": "string",
                                                "format": "email"
                                            }
                                        },
                                        "required": [
                                            "name"
                                        ],
                                        "description": "Crée le client au passage, si `clientId` est absent."
                                    },
                                    "title": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "description": "Objet, affiché sous le numéro."
                                    },
                                    "lines": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "description": {
                                                    "type": "string",
                                                    "maxLength": 500,
                                                    "examples": [
                                                        "Prestation de conseil"
                                                    ],
                                                    "description": "Libellé de la ligne."
                                                },
                                                "unitPrice": {
                                                    "type": "number",
                                                    "maximum": 99999999.99,
                                                    "minimum": 0,
                                                    "default": 0,
                                                    "examples": [
                                                        10000
                                                    ],
                                                    "description": "Prix unitaire HT. Défaut : 0."
                                                },
                                                "quantity": {
                                                    "type": "number",
                                                    "maximum": 100000,
                                                    "minimum": 0.01,
                                                    "examples": [
                                                        1
                                                    ]
                                                },
                                                "tvaBucket": {
                                                    "type": "string",
                                                    "enum": [
                                                        "tva16",
                                                        "tva13",
                                                        "tva5",
                                                        "tva1",
                                                        "tva0"
                                                    ],
                                                    "default": "tva16",
                                                    "examples": [
                                                        "tva16"
                                                    ],
                                                    "description": "Taux de TVA de la ligne. Défaut : tva16."
                                                },
                                                "unit": {
                                                    "type": "string",
                                                    "maxLength": 60,
                                                    "default": "u",
                                                    "description": "Unité affichée : h, jour, kg... Défaut : u."
                                                },
                                                "productId": {
                                                    "type": "integer",
                                                    "description": "Produit du catalogue lié à la ligne (`GET /products`)."
                                                },
                                                "type": {
                                                    "type": "string",
                                                    "enum": [
                                                        "plain",
                                                        "title",
                                                        "text"
                                                    ],
                                                    "default": "plain",
                                                    "description": "`title` : titre de section, `text` : paragraphe de texte mis en forme. Leur prix, quantité et TVA sont ignorés. Défaut : plain."
                                                }
                                            },
                                            "required": [
                                                "description",
                                                "quantity"
                                            ]
                                        },
                                        "description": "Lignes de la facture, dans l'ordre. Facultatif : sans ligne, la facture est créée vide ; sinon, la somme des lignes avant remise doit être supérieure à 0."
                                    },
                                    "date": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Défaut : aujourd'hui."
                                    },
                                    "dueDate": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Date d'échéance. Défaut : aujourd'hui + délai des réglages (30 jours s'il est nul)."
                                    },
                                    "discount": {
                                        "type": "object",
                                        "properties": {
                                            "type": {
                                                "type": "string",
                                                "enum": [
                                                    "percentage",
                                                    "amount"
                                                ]
                                            },
                                            "value": {
                                                "type": "number",
                                                "minimum": 0,
                                                "description": "Pourcentage (100 au plus) ou montant HT (le total au plus)."
                                            }
                                        },
                                        "description": "Remise sur le total HT."
                                    },
                                    "terms": {
                                        "type": "string",
                                        "description": "Conditions affichées en bas du document, texte mis en forme (gras, italique, listes). `\"\"` les retire. Défaut : conditions des réglages."
                                    },
                                    "notes": {
                                        "type": "string",
                                        "description": "Note interne, jamais affichée au client."
                                    }
                                }
                            },
                            "example": {
                                "clientId": 42,
                                "lines": [
                                    {
                                        "description": "Prestation de conseil",
                                        "unitPrice": 10000,
                                        "quantity": 1,
                                        "tvaBucket": "tva16"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 318,
                                    "number": "FACT-318",
                                    "clientId": 42,
                                    "clientName": "Acme Sarl",
                                    "title": "",
                                    "status": "due",
                                    "subtotal": 10000,
                                    "discount": {
                                        "type": null,
                                        "percentage": 0,
                                        "amount": 0
                                    },
                                    "tvaTotal": 1600,
                                    "total": 11600,
                                    "createdAt": "2026-09-28T08:16:30-10:00",
                                    "date": "2026-09-28",
                                    "dueDate": "2026-10-28",
                                    "lines": [
                                        {
                                            "id": 19,
                                            "productId": null,
                                            "description": "Prestation de conseil",
                                            "unitPrice": 10000,
                                            "unitPriceExact": 10000,
                                            "quantity": 1,
                                            "unit": "u",
                                            "tvaBucket": "tva16",
                                            "lineTotal": 10000
                                        }
                                    ],
                                    "tvaBreakdown": [
                                        {
                                            "key": "tva16",
                                            "label": "TVA 16 %",
                                            "rate": 16,
                                            "amount": 1600
                                        }
                                    ],
                                    "terms": "<p>Mauruuru pour votre confiance.</p>",
                                    "hasSections": false,
                                    "payments": [],
                                    "paid": 0,
                                    "balance": 11600,
                                    "emails": []
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Client introuvable, somme des lignes nulle, ou montant supérieur à 99 999 999 F. Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/invoices/{id}": {
            "get": {
                "operationId": "invoicesShow",
                "tags": [
                    "Factures"
                ],
                "summary": "Détail d'une facture",
                "description": "La facture avec ses lignes, le détail de la TVA, les paiements et les emails envoyés.\n\nDroit requis : factures (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant de la facture.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant de la facture."
                        }
                    },
                    {
                        "name": "sections",
                        "in": "query",
                        "required": false,
                        "description": "`1` : inclut aussi les lignes de titre et de texte.",
                        "schema": {
                            "type": "boolean",
                            "description": "`1` : inclut aussi les lignes de titre et de texte."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 318,
                                    "number": "FACT-318",
                                    "clientId": 42,
                                    "clientName": "Acme Sarl",
                                    "title": "",
                                    "status": "due",
                                    "subtotal": 10000,
                                    "discount": {
                                        "type": null,
                                        "percentage": 0,
                                        "amount": 0
                                    },
                                    "tvaTotal": 1600,
                                    "total": 11600,
                                    "createdAt": "2026-09-28T08:16:30-10:00",
                                    "date": "2026-09-28",
                                    "dueDate": "2026-10-28",
                                    "lines": [
                                        {
                                            "id": 19,
                                            "productId": null,
                                            "description": "Prestation de conseil",
                                            "unitPrice": 10000,
                                            "unitPriceExact": 10000,
                                            "quantity": 1,
                                            "unit": "u",
                                            "tvaBucket": "tva16",
                                            "lineTotal": 10000
                                        }
                                    ],
                                    "tvaBreakdown": [
                                        {
                                            "key": "tva16",
                                            "label": "TVA 16 %",
                                            "rate": 16,
                                            "amount": 1600
                                        }
                                    ],
                                    "terms": "<p>Mauruuru pour votre confiance.</p>",
                                    "hasSections": false,
                                    "payments": [],
                                    "paid": 0,
                                    "balance": 11600,
                                    "emails": []
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Facture introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/invoices/{id}/pdf": {
            "get": {
                "operationId": "invoicesPdf",
                "tags": [
                    "Factures"
                ],
                "summary": "Télécharger le PDF",
                "description": "La facture en PDF (`application/pdf`).\n\nDroit requis : factures (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant de la facture.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant de la facture."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Le fichier.",
                        "content": {
                            "application/pdf": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Facture introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/invoices/{id}/share": {
            "get": {
                "operationId": "invoicesShare",
                "tags": [
                    "Factures"
                ],
                "summary": "Obtenir le lien public",
                "description": "Un lien à envoyer au client pour voir et télécharger la facture, sans compte.\n\nDroit requis : factures (lecture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant de la facture.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant de la facture."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "shareUrl": "https://VOTRE-COMPTE.fenuacompta.com/…"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Facture introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/invoices/{id}/send": {
            "post": {
                "operationId": "invoicesSend",
                "tags": [
                    "Factures"
                ],
                "summary": "Envoyer par email",
                "description": "Envoie la facture en PDF à l'email du client.\n\nDroit requis : factures (écriture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant de la facture.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant de la facture."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true,
                                    "status": "due"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Facture introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Le client n'a pas d'email (`no_recipient`).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/payments": {
            "post": {
                "operationId": "paymentsCreate",
                "tags": [
                    "Paiements & dépenses"
                ],
                "summary": "Enregistrer un paiement",
                "description": "Un paiement reçu sur une facture ; son statut est recalculé (payée, partiellement payée).\n\n`status` : le statut de la facture après le paiement (`paid`, `part_paid`...).\n\nDroit requis : factures (écriture).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "invoiceId": {
                                        "type": "integer",
                                        "examples": [
                                            318
                                        ]
                                    },
                                    "amount": {
                                        "type": "number",
                                        "maximum": 99999999,
                                        "minimum": 1,
                                        "examples": [
                                            11600
                                        ],
                                        "description": "Montant reçu, arrondi au franc."
                                    },
                                    "date": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Défaut : aujourd'hui."
                                    },
                                    "method": {
                                        "type": "string",
                                        "maxLength": 60,
                                        "default": "Espèces",
                                        "examples": [
                                            "Virement"
                                        ],
                                        "description": "Défaut : Espèces."
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 1000
                                    }
                                },
                                "required": [
                                    "invoiceId",
                                    "amount"
                                ]
                            },
                            "example": {
                                "invoiceId": 318,
                                "amount": 11600,
                                "method": "Virement"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true,
                                    "invoiceId": 318,
                                    "status": "paid"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Facture introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/expenses": {
            "get": {
                "operationId": "expensesList",
                "tags": [
                    "Paiements & dépenses"
                ],
                "summary": "Lister les dépenses",
                "description": "Les dépenses créées par l'utilisateur de la clé, de la plus récente à la plus ancienne (1 000 au plus).\n\nDroit requis : dépenses (lecture).",
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 55,
                                            "amount": 11600,
                                            "tva": {
                                                "tva16": 1600
                                            },
                                            "importTva": null,
                                            "title": "Fournitures de bureau",
                                            "supplierId": null,
                                            "supplierName": null,
                                            "categoryId": 3,
                                            "clientId": null,
                                            "date": "2026-09-20",
                                            "paymentStatus": "paid",
                                            "paymentMethod": "Carte bancaire",
                                            "paymentDate": "2026-09-20",
                                            "dueDate": null,
                                            "notes": null,
                                            "attachmentId": null,
                                            "attachmentUrl": null,
                                            "attachmentType": null,
                                            "attachmentName": null,
                                            "createdAt": "2026-09-20T10:02:11-10:00"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "expensesCreate",
                "tags": [
                    "Paiements & dépenses"
                ],
                "summary": "Créer une dépense",
                "description": "Droit requis : dépenses (écriture).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "amount": {
                                        "type": "number",
                                        "maximum": 99999999,
                                        "minimum": -99999999,
                                        "examples": [
                                            11600
                                        ],
                                        "description": "Montant TTC payé. Négatif pour un avoir fournisseur."
                                    },
                                    "date": {
                                        "type": "string",
                                        "format": "date",
                                        "examples": [
                                            "2026-09-20"
                                        ]
                                    },
                                    "title": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "examples": [
                                            "Fournitures de bureau"
                                        ],
                                        "description": "Libellé de la dépense."
                                    },
                                    "tva": {
                                        "type": "object",
                                        "properties": {
                                            "tva16": {
                                                "type": "integer",
                                                "default": 0,
                                                "examples": [
                                                    1600
                                                ],
                                                "description": "Montant de TVA à 16 %, en francs. Défaut : 0."
                                            },
                                            "tva13": {
                                                "type": "integer",
                                                "default": 0,
                                                "description": "Montant de TVA à 13 %, en francs. Défaut : 0."
                                            },
                                            "tva5": {
                                                "type": "integer",
                                                "default": 0,
                                                "description": "Montant de TVA à 5 %, en francs. Défaut : 0."
                                            },
                                            "tva1": {
                                                "type": "integer",
                                                "default": 0,
                                                "description": "Montant de TVA à 1 %, en francs. Défaut : 0."
                                            }
                                        },
                                        "description": "TVA récupérable, par taux."
                                    },
                                    "importTva": {
                                        "type": "integer",
                                        "description": "TVA payée à l'importation, en francs."
                                    },
                                    "categoryId": {
                                        "type": "integer",
                                        "description": "Catégorie de dépense. Défaut : catégorie par défaut."
                                    },
                                    "supplierId": {
                                        "type": "integer",
                                        "description": "Fournisseur, si le module Fournisseurs est actif."
                                    },
                                    "clientId": {
                                        "type": "integer",
                                        "description": "Client concerné."
                                    },
                                    "paymentStatus": {
                                        "type": "string",
                                        "enum": [
                                            "paid",
                                            "pending"
                                        ],
                                        "default": "paid",
                                        "description": "`pending` : dépense à payer, avec `dueDate`. Défaut : paid."
                                    },
                                    "paymentMethod": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "examples": [
                                            "Carte bancaire"
                                        ]
                                    },
                                    "paymentDate": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Si payée. Défaut : date de la dépense."
                                    },
                                    "dueDate": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Échéance, si à payer."
                                    },
                                    "notes": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "amount",
                                    "date"
                                ]
                            },
                            "example": {
                                "amount": 11600,
                                "date": "2026-09-20",
                                "title": "Fournitures de bureau",
                                "tva": {
                                    "tva16": 1600
                                },
                                "paymentMethod": "Carte bancaire"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 55,
                                    "amount": 11600,
                                    "tva": {
                                        "tva16": 1600
                                    },
                                    "importTva": null,
                                    "title": "Fournitures de bureau",
                                    "supplierId": null,
                                    "supplierName": null,
                                    "categoryId": 3,
                                    "clientId": null,
                                    "date": "2026-09-20",
                                    "paymentStatus": "paid",
                                    "paymentMethod": "Carte bancaire",
                                    "paymentDate": "2026-09-20",
                                    "dueDate": null,
                                    "notes": null,
                                    "attachmentId": null,
                                    "attachmentUrl": null,
                                    "attachmentType": null,
                                    "attachmentName": null,
                                    "createdAt": "2026-09-20T10:02:11-10:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/products": {
            "get": {
                "operationId": "productsList",
                "tags": [
                    "Produits"
                ],
                "summary": "Lister les produits",
                "description": "Le catalogue, par ordre alphabétique (200 au plus) : de quoi remplir des lignes de devis ou de facture.",
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "required": false,
                        "description": "Recherche dans la désignation.",
                        "schema": {
                            "type": "string",
                            "examples": [
                                "miel"
                            ],
                            "description": "Recherche dans la désignation."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 12,
                                            "name": "Miel de Tahiti 500 g",
                                            "unitPrice": 1500,
                                            "unitPriceExact": 1500,
                                            "tvaBucket": "tva5",
                                            "unit": "pot",
                                            "kind": "produit"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "productsCreate",
                "tags": [
                    "Produits"
                ],
                "summary": "Créer un produit",
                "description": "La référence (SKU), le stock, les descriptions et les images se gèrent dans le logiciel.\n\nDroit requis : produits (écriture).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "examples": [
                                            "Miel de Tahiti 500 g"
                                        ],
                                        "description": "Désignation."
                                    },
                                    "unitPrice": {
                                        "type": "number",
                                        "maximum": 99999999.99,
                                        "minimum": 0,
                                        "examples": [
                                            1500
                                        ],
                                        "description": "Prix unitaire HT."
                                    },
                                    "tvaBucket": {
                                        "type": "string",
                                        "enum": [
                                            "tva16",
                                            "tva13",
                                            "tva5",
                                            "tva1",
                                            "tva0"
                                        ],
                                        "default": "tva16",
                                        "examples": [
                                            "tva5"
                                        ],
                                        "description": "Taux de TVA du produit. Défaut : tva16."
                                    },
                                    "unit": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "default": "/",
                                        "examples": [
                                            "pot"
                                        ],
                                        "description": "Défaut : /."
                                    },
                                    "kind": {
                                        "type": "string",
                                        "enum": [
                                            "produit",
                                            "service"
                                        ],
                                        "default": "produit",
                                        "description": "Sert à ventiler la TVA (livraisons de biens ou prestations de services). Défaut : produit."
                                    }
                                },
                                "required": [
                                    "name",
                                    "unitPrice"
                                ]
                            },
                            "example": {
                                "name": "Miel de Tahiti 500 g",
                                "unitPrice": 1500,
                                "tvaBucket": "tva5",
                                "unit": "pot"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 12,
                                    "name": "Miel de Tahiti 500 g",
                                    "unitPrice": 1500,
                                    "unitPriceExact": 1500,
                                    "tvaBucket": "tva5",
                                    "unit": "pot",
                                    "kind": "produit"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/products/{id}": {
            "put": {
                "operationId": "productsUpdate",
                "tags": [
                    "Produits"
                ],
                "summary": "Modifier un produit",
                "description": "Seuls les champs envoyés changent. Cette route ne modifie pas le stock.\n\nDroit requis : produits (écriture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du produit.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du produit."
                        }
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "maxLength": 255
                                    },
                                    "unitPrice": {
                                        "type": "number",
                                        "maximum": 99999999.99,
                                        "minimum": 0,
                                        "examples": [
                                            1600
                                        ],
                                        "description": "Prix unitaire HT."
                                    },
                                    "tvaBucket": {
                                        "type": "string",
                                        "enum": [
                                            "tva16",
                                            "tva13",
                                            "tva5",
                                            "tva1",
                                            "tva0"
                                        ],
                                        "description": "Taux de TVA du produit."
                                    },
                                    "unit": {
                                        "type": "string",
                                        "maxLength": 50
                                    },
                                    "kind": {
                                        "type": "string",
                                        "enum": [
                                            "produit",
                                            "service"
                                        ]
                                    }
                                }
                            },
                            "example": {
                                "unitPrice": 1600
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": 12,
                                    "name": "Miel de Tahiti 500 g",
                                    "unitPrice": 1500,
                                    "unitPriceExact": 1500,
                                    "tvaBucket": "tva5",
                                    "unit": "pot",
                                    "kind": "produit"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Produit introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Aucun champ à modifier. Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "operationId": "productsDelete",
                "tags": [
                    "Produits"
                ],
                "summary": "Supprimer un produit",
                "description": "Supprime le produit et ses images. Les factures déjà émises gardent leurs lignes.\n\nDroit requis : produits (écriture).",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Identifiant du produit.",
                        "schema": {
                            "type": "integer",
                            "examples": [
                                42
                            ],
                            "description": "Identifiant du produit."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "ok": true
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Produit introuvable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shop/products": {
            "get": {
                "operationId": "shopProducts",
                "tags": [
                    "Boutique en ligne"
                ],
                "summary": "Lire le catalogue et le stock",
                "description": "Les produits du catalogue avec leur prix, leur stock, leur catégorie, leurs descriptions et leurs images. Une synchronisation régulière ne demande que les produits modifiés depuis la précédente.\n\n`stock` et `lowStockAlert` valent `null` si le stock n'est pas suivi. Le stock est en unités entières. `priceTtc` est arrondi à la précision du compte (franc, ou centime pour un compte à 2 décimales).\n\nCorrespondance WooCommerce : `priceTtc` → `regular_price`, `shortDescription` → `short_description`, `category.name` → `categories`, `images[].url` → `images[].src`, `stock` → `stock_quantity`. Les URL d'images sont publiques.\n\nComparez régulièrement avec le catalogue complet pour détecter les produits retirés.\n\nDroit requis : produits (lecture).",
                "parameters": [
                    {
                        "name": "updatedSince",
                        "in": "query",
                        "required": false,
                        "description": "Seulement les produits modifiés depuis cette date (stock et images compris).",
                        "schema": {
                            "type": "string",
                            "format": "date-time",
                            "examples": [
                                "2026-09-23T08:00:00-10:00"
                            ],
                            "description": "Seulement les produits modifiés depuis cette date (stock et images compris)."
                        }
                    },
                    {
                        "name": "sku",
                        "in": "query",
                        "required": false,
                        "description": "Un seul produit, par sa référence.",
                        "schema": {
                            "type": "string",
                            "maxLength": 100,
                            "examples": [
                                "MIEL-500"
                            ],
                            "description": "Un seul produit, par sa référence."
                        }
                    },
                    {
                        "name": "hasSku",
                        "in": "query",
                        "required": false,
                        "description": "`1` : seulement les produits qui ont une référence, c'est-à-dire ceux vendus en ligne.",
                        "schema": {
                            "type": "integer",
                            "enum": [
                                0,
                                1
                            ],
                            "examples": [
                                1
                            ],
                            "description": "`1` : seulement les produits qui ont une référence, c'est-à-dire ceux vendus en ligne."
                        }
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Numéro de page. Défaut : 1.",
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1,
                            "description": "Numéro de page. Défaut : 1."
                        }
                    },
                    {
                        "name": "perPage",
                        "in": "query",
                        "required": false,
                        "description": "Éléments par page. Défaut : 100.",
                        "schema": {
                            "type": "integer",
                            "maximum": 200,
                            "minimum": 1,
                            "default": 100,
                            "description": "Éléments par page. Défaut : 100."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": 12,
                                            "sku": "MIEL-500",
                                            "name": "Miel de Tahiti 500 g",
                                            "unit": "pot",
                                            "category": {
                                                "id": 14,
                                                "name": "Épicerie fine"
                                            },
                                            "shortDescription": "Miel de fleurs récolté à Taravao.",
                                            "description": "<p>Miel cru, non chauffé, mis en pot à la main.</p>",
                                            "images": [
                                                {
                                                    "id": 301,
                                                    "url": "https://VOTRE-COMPTE.fenuacompta.com/…/miel-500.jpg",
                                                    "thumbnailUrl": "https://VOTRE-COMPTE.fenuacompta.com/…/miel-500-thumb.jpg",
                                                    "name": "miel-500.jpg",
                                                    "position": 0
                                                }
                                            ],
                                            "priceHt": 1500,
                                            "tvaRate": 5,
                                            "priceTtc": 1575,
                                            "stockManaged": true,
                                            "stock": 42,
                                            "lowStockAlert": 5,
                                            "updatedAt": "2026-09-23T09:14:02-10:00"
                                        }
                                    ],
                                    "page": 1,
                                    "perPage": 100,
                                    "total": 1
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Module boutique non activé sur ce compte (`module_disabled`). Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shop/orders": {
            "post": {
                "operationId": "shopOrdersCreate",
                "tags": [
                    "Boutique en ligne"
                ],
                "summary": "Facturer une commande de la boutique",
                "description": "Retrouve ou crée le client (par email), crée la facture au prix payé et, si la commande est payée, le paiement. Le stock est décrémenté.\n\n**Stock** : chaque réponse (réception, paiement, annulation) renvoie `stock`, le stock à jour des produits de la commande dont le stock est suivi. Reportez-le dans la boutique, qui décrémente aussi son propre stock.\n\nUne commande déjà reçue renvoie `200` et `\"duplicate\": true` : un envoi peut être rejoué sans risque.\n\n`paymentRecorded` indique si le paiement a été enregistré. S'il vaut `false`, enregistrez-le avec `POST /shop/orders/{reference}/payment`.\n\nDroit requis : factures (écriture).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "reference": {
                                        "type": "string",
                                        "maxLength": 100,
                                        "pattern": "^[A-Za-z0-9._-]+$",
                                        "examples": [
                                            "1042"
                                        ],
                                        "description": "Numéro de la commande dans la boutique. Renvoyer une commande déjà reçue ne crée rien de plus."
                                    },
                                    "source": {
                                        "type": "string",
                                        "maxLength": 50,
                                        "pattern": "^[A-Za-z0-9_-]+$",
                                        "default": "woocommerce",
                                        "description": "Boutique d'origine. Avec `reference`, identifie la commande. Défaut : woocommerce."
                                    },
                                    "date": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Date de la facture. Un horodatage UTC est ramené au jour de Tahiti. Échéance : le même jour si la commande est payée, sinon cette date + le délai de paiement des réglages (30 jours s'il est nul). Défaut : aujourd'hui."
                                    },
                                    "customer": {
                                        "type": "object",
                                        "properties": {
                                            "email": {
                                                "type": "string",
                                                "format": "email",
                                                "maxLength": 255,
                                                "examples": [
                                                    "client@example.com"
                                                ]
                                            },
                                            "company": {
                                                "type": "string",
                                                "maxLength": 255,
                                                "description": "Nom du client. À défaut : prénom et nom, puis l'email."
                                            },
                                            "firstName": {
                                                "type": "string",
                                                "maxLength": 100,
                                                "examples": [
                                                    "Teva"
                                                ],
                                                "description": "Prénom du contact."
                                            },
                                            "lastName": {
                                                "type": "string",
                                                "maxLength": 100,
                                                "examples": [
                                                    "Exemple"
                                                ],
                                                "description": "Nom du contact."
                                            },
                                            "phone": {
                                                "type": "string",
                                                "maxLength": 40,
                                                "examples": [
                                                    "+689 00 00 00 00"
                                                ]
                                            },
                                            "address": {
                                                "type": "object",
                                                "properties": {
                                                    "street": {
                                                        "type": "string",
                                                        "maxLength": 255,
                                                        "examples": [
                                                            "Rue du Commerce"
                                                        ]
                                                    },
                                                    "city": {
                                                        "type": "string",
                                                        "maxLength": 100,
                                                        "examples": [
                                                            "Papeete"
                                                        ]
                                                    },
                                                    "zip": {
                                                        "type": "string",
                                                        "maxLength": 20,
                                                        "examples": [
                                                            "98714"
                                                        ]
                                                    },
                                                    "country": {
                                                        "type": "string",
                                                        "maxLength": 100
                                                    }
                                                },
                                                "description": "Adresse de facturation."
                                            }
                                        },
                                        "required": [
                                            "email"
                                        ],
                                        "description": "L'acheteur. Un client existant avec le même email est réutilisé, sinon il est créé."
                                    },
                                    "lines": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "sku": {
                                                    "type": "string",
                                                    "maxLength": 100,
                                                    "examples": [
                                                        "MIEL-500"
                                                    ],
                                                    "description": "Référence du produit dans FenuaCompta, majuscules indifférentes. Une référence inconnue refuse toute la commande."
                                                },
                                                "quantity": {
                                                    "type": "number",
                                                    "maximum": 100000,
                                                    "minimum": 0.01,
                                                    "examples": [
                                                        2
                                                    ],
                                                    "description": "Nombre entier si le stock du produit est suivi."
                                                },
                                                "unitPriceTtc": {
                                                    "type": "number",
                                                    "maximum": 99999999,
                                                    "minimum": 0,
                                                    "examples": [
                                                        1575
                                                    ],
                                                    "description": "Prix unitaire TTC réellement payé, coupons déduits. Le HT est recalculé avec le taux de TVA du produit."
                                                },
                                                "name": {
                                                    "type": "string",
                                                    "maxLength": 500,
                                                    "description": "Libellé de la ligne sur la facture. Défaut : la désignation du produit."
                                                }
                                            },
                                            "required": [
                                                "sku",
                                                "quantity",
                                                "unitPriceTtc"
                                            ]
                                        },
                                        "minItems": 1,
                                        "description": "Les articles commandés, dans l'ordre de la facture."
                                    },
                                    "shipping": {
                                        "type": "object",
                                        "properties": {
                                            "amountTtc": {
                                                "type": "number",
                                                "maximum": 99999999,
                                                "minimum": 0,
                                                "examples": [
                                                    1000
                                                ],
                                                "description": "Requis avec shipping. Montant TTC payé."
                                            },
                                            "tvaRate": {
                                                "type": "integer",
                                                "enum": [
                                                    0,
                                                    1,
                                                    5,
                                                    13,
                                                    16
                                                ],
                                                "examples": [
                                                    13
                                                ],
                                                "description": "Requis si montant > 0. Taux de TVA des frais, en %."
                                            },
                                            "label": {
                                                "type": "string",
                                                "maxLength": 255,
                                                "default": "Frais de livraison",
                                                "examples": [
                                                    "Livraison Tahiti"
                                                ],
                                                "description": "Défaut : Frais de livraison."
                                            }
                                        },
                                        "description": "Frais de livraison, ajoutés en dernière ligne de la facture."
                                    },
                                    "payment": {
                                        "type": "object",
                                        "properties": {
                                            "status": {
                                                "type": "string",
                                                "enum": [
                                                    "paid",
                                                    "pending"
                                                ],
                                                "examples": [
                                                    "paid"
                                                ],
                                                "description": "`paid` : la facture est soldée. `pending` : elle reste due, à payer plus tard avec `POST /shop/orders/{reference}/payment`."
                                            },
                                            "method": {
                                                "type": "string",
                                                "maxLength": 60,
                                                "default": "Boutique en ligne",
                                                "examples": [
                                                    "Carte bancaire"
                                                ],
                                                "description": "Moyen de paiement affiché sur le paiement. Défaut : Boutique en ligne."
                                            },
                                            "transactionId": {
                                                "type": "string",
                                                "maxLength": 100,
                                                "description": "Identifiant du paiement chez le prestataire, affiché sur le paiement."
                                            },
                                            "date": {
                                                "type": "string",
                                                "format": "date",
                                                "description": "Défaut : aujourd'hui."
                                            }
                                        },
                                        "required": [
                                            "status"
                                        ]
                                    },
                                    "totalTtc": {
                                        "type": "number",
                                        "minimum": 0,
                                        "examples": [
                                            4150
                                        ],
                                        "description": "Total payé sur la boutique. Il est comparé au total de la facture dans `totals`."
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 2000,
                                        "description": "Note ajoutée à la facture."
                                    }
                                },
                                "required": [
                                    "reference",
                                    "customer",
                                    "lines",
                                    "payment"
                                ]
                            },
                            "example": {
                                "reference": "1042",
                                "customer": {
                                    "email": "client@example.com",
                                    "firstName": "Teva",
                                    "lastName": "Exemple",
                                    "phone": "+689 00 00 00 00",
                                    "address": {
                                        "street": "Rue du Commerce",
                                        "city": "Papeete",
                                        "zip": "98714"
                                    }
                                },
                                "lines": [
                                    {
                                        "sku": "MIEL-500",
                                        "quantity": 2,
                                        "unitPriceTtc": 1575
                                    }
                                ],
                                "shipping": {
                                    "amountTtc": 1000,
                                    "tvaRate": 13,
                                    "label": "Livraison Tahiti"
                                },
                                "payment": {
                                    "status": "paid",
                                    "method": "Carte bancaire"
                                },
                                "totalTtc": 4150
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "source": "woocommerce",
                                    "reference": "1042",
                                    "status": "invoiced",
                                    "clientId": 57,
                                    "invoice": {
                                        "id": 318,
                                        "number": "FACT-318",
                                        "clientId": 57,
                                        "clientName": "Teva Exemple",
                                        "title": "Commande en ligne n° 1042",
                                        "status": "paid",
                                        "subtotal": 3634,
                                        "discount": {
                                            "type": null,
                                            "percentage": 0,
                                            "amount": 0
                                        },
                                        "tvaTotal": 516,
                                        "total": 4150,
                                        "createdAt": "2026-09-23T09:20:11-10:00",
                                        "date": "2026-09-23",
                                        "dueDate": "2026-09-23",
                                        "lines": []
                                    },
                                    "creditNote": null,
                                    "totals": {
                                        "invoice": 4150,
                                        "site": 4150,
                                        "difference": 0
                                    },
                                    "stock": [
                                        {
                                            "sku": "MIEL-500",
                                            "stock": 40
                                        }
                                    ],
                                    "paymentRecorded": true
                                }
                            }
                        }
                    },
                    "200": {
                        "description": "Commande déjà reçue : rien n'est recréé.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "source": "woocommerce",
                                    "reference": "1042",
                                    "status": "invoiced",
                                    "clientId": 57,
                                    "invoice": {
                                        "id": 318,
                                        "number": "FACT-318",
                                        "clientId": 57,
                                        "clientName": "Teva Exemple",
                                        "title": "Commande en ligne n° 1042",
                                        "status": "paid",
                                        "subtotal": 3634,
                                        "discount": {
                                            "type": null,
                                            "percentage": 0,
                                            "amount": 0
                                        },
                                        "tvaTotal": 516,
                                        "total": 4150,
                                        "createdAt": "2026-09-23T09:20:11-10:00",
                                        "date": "2026-09-23",
                                        "dueDate": "2026-09-23",
                                        "lines": []
                                    },
                                    "creditNote": null,
                                    "totals": {
                                        "invoice": 4150,
                                        "site": 4150,
                                        "difference": 0
                                    },
                                    "stock": [
                                        {
                                            "sku": "MIEL-500",
                                            "stock": 40
                                        }
                                    ],
                                    "duplicate": true
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Module boutique non activé sur ce compte (`module_disabled`). Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Opération impossible dans l'état actuel de la commande : réessayez plus tard ou consultez `GET /shop/orders/{reference}`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Référence de produit inconnue, quantité non entière pour un produit dont le stock est suivi, total nul ou taux de TVA des frais manquant : rien n'est créé. Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shop/orders/{reference}": {
            "get": {
                "operationId": "shopOrdersShow",
                "tags": [
                    "Boutique en ligne"
                ],
                "summary": "État d'une commande facturée",
                "description": "La commande, sa facture et, si elle a été annulée, son avoir.\n\nDroit requis : factures (lecture).",
                "parameters": [
                    {
                        "name": "reference",
                        "in": "path",
                        "required": true,
                        "description": "Numéro de la commande dans la boutique.",
                        "schema": {
                            "type": "string",
                            "maxLength": 100,
                            "pattern": "^[A-Za-z0-9._-]+$",
                            "examples": [
                                "1042"
                            ],
                            "description": "Numéro de la commande dans la boutique."
                        }
                    },
                    {
                        "name": "source",
                        "in": "query",
                        "required": false,
                        "description": "Boutique d'origine, si la commande a été envoyée avec une autre `source`. Défaut : woocommerce.",
                        "schema": {
                            "type": "string",
                            "maxLength": 50,
                            "default": "woocommerce",
                            "description": "Boutique d'origine, si la commande a été envoyée avec une autre `source`. Défaut : woocommerce."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "source": "woocommerce",
                                    "reference": "1042",
                                    "status": "invoiced",
                                    "clientId": 57,
                                    "invoice": {
                                        "id": 318,
                                        "number": "FACT-318",
                                        "clientId": 57,
                                        "clientName": "Teva Exemple",
                                        "title": "Commande en ligne n° 1042",
                                        "status": "paid",
                                        "subtotal": 3634,
                                        "discount": {
                                            "type": null,
                                            "percentage": 0,
                                            "amount": 0
                                        },
                                        "tvaTotal": 516,
                                        "total": 4150,
                                        "createdAt": "2026-09-23T09:20:11-10:00",
                                        "date": "2026-09-23",
                                        "dueDate": "2026-09-23",
                                        "lines": []
                                    },
                                    "creditNote": null,
                                    "totals": {
                                        "invoice": 4150,
                                        "site": 4150,
                                        "difference": 0
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Module boutique non activé sur ce compte (`module_disabled`). Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Aucune commande avec cette référence (et cette source).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shop/orders/{reference}/cancel": {
            "post": {
                "operationId": "shopOrdersCancel",
                "tags": [
                    "Boutique en ligne"
                ],
                "summary": "Annuler une commande",
                "description": "Annulation totale : crée un avoir et remet le stock. Le remboursement se fait dans votre moyen de paiement. Une commande déjà annulée renvoie la même réponse, sans second avoir.\n\nDroit requis : factures (écriture).",
                "parameters": [
                    {
                        "name": "reference",
                        "in": "path",
                        "required": true,
                        "description": "Numéro de la commande dans la boutique.",
                        "schema": {
                            "type": "string",
                            "maxLength": 100,
                            "pattern": "^[A-Za-z0-9._-]+$",
                            "examples": [
                                "1042"
                            ],
                            "description": "Numéro de la commande dans la boutique."
                        }
                    },
                    {
                        "name": "source",
                        "in": "query",
                        "required": false,
                        "description": "Boutique d'origine, si la commande a été envoyée avec une autre `source`. Défaut : woocommerce.",
                        "schema": {
                            "type": "string",
                            "maxLength": 50,
                            "default": "woocommerce",
                            "description": "Boutique d'origine, si la commande a été envoyée avec une autre `source`. Défaut : woocommerce."
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "source": "woocommerce",
                                    "reference": "1042",
                                    "status": "cancelled",
                                    "clientId": 57,
                                    "invoice": {
                                        "id": 318,
                                        "number": "FACT-318",
                                        "clientId": 57,
                                        "clientName": "Teva Exemple",
                                        "title": "Commande en ligne n° 1042",
                                        "status": "paid",
                                        "subtotal": 3634,
                                        "discount": {
                                            "type": null,
                                            "percentage": 0,
                                            "amount": 0
                                        },
                                        "tvaTotal": 516,
                                        "total": 4150,
                                        "createdAt": "2026-09-23T09:20:11-10:00",
                                        "date": "2026-09-23",
                                        "dueDate": "2026-09-23",
                                        "lines": []
                                    },
                                    "creditNote": {
                                        "id": 325,
                                        "number": "FACT-325",
                                        "clientId": 57,
                                        "clientName": "Teva Exemple",
                                        "title": "Commande en ligne n° 1042",
                                        "status": "credit",
                                        "subtotal": -3634,
                                        "discount": {
                                            "type": null,
                                            "percentage": 0,
                                            "amount": 0
                                        },
                                        "tvaTotal": -516,
                                        "total": -4150,
                                        "createdAt": "2026-09-24T08:02:40-10:00",
                                        "date": "2026-09-24",
                                        "dueDate": "2026-09-24",
                                        "lines": []
                                    },
                                    "totals": {
                                        "invoice": 4150,
                                        "site": 4150,
                                        "difference": 0
                                    },
                                    "stock": [
                                        {
                                            "sku": "MIEL-500",
                                            "stock": 42
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Module boutique non activé sur ce compte (`module_disabled`). Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Aucune commande avec cette référence (et cette source).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Opération impossible dans l'état actuel de la commande : réessayez plus tard ou consultez `GET /shop/orders/{reference}`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/shop/orders/{reference}/payment": {
            "post": {
                "operationId": "shopOrdersPay",
                "tags": [
                    "Boutique en ligne"
                ],
                "summary": "Enregistrer le paiement d'une commande",
                "description": "Pour une commande envoyée en `pending` puis payée sur la boutique : enregistre le paiement du reste dû. Une facture déjà soldée n'est pas payée deux fois.\n\nDroit requis : factures (écriture).",
                "parameters": [
                    {
                        "name": "reference",
                        "in": "path",
                        "required": true,
                        "description": "Numéro de la commande dans la boutique.",
                        "schema": {
                            "type": "string",
                            "maxLength": 100,
                            "pattern": "^[A-Za-z0-9._-]+$",
                            "examples": [
                                "1042"
                            ],
                            "description": "Numéro de la commande dans la boutique."
                        }
                    },
                    {
                        "name": "source",
                        "in": "query",
                        "required": false,
                        "description": "Boutique d'origine, si la commande a été envoyée avec une autre `source`. Défaut : woocommerce.",
                        "schema": {
                            "type": "string",
                            "maxLength": 50,
                            "default": "woocommerce",
                            "description": "Boutique d'origine, si la commande a été envoyée avec une autre `source`. Défaut : woocommerce."
                        }
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "method": {
                                        "type": "string",
                                        "maxLength": 60,
                                        "default": "Boutique en ligne",
                                        "examples": [
                                            "Carte bancaire"
                                        ],
                                        "description": "Défaut : Boutique en ligne."
                                    },
                                    "transactionId": {
                                        "type": "string",
                                        "maxLength": 100,
                                        "examples": [
                                            "pi_3Q..."
                                        ]
                                    },
                                    "date": {
                                        "type": "string",
                                        "format": "date",
                                        "description": "Défaut : aujourd'hui."
                                    }
                                }
                            },
                            "example": {
                                "method": "Carte bancaire",
                                "transactionId": "pi_3Q..."
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Succès.",
                        "content": {
                            "application/json": {
                                "example": {
                                    "source": "woocommerce",
                                    "reference": "1042",
                                    "status": "invoiced",
                                    "clientId": 57,
                                    "invoice": {
                                        "id": 318,
                                        "number": "FACT-318",
                                        "clientId": 57,
                                        "clientName": "Teva Exemple",
                                        "title": "Commande en ligne n° 1042",
                                        "status": "paid",
                                        "subtotal": 3634,
                                        "discount": {
                                            "type": null,
                                            "percentage": 0,
                                            "amount": 0
                                        },
                                        "tvaTotal": 516,
                                        "total": 4150,
                                        "createdAt": "2026-09-23T09:20:11-10:00",
                                        "date": "2026-09-23",
                                        "dueDate": "2026-09-23",
                                        "lines": []
                                    },
                                    "creditNote": null,
                                    "totals": {
                                        "invoice": 4150,
                                        "site": 4150,
                                        "difference": 0
                                    },
                                    "stock": [
                                        {
                                            "sku": "MIEL-500",
                                            "stock": 40
                                        }
                                    ],
                                    "paymentRecorded": true
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé absente, invalide ou révoquée.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "402": {
                        "description": "Abonnement inactif.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Module boutique non activé sur ce compte (`module_disabled`). Permission insuffisante.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Aucune commande avec cette référence (et cette source).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Opération impossible dans l'état actuel de la commande : réessayez plus tard ou consultez `GET /shop/orders/{reference}`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Données invalides, détail dans `fields`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Trop de requêtes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "cle": {
                "type": "http",
                "scheme": "bearer",
                "description": "Clé API « fenuacompta_key_… », créée dans Réglages › Documentation API."
            }
        },
        "schemas": {
            "Erreur": {
                "type": "object",
                "required": [
                    "code",
                    "message"
                ],
                "properties": {
                    "code": {
                        "type": "string",
                        "examples": [
                            "validation"
                        ]
                    },
                    "message": {
                        "type": "string"
                    },
                    "fields": {
                        "type": "object",
                        "description": "Champ en erreur (notation pointée) => messages.",
                        "additionalProperties": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        }
    }
}