{
    "openapi": "3.1.0",
    "info": {
        "title": "Emargia : API d'émargement électronique",
        "version": "1.12.1",
        "description": "Émargement électronique avec dossier de preuve (service opéré par Codelpi) : codes et liens à usage unique par SMS, appareil reconnu, signature au doigt, preuves JSON, feuilles PDF scellées, chaînage des empreintes, horodatage RFC 3161, vérification publique.\n\n## Authentification\nChaque appel porte l'en-tête `X-Api-Key`. Deux clés par compte, **mêmes URL et mêmes réponses** :\n- `pr_...` **production** : les SMS partent réellement, les séances sont chaînées et horodatées, chaque présence et chaque document signé sont débités du crédit prépayé.\n- `ps_...` **bac à sable** : rien ne part et rien n'est débité. Les liens personnels sont renvoyés dans les réponses (`lien`), le code s'affiche sur la page de signature, et selon la configuration du service chaque lien ou code est aussi envoyé par mail au mail de contact du compte et/ou par SMS de test (préfixé TEST, depuis le compte de test du service, plafonné). Les documents sont marqués « DOCUMENT DE TEST », les webhooks sont envoyés avec `\"sandbox\":1`. Les données du bac à sable sont invisibles avec la clé de production, et inversement ; elles se vident d'un bouton depuis l'espace client.\n\n## Conventions\n- `POST` avec un corps JSON (`Content-Type: application/json`). Les champs absents sont ignorés ou prennent leur valeur par défaut.\n- Réponse `{\"succes\":1, ...}` ou `{\"succes\":0, \"erreur\":\"message lisible\", \"code\":\"code_stable\"}`.\n- Codes stables : `cle_invalide` (401), `solde_insuffisant` (402 : crédit prépayé épuisé, recharger le compte), `compte_suspendu` (403), `dossier_inconnu`, `seance_inconnue`, `participant_inconnu` (404), `seance_cloturee`, `deja_signe`, `trop_tot` (signature avant l'ouverture de la fenêtre), `hors_creneau` (séance terminée, option `hors_creneau` = refuser), `appareil` (téléphone déjà associé à une autre personne) (409), `limite` (429), `parametre`, `sans_mobile`, `sms` (400), `interne` (500).\n- Dates `AAAA-MM-JJ`, heures `HH:MM`, mobiles en national (`06 12 34 56 78`) ou international (`+33612345678`), normalisés en E.164 par le service. Un mobile non reconnu est renvoyé dans `mobiles_invalides` et la personne est traitée comme sans mobile.\n- Les URL courtes `/v1/<action>`, `/api/<action>` et la forme `api.php?action=<action>` sont équivalentes.\n\n## Connexion sans mot de passe (SSO)\nVotre logiciel appelle `sso/token` avec sa clé (le compte lui-même, ou un compte maître pour l'un de ses sous-comptes) en passant l'identité de l'utilisateur et la page voulue ; il reçoit une URL à usage unique valable 60 s et y redirige le navigateur. La session s'ouvre sur l'espace du compte, sans mot de passe ni code ; l'utilisateur (nom, mail) figure sur chaque ligne de journal de cette session. Rôle `lecture` : consultation et téléchargements seulement. La clé API ne transite jamais par le navigateur.\n\n## Prépayé\nPas d'abonnement : le compte achète des **recharges** en euros HT (paliers de la grille, `tarifs`). Une **présence** = une personne (stagiaire ou formateur) signée ou attestée sur une demi-journée (une séance « journée » compte deux). Une **signature** = un document signé, quel que soit le nombre de signataires ; débitée à l'envoi, remboursée si le document finit refusé, expiré ou annulé. Chaque recharge garde ses tarifs et se consomme avant la suivante (du plus ancien lot au plus récent) ; elle est valable 24 mois. SMS et horodatage inclus. Un éditeur (compte maître, clé de production) recharge ses sous-comptes avec `recharge/creer` : il est facturé avec sa remise revendeur. `solde/lire` donne le crédit, `solde/mouvements` la consommation détaillée (date, action, lot, tarif, solde avant et après). Quand le crédit passe sous le seuil du compte, webhook `solde.faible` (et mail) ; une action refusée faute de crédit renvoie `solde_insuffisant` (402).\n\n## Identification des participants et preuve\nPar défaut (`sms_mode` = `formation`), chaque participant reçoit **un seul SMS pour toute la formation** : lien personnel (ou code depuis le lien de séance / QR code). Une fois vérifié par SMS, son **téléphone est reconnu** (cookie + copie locale, 30 jours au plus, un téléphone pour une seule personne par formation) et il signe chaque demi-journée depuis le QR code ou son lien, sans nouveau SMS. Le formateur identifié peut renvoyer un SMS (3 par personne et par demi-journée) et afficher le QR code ; seul l'organisme corrige un numéro (`participant/modifier`, journalisé, téléphone oublié). Fenêtre de signature : `fenetre_avant` minutes avant le début (jamais avant), `fenetre_apres` minutes après la fin ; au-delà la signature est marquée « hors créneau » ou refusée (`hors_creneau`). Chaque signature enregistre la mention acceptée (version), l'écart avec la séance, le canal d'authentification, l'adresse IP et le navigateur ; l'ouverture des liens est journalisée. À la clôture, la séance produit la feuille PDF, un **dossier de preuve PDF** lisible (`seance/telecharger` type `preuve`) et les preuves JSON, tous couverts par l'empreinte chaînée et horodatée. Le journal du compte est chaîné (chaque ligne porte l'empreinte de la précédente) et son intégrité est contrôlée périodiquement.\n\n## Webhooks (service vers vous)\nSi `url_webhook` est renseignée sur le compte, chaque événement est envoyé en `POST` JSON avec les en-têtes `X-Presence-Evenement` et `X-Presence-Signature: sha256=<HMAC-SHA256 du corps avec secret_webhook>`. Répondez `200` ; sinon le service réessaie après 1 min, 5 min, 30 min, 2 h, 12 h puis abandonne. Les documents ne sont jamais joints : récupérez-les avec `seance/telecharger`. Voir la section **Webhooks** en bas de page."
    },
    "servers": [
        {
            "url": "https://app.emargia.fr/v1",
            "description": "URL courtes versionnées (réécriture Apache)"
        },
        {
            "url": "https://app.emargia.fr/api",
            "description": "URL courtes (réécriture Apache)"
        },
        {
            "url": "https://app.emargia.fr/api.php",
            "description": "Forme longue (api.php/<action>)"
        }
    ],
    "tags": [
        {
            "name": "Compte",
            "description": "État et réglages du compte"
        },
        {
            "name": "Prépayé",
            "description": "Crédit prépayé : solde, lots (recharges), consommation, recharges par l'éditeur, factures"
        },
        {
            "name": "Dossiers",
            "description": "Un dossier = une formation, avec ses séances et ses participants"
        },
        {
            "name": "Séances",
            "description": "Envoi des demandes de signature, absences, attestations, clôture"
        },
        {
            "name": "Documents",
            "description": "Feuilles PDF, preuves JSON, jetons d'horodatage"
        },
        {
            "name": "Signature de documents",
            "description": "Conventions, devis, contrats : plusieurs signataires, séquentiel ou non, code à usage unique, PDF final signé et scellé"
        },
        {
            "name": "Vérification",
            "description": "Sans clé : contrôle public d'une feuille"
        }
    ],
    "paths": {
        "/ping": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "Tester sa clé",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Clé valide",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "mode": {
                                            "type": "string",
                                            "enum": [
                                                "production",
                                                "sandbox"
                                            ]
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "date": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé invalide",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "ping"
            }
        },
        "/compte/infos": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "État du compte",
                "description": "Options effectives, crédit prépayé (`solde`, `solde_faible`, `presences_possibles`, `prochaine_expiration`, `recharge_par` : compte, editeur ou les_deux), compte maître (`maitre` : compte_id, nom, date_rattachement ; null pour un compte direct), `achat_direct`, consommation du mois (`presences_mois`, `signatures_mois`, `consommation_mois_ht`), nombre de dossiers.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "État",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "mode": {
                                            "type": "string"
                                        },
                                        "production_active": {
                                            "type": "integer"
                                        },
                                        "nom": {
                                            "type": "string"
                                        },
                                        "ref_client": {
                                            "type": "string"
                                        },
                                        "options": {
                                            "$ref": "#/components/schemas/Options"
                                        },
                                        "url_webhook": {
                                            "type": "string"
                                        },
                                        "sms_expediteur": {
                                            "type": "string"
                                        },
                                        "sms_configure": {
                                            "type": "integer"
                                        },
                                        "sms_solde": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ]
                                        },
                                        "nb_dossiers": {
                                            "type": "integer"
                                        },
                                        "nb_dossiers_ouverts": {
                                            "type": "integer"
                                        },
                                        "nb_dossiers_sandbox": {
                                            "type": "integer"
                                        },
                                        "assiduites_mois": {
                                            "type": "integer"
                                        },
                                        "assiduites_mois_precedent": {
                                            "type": "integer"
                                        },
                                        "nb_clotures": {
                                            "type": "integer"
                                        },
                                        "version": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé invalide",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "compte_infos"
            }
        },
        "/compte/modifier": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "Modifier le compte",
                "description": "Seuls les champs présents sont modifiés. `options` est fusionné avec les options existantes. `nda` (numéro de déclaration d'activité) est imprimé sur les feuilles et le dossier de preuve.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "nom": {
                                        "type": "string"
                                    },
                                    "mail_contact": {
                                        "type": "string"
                                    },
                                    "url_webhook": {
                                        "type": "string",
                                        "description": "Vide = plus de webhook"
                                    },
                                    "nda": {
                                        "type": "string",
                                        "maxLength": 20,
                                        "example": "11 75 12345 75"
                                    },
                                    "sms_cle_api": {
                                        "type": "string",
                                        "description": "Clé API SMS Partner du sous-compte du client (chiffrée côté service)"
                                    },
                                    "sms_expediteur": {
                                        "type": "string",
                                        "maxLength": 11
                                    },
                                    "options": {
                                        "$ref": "#/components/schemas/Options"
                                    }
                                }
                            },
                            "example": {
                                "url_webhook": "https://www.exemple.fr/webhook/presence.php",
                                "nda": "11 75 12345 75",
                                "options": {
                                    "envoi_auto": 1,
                                    "sms_mode": "formation",
                                    "fenetre_avant": 30,
                                    "fenetre_apres": 120
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Paramètre invalide",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "compte_modifier"
            }
        },
        "/compte/creer": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "Créer un compte ou un sous-compte",
                "description": "Avec la **clé maître** du service (exploitant) : compte principal. Avec la **clé de production d'un compte maître** : sous-compte rattaché (un par organisme géré), en production immédiatement ; son crédit est rechargé par le maître (`recharge/creer`), ou aussi par lui-même sur le site avec `achat_direct` = 1 (il est alors facturé en direct pour ses propres achats). Les clés ne sont renvoyées qu'ici, une seule fois : conservez-les chiffrées côté logiciel, avec le `compte_id`.\n\nExemple PHP (Galaxy, depuis la fiche de l\\'établissement) :\n```php\n$ch = curl_init('https://app.emargia.fr/v1/compte/creer');\ncurl_setopt_array($ch, array(CURLOPT_POST => 1, CURLOPT_RETURNTRANSFER => 1,\n    CURLOPT_HTTPHEADER => array('X-Api-Key: ' . $cle_maitre, 'Content-Type: application/json'),\n    CURLOPT_POSTFIELDS => json_encode(array('nom' => $etab['nom'], 'ref_client' => 'etab_' . $etab['id'], 'mail_contact' => $etab['mail'],\n        'url_webhook' => 'https://www.monlogiciel.fr/webhook/presence.php', 'achat_direct' => 1))));\n$r = json_decode(curl_exec($ch), true);\nif(!empty($r['succes'])){ /* enregistrer compte_id, cle_api, cle_sandbox, secret_webhook sur l\\'établissement */ }\n```",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "nom"
                                ],
                                "properties": {
                                    "nom": {
                                        "type": "string"
                                    },
                                    "ref_client": {
                                        "type": "string",
                                        "description": "Référence libre renvoyée dans chaque webhook"
                                    },
                                    "login": {
                                        "type": "string"
                                    },
                                    "mdp": {
                                        "type": "string",
                                        "minLength": 8
                                    },
                                    "mail_contact": {
                                        "type": "string"
                                    },
                                    "url_webhook": {
                                        "type": "string"
                                    },
                                    "sms_cle_api": {
                                        "type": "string"
                                    },
                                    "sms_expediteur": {
                                        "type": "string"
                                    },
                                    "achat_direct": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ],
                                        "default": 0,
                                        "description": "Sous-compte seulement : 1 = il achète aussi lui-même ses recharges sur le site"
                                    },
                                    "options": {
                                        "$ref": "#/components/schemas/Options"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Compte créé",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "cle_api": {
                                            "type": "string",
                                            "example": "pr_..."
                                        },
                                        "cle_sandbox": {
                                            "type": "string",
                                            "example": "ps_..."
                                        },
                                        "secret_webhook": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Clé maître requise",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "compte_creer"
            }
        },
        "/compte/rattacher": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "Rattacher un compte existant (code de rattachement)",
                "description": "Un organisme qui a déjà son propre compte Emargia le rattache à votre logiciel : il génère sur sa page « Mon compte » un **code de rattachement** (8 caractères, 30 minutes) et le saisit dans votre logiciel, qui appelle cet endpoint avec sa clé de production de compte maître. Le compte devient l'un de vos sous-comptes (`ref_client` = votre identifiant), garde par défaut l'achat direct de ses recharges (`achat_direct` = 1 ; 0 pour que vous soyez seul à le recharger), et reçoit de **nouvelles clés API**, renvoyées ici une seule fois (les anciennes cessent de fonctionner). Le client est prévenu par mail ; tout est journalisé des deux côtés. Ensuite : `dossier/creer`, `sso/token`, `recharge/creer` comme pour un sous-compte créé par vous.\n\nExemple PHP :\n```php\n$ch = curl_init('https://app.emargia.fr/v1/compte/rattacher');\ncurl_setopt_array($ch, array(CURLOPT_POST => 1, CURLOPT_RETURNTRANSFER => 1,\n    CURLOPT_HTTPHEADER => array('X-Api-Key: ' . $cle_maitre, 'Content-Type: application/json'),\n    CURLOPT_POSTFIELDS => json_encode(array('code' => $_POST['code_rattachement'], 'ref_client' => 'etab_' . $etab['id']))));\n$r = json_decode(curl_exec($ch), true);\nif(!empty($r['succes'])){ /* enregistrer compte_id, cle_api, cle_sandbox, secret_webhook sur l\\'établissement */ }\n```",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "code"
                                ],
                                "properties": {
                                    "code": {
                                        "type": "string",
                                        "example": "K7PX2M4Q"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "achat_direct": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ],
                                        "default": 1
                                    }
                                }
                            },
                            "example": {
                                "code": "K7PX2M4Q",
                                "ref_client": "etab_45"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Compte rattaché",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "nom": {
                                            "type": "string"
                                        },
                                        "ref_client": {
                                            "type": "string"
                                        },
                                        "cle_api": {
                                            "type": "string"
                                        },
                                        "cle_sandbox": {
                                            "type": "string"
                                        },
                                        "secret_webhook": {
                                            "type": "string"
                                        },
                                        "solde": {
                                            "type": "number"
                                        },
                                        "achat_direct": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Code inconnu ou expiré, compte déjà rattaché",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "compte_rattacher"
            }
        },
        "/compte/sous_comptes": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "Sous-comptes du compte maître",
                "description": "Un compte maître gère un sous-compte par organisme ; chacun a ses propres clés, son webhook, son crédit prépayé (`solde`).",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Liste",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "sous_comptes": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "compte_id": {
                                                        "type": "integer"
                                                    },
                                                    "nom": {
                                                        "type": "string"
                                                    },
                                                    "ref_client": {
                                                        "type": "string"
                                                    },
                                                    "actif": {
                                                        "type": "integer"
                                                    },
                                                    "cle_api_prefixe": {
                                                        "type": "string"
                                                    },
                                                    "sms_configure": {
                                                        "type": "integer"
                                                    },
                                                    "nb_dossiers": {
                                                        "type": "integer"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "compte_sous_comptes"
            }
        },
        "/tarifs": {
            "post": {
                "tags": [
                    "Prépayé"
                ],
                "summary": "Grille des recharges (sans clé)",
                "description": "Pour le site vitrine et les éditeurs : paliers de recharge avec le prix d'une présence et d'une signature de document. Prix HT, TVA en sus, SMS et horodatage inclus.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Grille",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "devise": {
                                            "type": "string"
                                        },
                                        "mode": {
                                            "type": "string",
                                            "example": "prepaye"
                                        },
                                        "tva_pct": {
                                            "type": "number"
                                        },
                                        "validite_mois": {
                                            "type": "integer"
                                        },
                                        "unites": {
                                            "type": "object"
                                        },
                                        "paliers": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Palier"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "tarifs"
            }
        },
        "/solde/lire": {
            "post": {
                "tags": [
                    "Prépayé"
                ],
                "summary": "Crédit prépayé du compte",
                "description": "Solde total utilisable (€ HT), lots actifs du plus ancien au plus récent avec leurs tarifs et leur date d'expiration, prix de la prochaine présence et de la prochaine signature, nombre d'actions possibles, recharges en attente de paiement, grille courante. Un compte maître peut interroger un sous-compte. `sandbox_gratuit` = 1 avec la clé bac à sable (rien n'est débité).",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "compte_id": {
                                        "type": "integer",
                                        "description": "Compte maître : un de vos sous-comptes (défaut : votre compte)"
                                    },
                                    "ref_client": {
                                        "type": "string",
                                        "description": "Ou sa référence client"
                                    }
                                }
                            },
                            "example": []
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Crédit",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "solde": {
                                            "type": "number",
                                            "example": 99.52
                                        },
                                        "devise": {
                                            "type": "string"
                                        },
                                        "seuil_alerte": {
                                            "type": "number"
                                        },
                                        "solde_faible": {
                                            "type": "integer"
                                        },
                                        "prix_prochaine_presence": {
                                            "type": [
                                                "number",
                                                "null"
                                            ]
                                        },
                                        "prix_prochaine_signature": {
                                            "type": [
                                                "number",
                                                "null"
                                            ]
                                        },
                                        "presences_possibles": {
                                            "type": "integer"
                                        },
                                        "signatures_possibles": {
                                            "type": "integer"
                                        },
                                        "prochaine_expiration": {
                                            "type": [
                                                "string",
                                                "null"
                                            ]
                                        },
                                        "validite_mois": {
                                            "type": "integer"
                                        },
                                        "recharge_par": {
                                            "type": "string",
                                            "enum": [
                                                "compte",
                                                "editeur"
                                            ]
                                        },
                                        "lots": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Lot"
                                            }
                                        },
                                        "en_attente": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Lot"
                                            }
                                        },
                                        "paliers": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Palier"
                                            }
                                        },
                                        "sandbox_gratuit": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "solde_lire"
            }
        },
        "/solde/mouvements": {
            "post": {
                "tags": [
                    "Prépayé"
                ],
                "summary": "Consommation détaillée",
                "description": "Chaque débit (présence, signature), crédit (achat, remboursement, ajustement) et expiration, avec le lot utilisé, le tarif appliqué, le solde du lot avant et après : de quoi justifier la consommation à votre client. Les présences portent `seance_id` et `participant_id`, les signatures `document_id`.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "compte_id": {
                                        "type": "integer",
                                        "description": "Compte maître : un de vos sous-comptes (défaut : votre compte)"
                                    },
                                    "ref_client": {
                                        "type": "string",
                                        "description": "Ou sa référence client"
                                    },
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "achat",
                                            "presence",
                                            "signature",
                                            "remboursement",
                                            "expiration",
                                            "ajustement"
                                        ]
                                    },
                                    "lot_id": {
                                        "type": "integer"
                                    },
                                    "depuis": {
                                        "type": "string",
                                        "example": "2026-11-01"
                                    },
                                    "jusqu": {
                                        "type": "string",
                                        "example": "2026-11-30"
                                    },
                                    "limite": {
                                        "type": "integer",
                                        "default": 200,
                                        "maximum": 5000
                                    }
                                }
                            },
                            "example": {
                                "depuis": "2026-11-01",
                                "jusqu": "2026-11-30"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Mouvements",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "mouvements": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Mouvement"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "solde_mouvements"
            }
        },
        "/recharge/creer": {
            "post": {
                "tags": [
                    "Prépayé"
                ],
                "summary": "Recharger un sous-compte (éditeur)",
                "description": "Avec la clé de production d'un compte maître : crédite immédiatement le sous-compte (`compte_id` ou `ref_client`) d'une recharge du montant indiqué, qui doit être un palier de la grille. Le lot garde les tarifs du palier et expire après la durée de validité. Le service vous facture le montant moins votre remise revendeur (prélevé sur votre mandat SEPA, sinon à régler par virement) ; vous facturez votre client vous-même. `ref_externe` (votre n° de commande ou de facture) rend l'appel idempotent : un second appel avec la même référence renvoie le lot existant (`existant` = 1). Un compte marqué revendeur peut aussi se recharger lui-même (sans `compte_id`). Bac à sable : rien n'est créé.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "montant"
                                ],
                                "properties": {
                                    "compte_id": {
                                        "type": "integer",
                                        "description": "Compte maître : un de vos sous-comptes (défaut : votre compte)"
                                    },
                                    "ref_client": {
                                        "type": "string",
                                        "description": "Ou sa référence client"
                                    },
                                    "montant": {
                                        "type": "number",
                                        "description": "Montant HT d'un palier actif (ex. 20, 50, 100, 250, 500, 1000)"
                                    },
                                    "ref_externe": {
                                        "type": "string"
                                    },
                                    "commentaire": {
                                        "type": "string"
                                    }
                                }
                            },
                            "example": {
                                "ref_client": "client_45|3",
                                "montant": 100,
                                "ref_externe": "FAC-2026-0412"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Recharge créditée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "existant": {
                                            "type": "integer"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "solde": {
                                            "type": "number"
                                        },
                                        "recharge": {
                                            "$ref": "#/components/schemas/Lot"
                                        },
                                        "facture": {
                                            "oneOf": [
                                                {
                                                    "$ref": "#/components/schemas/Facture"
                                                },
                                                {
                                                    "type": "null"
                                                }
                                            ]
                                        },
                                        "erreur_facture": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Montant hors grille, compte non autorisé",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "recharge_creer"
            }
        },
        "/recharge/lister": {
            "post": {
                "tags": [
                    "Prépayé"
                ],
                "summary": "Recharges (lots) du compte",
                "description": "Tous les lots du compte (ou d'un sous-compte), actifs, épuisés, expirés, en attente de paiement.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "compte_id": {
                                        "type": "integer",
                                        "description": "Compte maître : un de vos sous-comptes (défaut : votre compte)"
                                    },
                                    "ref_client": {
                                        "type": "string",
                                        "description": "Ou sa référence client"
                                    }
                                }
                            },
                            "example": []
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Lots",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "recharges": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Lot"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "recharge_lister"
            }
        },
        "/facture/lister": {
            "post": {
                "tags": [
                    "Prépayé"
                ],
                "summary": "Factures du service adressées au compte",
                "description": "Factures de recharge émises à ce compte (client direct : une par achat ; éditeur : une par recharge de sous-compte, avec la remise revendeur). Les PDF sont envoyés par mail depuis la facturation du service.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "statut": {
                                        "type": "string",
                                        "enum": [
                                            "a_regler",
                                            "prelevement_en_cours",
                                            "payee",
                                            "echec",
                                            "annulee"
                                        ]
                                    },
                                    "limite": {
                                        "type": "integer",
                                        "default": 200
                                    }
                                }
                            },
                            "example": []
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Factures",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "factures": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Facture"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "facture_lister"
            }
        },
        "/dossier/creer": {
            "post": {
                "tags": [
                    "Dossiers"
                ],
                "summary": "Créer un dossier avec ses séances et participants",
                "description": "Idempotent : si `ref_externe` existe déjà pour ce compte (dans le même mode), le dossier existant est renvoyé avec `existant:1` sans aucune modification. Pour mettre à jour, utilisez `dossier/synchroniser`.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/DossierEntree"
                            },
                            "example": {
                                "ref_externe": "ce_4521",
                                "intitule": "Habilitation électrique B0 H0V",
                                "reference": "CONV-2026-118",
                                "organisme": "Centre de formation Dupont",
                                "lieu": "Salle 2, 12 rue des Lilas, 13001 Marseille",
                                "date_debut": "2026-11-10",
                                "date_fin": "2026-11-10",
                                "options": {
                                    "envoi_auto": 1
                                },
                                "seances": [
                                    {
                                        "ref_externe": "prev_889",
                                        "date": "2026-11-10",
                                        "demi_journee": "matin",
                                        "heure_debut": "09:00",
                                        "heure_fin": "12:30"
                                    },
                                    {
                                        "ref_externe": "prev_890",
                                        "date": "2026-11-10",
                                        "demi_journee": "apres_midi",
                                        "heure_debut": "13:30",
                                        "heure_fin": "17:00",
                                        "participants": [
                                            "stag_120",
                                            "int_7"
                                        ]
                                    }
                                ],
                                "participants": [
                                    {
                                        "ref_externe": "stag_120",
                                        "role": "stagiaire",
                                        "nom": "Durand",
                                        "prenom": "Marie",
                                        "mobile": "06 12 34 56 78",
                                        "mail": "m.durand@exemple.fr",
                                        "societe": "SARL Martin"
                                    },
                                    {
                                        "ref_externe": "stag_121",
                                        "role": "stagiaire",
                                        "nom": "Petit",
                                        "prenom": "Jean",
                                        "mobile": "+33 7 11 22 33 44"
                                    },
                                    {
                                        "ref_externe": "int_7",
                                        "role": "formateur",
                                        "nom": "Bernard",
                                        "prenom": "Luc",
                                        "mobile": "0611223344",
                                        "mail": "luc@exemple.fr"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Dossier",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DossierCreation"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "ref_externe ou intitule manquant, payload illisible",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "dossier_creer"
            }
        },
        "/dossier/synchroniser": {
            "post": {
                "tags": [
                    "Dossiers"
                ],
                "summary": "Synchroniser un dossier",
                "description": "Même payload que la création. Ajoute les nouvelles séances et participants, met à jour ce qui n'est pas signé (horaires, mobiles, mails), annule les séances disparues sans signature, désactive les participants disparus (leurs signatures restent). Ne touche jamais une séance clôturée ni une signature ; les écarts sont listés dans `rapport.ignores` et `rapport.conflits`.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/DossierEntree"
                            },
                            "example": {
                                "ref_externe": "ce_4521",
                                "intitule": "Habilitation électrique B0 H0V",
                                "reference": "CONV-2026-118",
                                "organisme": "Centre de formation Dupont",
                                "lieu": "Salle 2, 12 rue des Lilas, 13001 Marseille",
                                "date_debut": "2026-11-10",
                                "date_fin": "2026-11-10",
                                "options": {
                                    "envoi_auto": 1
                                },
                                "seances": [
                                    {
                                        "ref_externe": "prev_889",
                                        "date": "2026-11-10",
                                        "demi_journee": "matin",
                                        "heure_debut": "09:00",
                                        "heure_fin": "12:30"
                                    },
                                    {
                                        "ref_externe": "prev_890",
                                        "date": "2026-11-10",
                                        "demi_journee": "apres_midi",
                                        "heure_debut": "13:30",
                                        "heure_fin": "17:00",
                                        "participants": [
                                            "stag_120",
                                            "int_7"
                                        ]
                                    }
                                ],
                                "participants": [
                                    {
                                        "ref_externe": "stag_120",
                                        "role": "stagiaire",
                                        "nom": "Durand",
                                        "prenom": "Marie",
                                        "mobile": "06 12 34 56 78",
                                        "mail": "m.durand@exemple.fr",
                                        "societe": "SARL Martin"
                                    },
                                    {
                                        "ref_externe": "stag_121",
                                        "role": "stagiaire",
                                        "nom": "Petit",
                                        "prenom": "Jean",
                                        "mobile": "+33 7 11 22 33 44"
                                    },
                                    {
                                        "ref_externe": "int_7",
                                        "role": "formateur",
                                        "nom": "Bernard",
                                        "prenom": "Luc",
                                        "mobile": "0611223344",
                                        "mail": "luc@exemple.fr"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Dossier et rapport",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DossierCreation"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Payload invalide",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "dossier_synchroniser"
            }
        },
        "/dossier/lire": {
            "post": {
                "tags": [
                    "Dossiers"
                ],
                "summary": "Lire un dossier",
                "description": "État complet pour l'affichage : séances avec compteurs, avertissements, lien de séance et clôture (dont `preuve_pdf_url`) ; participants avec mobile masqué, origine du numéro, SMS envoyés, téléphone reconnu et statut par séance ; SMS du dossier et doublons de mobile.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "dossier_id": {
                                        "type": "integer"
                                    },
                                    "ref_externe": {
                                        "type": "string",
                                        "description": "Si dossier_id est absent"
                                    }
                                }
                            },
                            "example": {
                                "ref_externe": "ce_4521"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Dossier",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DossierLecture"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Dossier inconnu",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "dossier_lire"
            }
        },
        "/participant/modifier": {
            "post": {
                "tags": [
                    "Dossiers"
                ],
                "summary": "Corriger un participant (numéro de mobile, mail, société...)",
                "description": "Réservé à l'organisme (le formateur ne peut que renvoyer le SMS). `participant_id`, ou `dossier_id` / `ref_externe` du dossier + `participant_ref`. Nom, prénom et rôle ne changent plus après une signature (code `deja_signe`). Un mobile corrigé est journalisé (ancien et nouveau masqués, auteur) et le téléphone reconnu de la personne est oublié : renvoyez-lui un lien avec `seance/envoyer_demandes` (`renvoyer` = 1). `avertissements` signale un mobile partagé avec un autre participant.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "participant_id": {
                                        "type": "integer"
                                    },
                                    "dossier_id": {
                                        "type": "integer"
                                    },
                                    "ref_externe": {
                                        "type": "string",
                                        "description": "Référence du dossier (avec participant_ref)"
                                    },
                                    "participant_ref": {
                                        "type": "string"
                                    },
                                    "mobile": {
                                        "type": "string"
                                    },
                                    "mail": {
                                        "type": "string"
                                    },
                                    "societe": {
                                        "type": "string"
                                    },
                                    "nom": {
                                        "type": "string"
                                    },
                                    "prenom": {
                                        "type": "string"
                                    },
                                    "role": {
                                        "type": "string",
                                        "enum": [
                                            "stagiaire",
                                            "formateur"
                                        ]
                                    },
                                    "actif": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    }
                                }
                            },
                            "example": {
                                "dossier_id": 17,
                                "participant_ref": "stag_120",
                                "mobile": "06 12 34 56 79"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "modifie": {
                                            "type": "integer"
                                        },
                                        "participant_id": {
                                            "type": "integer"
                                        },
                                        "avertissements": {
                                            "type": "array",
                                            "items": {
                                                "type": "string"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Participant inconnu",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Déjà signé : identité figée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "participant_modifier"
            }
        },
        "/sso/token": {
            "post": {
                "tags": [
                    "Compte"
                ],
                "summary": "Ouvrir l'espace client sans mot de passe (SSO)",
                "description": "Renvoie une URL à usage unique (`ttl` secondes, 60 par défaut, 300 au plus) vers laquelle rediriger le navigateur de l'utilisateur : la session s'ouvre sur l'espace du compte de la clé, ou du sous-compte indiqué (`compte_id` ou `ref_client`) avec la clé du compte maître. `mail` et/ou `nom` identifient l'utilisateur dans le journal (obligatoire : l'un des deux). `role` : `gestion` (défaut) ou `lecture` (aucune modification possible). `page` : tableau (défaut), dossiers, dossier (+ `dossier_id` ou `ref_externe`), seance (+ `seance_id`), documents, document (+ `document_id`), recharges, compte, journal, sous_comptes ; `sandbox` = 1 ouvre la liste des dossiers du bac à sable. Chaque jeton créé et chaque connexion sont journalisés. Appel serveur à serveur uniquement : la clé ne doit jamais être exposée au navigateur.\n\nExemple PHP (bouton « Ouvrir l\\'émargement » dans votre logiciel) :\n```php\n$ch = curl_init('https://app.emargia.fr/v1/sso/token');\ncurl_setopt_array($ch, array(CURLOPT_POST => 1, CURLOPT_RETURNTRANSFER => 1, CURLOPT_TIMEOUT => 10,\n    CURLOPT_HTTPHEADER => array('X-Api-Key: ' . $cle_maitre, 'Content-Type: application/json'),\n    CURLOPT_POSTFIELDS => json_encode(array('ref_client' => 'etab_' . $etab['id'],\n        'mail' => $utilisateur['mail'], 'nom' => $utilisateur['prenom'] . ' ' . $utilisateur['nom'],\n        'role' => $utilisateur['droit_modif'] ? 'gestion' : 'lecture',\n        'page' => 'dossier', 'ref_externe' => 'ce_' . $convention_element_id))));\n$r = json_decode(curl_exec($ch), true);\nif(!empty($r['succes'])){ header('Location: ' . $r['url']); exit; }\necho 'Service indisponible : ' . (isset($r['erreur']) ? $r['erreur'] : 'pas de réponse');\n```\n`ref_client` (ou `compte_id`) désigne le sous-compte ; avec la clé du compte lui-même, omettez-le.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "mail": {
                                        "type": "string"
                                    },
                                    "nom": {
                                        "type": "string"
                                    },
                                    "role": {
                                        "type": "string",
                                        "enum": [
                                            "gestion",
                                            "lecture"
                                        ],
                                        "default": "gestion"
                                    },
                                    "page": {
                                        "type": "string",
                                        "enum": [
                                            "tableau",
                                            "dossiers",
                                            "dossier",
                                            "seance",
                                            "documents",
                                            "document",
                                            "recharges",
                                            "compte",
                                            "journal",
                                            "sous_comptes"
                                        ],
                                        "default": "tableau"
                                    },
                                    "dossier_id": {
                                        "type": "integer"
                                    },
                                    "ref_externe": {
                                        "type": "string"
                                    },
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "document_id": {
                                        "type": "integer"
                                    },
                                    "sandbox": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    },
                                    "ttl": {
                                        "type": "integer",
                                        "default": 60
                                    }
                                }
                            },
                            "example": {
                                "ref_client": "client_45|3",
                                "mail": "marie@of-exemple.fr",
                                "nom": "Marie Dupont",
                                "role": "gestion",
                                "page": "dossier",
                                "ref_externe": "ce_4521"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "URL à ouvrir",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "url": {
                                            "type": "string",
                                            "example": "https://app.emargia.fr/sso/Kq8…"
                                        },
                                        "expire": {
                                            "type": "string"
                                        },
                                        "compte_id": {
                                            "type": "integer"
                                        },
                                        "page": {
                                            "type": "object"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Utilisateur non identifié (mail ou nom)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Compte suspendu",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "sso_token"
            }
        },
        "/compte/exporter": {
            "post": {
                "tags": [
                    "Documents"
                ],
                "summary": "Restitution : archive zip de toutes les séances clôturées du compte, par période",
                "description": "Feuilles PDF, dossiers de preuve, preuves JSON, jetons d'horodatage, une synthèse par formation et un index CSV des empreintes : tout est lisible sans le service. `depuis` et `jusqu` (AAAA-MM-JJ, dates de clôture) bornent la période ; absents : tout. Chaque export est inscrit au journal des accès. Pour un gros compte, préférez des périodes (un trimestre, une année).",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "depuis": {
                                        "type": "string",
                                        "example": "2026-01-01"
                                    },
                                    "jusqu": {
                                        "type": "string",
                                        "example": "2026-03-31"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Le zip",
                        "content": {
                            "application/zip": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    }
                },
                "operationId": "compte_exporter"
            }
        },
        "/dossier/telecharger": {
            "post": {
                "tags": [
                    "Documents"
                ],
                "summary": "Archive zip de toutes les séances clôturées d'un dossier",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "dossier_id"
                                ],
                                "properties": {
                                    "dossier_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fichier zip (feuilles PDF, preuves JSON, jetons .tsr, images de signature)",
                        "content": {
                            "application/zip": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Aucune séance clôturée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "dossier_telecharger"
            }
        },
        "/dossier/certificats": {
            "post": {
                "tags": [
                    "Documents"
                ],
                "summary": "Certificat(s) de réalisation (PDF ou zip)",
                "description": "Un certificat par stagiaire établi à partir des émargements : durée prévue, durée réalisée (présences signées ou attestées sur les séances clôturées), assiduité, détail des séances. Avec `participant_id` ou `participant_ref` : le PDF de ce stagiaire ; sinon un zip de tous les stagiaires. Chaque certificat est scellé (empreinte imprimée, QR de vérification, fichier conservé) et regénéré seulement si les émargements ont changé. Options `modele_certificat`, `signataire`, `couleur`, `mentions` du compte ou du dossier.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "dossier_id": {
                                        "type": "integer"
                                    },
                                    "ref_externe": {
                                        "type": "string",
                                        "description": "À la place de dossier_id"
                                    },
                                    "participant_id": {
                                        "type": "integer"
                                    },
                                    "participant_ref": {
                                        "type": "string",
                                        "description": "ref_externe du stagiaire, à la place de participant_id"
                                    }
                                }
                            },
                            "example": {
                                "dossier_id": 12,
                                "participant_ref": "stag_120"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "PDF (un stagiaire) ou zip (tous), en-tête X-Empreinte-SHA256",
                        "content": {
                            "application/pdf": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "application/zip": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Aucune séance clôturée, formateur",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Dossier ou participant inconnu",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "dossier_certificats"
            }
        },
        "/document/creer": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "Proposer un PDF à la signature",
                "description": "Le PDF est passé en base64 dans `pdf` (ou en `multipart/form-data` dans `fichier`, avec `signataires` en JSON). Chaque signataire reçoit un lien personnel (mail, ou SMS s'il n'a pas de mail), lit le document, consent, reçoit un code à usage unique (SMS, ou mail sans mobile), dessine sa signature. `sequentiel` : un signataire à la fois dans l'ordre `ordre`. `emplacement` : `page` (une page des signatures est ajoutée au PDF, défaut) ou `positions` (signature apposée à l'endroit indiqué pour chaque signataire). Quand tous ont signé : PDF final (signatures, page de preuve, QR de vérification, cachet serveur si configuré), preuves JSON, chaîne et horodatage, webhook `document.signe`, mail aux signataires. `envoi_auto` = 0 : rien n'est envoyé, les liens sont renvoyés pour que vous les diffusiez. Bac à sable : liens et codes renvoyés, aucun envoi, filigrane.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "titre",
                                    "pdf",
                                    "signataires"
                                ],
                                "properties": {
                                    "titre": {
                                        "type": "string"
                                    },
                                    "pdf": {
                                        "type": "string",
                                        "description": "PDF en base64 (20 Mo max) ; les PDF compressés sont réécrits automatiquement si qpdf ou Ghostscript est installé"
                                    },
                                    "nom_fichier": {
                                        "type": "string"
                                    },
                                    "message": {
                                        "type": "string",
                                        "description": "Message aux signataires"
                                    },
                                    "ref_externe": {
                                        "type": "string",
                                        "description": "Votre identifiant (devis, convention...), unique par compte et par mode"
                                    },
                                    "signataires": {
                                        "type": "array",
                                        "items": {
                                            "$ref": "#/components/schemas/SignataireEntree"
                                        }
                                    },
                                    "champs": {
                                        "type": "array",
                                        "items": {
                                            "$ref": "#/components/schemas/Champ"
                                        },
                                        "description": "Éléments à collecter avant la signature (formulaire séparé, valeurs dans les preuves, la page de preuve, document/lire et les webhooks)"
                                    },
                                    "sequentiel": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    },
                                    "date_limite": {
                                        "type": "string",
                                        "example": "2026-12-15",
                                        "description": "Défaut : + 30 jours"
                                    },
                                    "relance_jours": {
                                        "type": "integer",
                                        "description": "Relance des non-signés tous les N jours (0 = jamais, défaut 3)"
                                    },
                                    "max_relances": {
                                        "type": "integer",
                                        "default": 3
                                    },
                                    "envoi_auto": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ],
                                        "default": 1
                                    },
                                    "emplacement": {
                                        "type": "string",
                                        "enum": [
                                            "page",
                                            "positions"
                                        ],
                                        "default": "page"
                                    }
                                }
                            },
                            "example": {
                                "titre": "Convention de formation CONV-2026-118",
                                "ref_externe": "conv_118",
                                "pdf": "JVBERi0xLjQK...",
                                "message": "Merci de signer la convention avant le début de la formation.",
                                "sequentiel": 1,
                                "signataires": [
                                    {
                                        "ref_externe": "client",
                                        "nom": "Martin",
                                        "prenom": "Paul",
                                        "mail": "p.martin@exemple.fr",
                                        "mobile": "06 12 34 56 78",
                                        "qualite": "Gérant",
                                        "societe": "SARL Martin",
                                        "ordre": 1
                                    },
                                    {
                                        "ref_externe": "of",
                                        "nom": "Dupont",
                                        "prenom": "Marie",
                                        "mail": "direction@of.fr",
                                        "mobile": "06 98 76 54 32",
                                        "qualite": "Directrice",
                                        "ordre": 2
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Document créé",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Document"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "PDF illisible, signataire sans mail ni mobile, référence en double",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "document_creer"
            }
        },
        "/document/lire": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "État d'un document et de ses signataires",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "document_id": {
                                        "type": "integer"
                                    },
                                    "ref_externe": {
                                        "type": "string",
                                        "description": "À la place de document_id"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Document",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Document"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Document inconnu",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "document_lire"
            }
        },
        "/document/lister": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "Liste des documents du compte (mode de la clé)",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "statut": {
                                        "type": "string",
                                        "enum": [
                                            "en_cours",
                                            "signe",
                                            "refuse",
                                            "expire",
                                            "annule"
                                        ]
                                    },
                                    "recherche": {
                                        "type": "string"
                                    },
                                    "limite": {
                                        "type": "integer",
                                        "default": 200
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Liste",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "documents": {
                                            "type": "array",
                                            "items": {
                                                "type": "object"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "document_lister"
            }
        },
        "/document/relancer": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "Relancer les signataires en attente (ou un seul)",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "document_id"
                                ],
                                "properties": {
                                    "document_id": {
                                        "type": "integer"
                                    },
                                    "signataire_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "relances": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Document clos",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "document_relancer"
            }
        },
        "/document/annuler": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "Annuler une demande de signature en cours",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "document_id"
                                ],
                                "properties": {
                                    "document_id": {
                                        "type": "integer"
                                    },
                                    "motif": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "statut": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Document déjà clos",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "document_annuler"
            }
        },
        "/document/supprimer": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "Supprimer un document (bac à sable, ou jamais signé)",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "document_id"
                                ],
                                "properties": {
                                    "document_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Document signé ou portant des signatures",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "document_supprimer"
            }
        },
        "/document/telecharger": {
            "post": {
                "tags": [
                    "Signature de documents"
                ],
                "summary": "Télécharger l'original, le PDF signé, les preuves, le jeton ou un zip",
                "description": "Accepte aussi `GET ?action=document/telecharger&document_id=..&type=..`. En-tête `X-Empreinte-SHA256`.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "document_id"
                                ],
                                "properties": {
                                    "document_id": {
                                        "type": "integer"
                                    },
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "original",
                                            "final",
                                            "preuves",
                                            "tsr",
                                            "zip",
                                            "collecte"
                                        ],
                                        "default": "final"
                                    },
                                    "signataire_id": {
                                        "type": "integer",
                                        "description": "Type collecte"
                                    },
                                    "cle": {
                                        "type": "string",
                                        "description": "Type collecte : clé du champ fichier"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Le fichier",
                        "content": {
                            "application/pdf": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            },
                            "application/zip": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Document non signé",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "document_telecharger"
            }
        },
        "/dossier/synthese": {
            "post": {
                "tags": [
                    "Documents"
                ],
                "summary": "Feuille de synthèse du dossier (PDF)",
                "description": "Grille participants × séances de toute la formation, avec les empreintes des séances clôturées. Document récapitulatif non scellé : les pièces probantes restent les feuilles de séance.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "dossier_id"
                                ],
                                "properties": {
                                    "dossier_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "PDF",
                        "content": {
                            "application/pdf": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Aucune séance",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "dossier_synthese"
            }
        },
        "/statut": {
            "post": {
                "tags": [
                    "Vérification"
                ],
                "summary": "État du service (supervision, sans clé)",
                "description": "HTTP 200 si tout va bien, 503 sinon (cron absent depuis 30 min, stockage non inscriptible, disque plein). Aucune donnée sensible.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "État",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "ok": {
                                            "type": "integer"
                                        },
                                        "version": {
                                            "type": "string"
                                        },
                                        "cron_age_secondes": {
                                            "type": [
                                                "integer",
                                                "null"
                                            ]
                                        },
                                        "horodatages_en_attente": {
                                            "type": "integer"
                                        },
                                        "webhooks_en_attente": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Service dégradé"
                    }
                },
                "operationId": "statut"
            }
        },
        "/seance/envoyer_demandes": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Envoyer les demandes de signature (SMS)",
                "description": "Un lien personnel par participant concerné (1 SMS). En mode `formation` (défaut), le lien couvre toutes les séances de la formation et n'est pas renvoyé aux participants dont le téléphone est déjà reconnu (`etat` = appareil_reconnu) : ils signent depuis le QR code ou leur lien. Avec `sms_mode` = jour, le lien couvre les séances du jour. `renvoyer` = 1 force un nouveau lien (numéro corrigé, SMS non reçu). Au-delà de 3 SMS pour une même personne sur une formation, l'organisme est prévenu par mail (numéro faux ?). Vérifie d'abord le crédit du compte. En bac à sable, les liens sont renvoyés dans la réponse au lieu d'être envoyés.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "participants": {
                                        "type": "array",
                                        "items": {
                                            "type": "integer"
                                        },
                                        "description": "Ids ; absent : tous ceux qui n'ont pas signé"
                                    },
                                    "renvoyer": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ],
                                        "description": "1 : nouveau lien même si un lien valide existe (l'ancien est périmé)"
                                    }
                                }
                            },
                            "example": {
                                "seance_id": 41
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Rapport par participant",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Envoi"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Crédit SMS insuffisant, aucune clé SMS",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Séance clôturée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_envoyer_demandes"
            }
        },
        "/seance/lien": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Lien de séance (tablette du formateur, QR code, mail)",
                "description": "Le participant dont le téléphone est reconnu signe directement ; sinon il choisit son nom, reçoit un code par SMS et signe (son téléphone devient reconnu). Ouvert `fenetre_avant` minutes avant le début, jusqu'à la clôture. Le formateur identifié y suit la séance, renvoie les SMS, affiche le QR code plein écran et clôture. `url_qr` renvoie l'image PNG du QR code.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Lien",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "seance_id": {
                                            "type": "integer"
                                        },
                                        "url": {
                                            "type": "string"
                                        },
                                        "url_qr": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Séance clôturée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_lien"
            }
        },
        "/seance/envoi_auto": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Activer ou non l'envoi automatique à l'heure de début",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "actif"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer",
                                        "description": "Une séance..."
                                    },
                                    "dossier_id": {
                                        "type": "integer",
                                        "description": "... ou toutes les séances non envoyées du dossier"
                                    },
                                    "actif": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    },
                                    "minutes_avant": {
                                        "type": "integer"
                                    }
                                }
                            },
                            "example": {
                                "dossier_id": 17,
                                "actif": 1,
                                "minutes_avant": 10
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "seances": {
                                            "type": "integer",
                                            "description": "Nombre de séances modifiées"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_envoi_auto"
            }
        },
        "/seance/absent": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Poser ou retirer une absence, avec justificatif éventuel",
                "description": "Le justificatif (photo JPEG, PNG, WebP ou fichier PDF, 8 Mo max) est passé en base64 dans `justificatif` (data URL acceptée) ou en `multipart/form-data` dans le champ `fichier`. Les photos sont ramenées à 1600 px en JPEG, orientation corrigée. Son empreinte SHA-256 entre dans les preuves de la séance et la feuille mentionne « justificatif joint ». `toutes_seances` : même absence (et même justificatif) sur les autres séances non signées et non clôturées de ce participant dans le dossier.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id",
                                    "participant_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "participant_id": {
                                        "type": "integer"
                                    },
                                    "motif": {
                                        "type": "string"
                                    },
                                    "retirer": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    },
                                    "justificatif": {
                                        "type": "string",
                                        "description": "Photo ou PDF en base64"
                                    },
                                    "justificatif_nom": {
                                        "type": "string",
                                        "description": "Nom d'origine, informatif"
                                    },
                                    "toutes_seances": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    }
                                }
                            },
                            "example": {
                                "seance_id": 41,
                                "participant_id": 203,
                                "motif": "Arrêt maladie",
                                "justificatif": "data:image/jpeg;base64,/9j/4AAQ...",
                                "toutes_seances": 1
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "statut": {
                                            "type": "string"
                                        },
                                        "justificatif": {
                                            "type": "integer"
                                        },
                                        "justificatif_empreinte": {
                                            "type": "string"
                                        },
                                        "seances": {
                                            "type": "array",
                                            "items": {
                                                "type": "integer"
                                            },
                                            "description": "Séances concernées"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Déjà signé ou séance clôturée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_absent"
            }
        },
        "/seance/justificatif": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Joindre un justificatif à une absence déjà enregistrée (même après clôture)",
                "description": "Même formats que `seance/absent`. Un seul justificatif par absence : le précédent est remplacé. Avant clôture, son empreinte entre dans les preuves ; après clôture, il est conservé avec la séance et tracé au journal mais reste hors des preuves scellées (`apres_cloture: 1`), la feuille ne change pas. `toutes_seances` : même document sur toutes les absences enregistrées du participant dans le dossier. Webhook `absence.justificatif`.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id",
                                    "participant_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "participant_id": {
                                        "type": "integer"
                                    },
                                    "justificatif": {
                                        "type": "string",
                                        "description": "Photo ou PDF en base64 (ou champ multipart `fichier`)"
                                    },
                                    "justificatif_nom": {
                                        "type": "string"
                                    },
                                    "toutes_seances": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ]
                                    }
                                }
                            },
                            "example": {
                                "seance_id": 41,
                                "participant_id": 203,
                                "justificatif": "JVBERi0xLjQK...",
                                "justificatif_nom": "arret.pdf"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "empreinte": {
                                            "type": "string"
                                        },
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "jpg",
                                                "pdf"
                                            ]
                                        },
                                        "apres_cloture": {
                                            "type": "integer"
                                        },
                                        "seances": {
                                            "type": "array",
                                            "items": {
                                                "type": "integer"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Pas d'absence enregistrée, format non reconnu",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_justificatif"
            }
        },
        "/seance/attester": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Attester une présence sans code (par un formateur)",
                "description": "Nécessite l'option `attestation_formateur` sur le compte ou le dossier et un motif. Mentionné comme tel sur la feuille et dans les preuves, compté à part.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id",
                                    "participant_id",
                                    "atteste_par",
                                    "motif"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "participant_id": {
                                        "type": "integer"
                                    },
                                    "atteste_par": {
                                        "type": "integer",
                                        "description": "participant_id d'un formateur du dossier"
                                    },
                                    "motif": {
                                        "type": "string"
                                    }
                                }
                            },
                            "example": {
                                "seance_id": 41,
                                "participant_id": 203,
                                "atteste_par": 210,
                                "motif": "Mobile oublié, présence constatée en salle"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Fait",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "statut": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Option inactive, motif manquant, atteste_par n'est pas formateur",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_attester"
            }
        },
        "/seance/recapitulatif": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Récapitulatif avant clôture",
                "description": "Compteurs, participants sans signature (pour décider : laisser, absent, attester) et formateurs du dossier.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Récapitulatif",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "compteurs": {
                                            "$ref": "#/components/schemas/Compteurs"
                                        },
                                        "attendus": {
                                            "type": "array",
                                            "items": {
                                                "type": "object"
                                            }
                                        },
                                        "formateurs": {
                                            "type": "array",
                                            "items": {
                                                "type": "object"
                                            }
                                        },
                                        "options": {
                                            "$ref": "#/components/schemas/Options"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_recapitulatif"
            }
        },
        "/seance/cloturer": {
            "post": {
                "tags": [
                    "Séances"
                ],
                "summary": "Clôturer une séance",
                "description": "Fige la séance : fichier de preuves JSON, feuille PDF, empreintes chaînées, horodatage RFC 3161, webhook `seance.cloturee`. Irréversible : plus aucune signature ni synchronisation ensuite. Les décisions sur les participants sans signature sont appliquées avant.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "participant_id": {
                                        "type": "integer",
                                        "description": "Formateur qui atteste, si des décisions \"attester\" sont présentes"
                                    },
                                    "decisions": {
                                        "type": "object",
                                        "description": "participant_id => {action: laisser | absent | attester, motif}",
                                        "additionalProperties": {
                                            "type": "object",
                                            "properties": {
                                                "action": {
                                                    "type": "string",
                                                    "enum": [
                                                        "laisser",
                                                        "absent",
                                                        "attester"
                                                    ]
                                                },
                                                "motif": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                }
                            },
                            "example": {
                                "seance_id": 41,
                                "participant_id": 210,
                                "decisions": {
                                    "203": {
                                        "action": "absent",
                                        "motif": "Malade"
                                    },
                                    "204": {
                                        "action": "attester",
                                        "motif": "Mobile oublié"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Documents scellés",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "allOf": [
                                        {
                                            "$ref": "#/components/schemas/Cloture"
                                        },
                                        {
                                            "type": "object",
                                            "properties": {
                                                "succes": {
                                                    "type": "integer",
                                                    "enum": [
                                                        1
                                                    ],
                                                    "description": "Toujours 1 en cas de succès"
                                                },
                                                "seance_id": {
                                                    "type": "integer"
                                                },
                                                "sandbox": {
                                                    "type": "integer"
                                                },
                                                "date_cloture": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Déjà clôturée",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_cloturer"
            }
        },
        "/seance/telecharger": {
            "post": {
                "tags": [
                    "Documents"
                ],
                "summary": "Télécharger un document d'une séance",
                "description": "Le fichier est renvoyé tel quel, avec `Content-Disposition` et `X-Empreinte-SHA256`. Accepte aussi `GET ?action=seance/telecharger&seance_id=..&type=..`. `pdf` (feuille), `preuve` (dossier de preuve PDF lisible : séance, participants, authentification de chaque signature, chronologie, empreintes), `preuves` (JSON), `tsr` et `zip` exigent une séance clôturée ; `justificatif` (avec `participant_id`) renvoie la photo JPEG ou le PDF du justificatif d'absence, clôturée ou non. Chaque téléchargement est inscrit au journal des accès.",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    },
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "pdf",
                                            "preuve",
                                            "preuves",
                                            "tsr",
                                            "zip",
                                            "justificatif",
                                            "collecte"
                                        ],
                                        "default": "pdf"
                                    },
                                    "participant_id": {
                                        "type": "integer",
                                        "description": "Avec type = justificatif ou collecte"
                                    },
                                    "cle": {
                                        "type": "string",
                                        "description": "Type collecte : clé du champ fichier (seance_id ignoré)"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Le fichier",
                        "content": {
                            "application/pdf": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            },
                            "application/zip": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            },
                            "image/jpeg": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Séance non clôturée, pas de justificatif",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Erreur"
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_telecharger"
            }
        },
        "/seance/preuve": {
            "post": {
                "tags": [
                    "Documents"
                ],
                "summary": "Preuves JSON d'une séance clôturée",
                "security": [
                    {
                        "cleApi": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "seance_id"
                                ],
                                "properties": {
                                    "seance_id": {
                                        "type": "integer"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Preuves (participants, signatures avec mobile masqué et empreinte salée, jetons, journal, horodatages UTC et locaux)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "succes": {
                                            "type": "integer",
                                            "enum": [
                                                1
                                            ],
                                            "description": "Toujours 1 en cas de succès"
                                        },
                                        "preuves": {
                                            "type": "object"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "operationId": "seance_preuve"
            }
        },
        "/verifier": {
            "post": {
                "tags": [
                    "Vérification"
                ],
                "summary": "Vérifier une feuille ou un certificat (sans clé)",
                "description": "Par empreinte (de séance, du PDF ou des preuves : celle imprimée sur la feuille ; ou celle imprimée sur un certificat de réalisation) ou par dépôt du fichier PDF (`multipart/form-data`, champ `fichier`). N'expose aucune donnée nominative. Un certificat reconnu renvoie `certificat: 1` avec durées prévue et réalisée. La page publique `verifier.php` fait la même chose pour un humain.",
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "empreinte": {
                                        "type": "string",
                                        "minLength": 64,
                                        "maxLength": 64
                                    }
                                }
                            }
                        },
                        "multipart/form-data": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "fichier": {
                                        "type": "string",
                                        "format": "binary"
                                    },
                                    "empreinte": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Résultat",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Verification"
                                }
                            }
                        }
                    }
                },
                "operationId": "verifier",
                "security": []
            }
        }
    },
    "webhooks": {
        "signature.faite": {
            "post": {
                "summary": "Une présence a changé d'état (signée, attestée, absent, ou retour à attendue) ; `reponses` : éléments collectés à cette signature",
                "description": "En-têtes : `X-Presence-Evenement: signature.faite`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "participant": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "role": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "reponses": {
                                        "type": "array",
                                        "items": {
                                            "$ref": "#/components/schemas/Reponse"
                                        }
                                    },
                                    "signature": {
                                        "type": "object",
                                        "properties": {
                                            "statut": {
                                                "type": "string",
                                                "enum": [
                                                    "signee",
                                                    "attestee",
                                                    "absent",
                                                    "attendue"
                                                ]
                                            },
                                            "canal": {
                                                "type": "string",
                                                "enum": [
                                                    "sms_lien",
                                                    "sms_code",
                                                    "mail_lien",
                                                    "mail_code",
                                                    "appareil",
                                                    "formateur",
                                                    "attestation",
                                                    ""
                                                ]
                                            },
                                            "authentification": {
                                                "type": "string",
                                                "description": "Libellé de la preuve d'identité (ex. « appareil reconnu, validé par SMS le ... »)"
                                            },
                                            "date": {
                                                "type": [
                                                    "string",
                                                    "null"
                                                ]
                                            },
                                            "tardive": {
                                                "type": "integer",
                                                "description": "1 : hors créneau"
                                            },
                                            "ecart_minutes": {
                                                "type": "integer",
                                                "description": "Minutes après la fin de la séance (0 dans le créneau)"
                                            },
                                            "mention_version": {
                                                "type": "string",
                                                "description": "Version de la mention acceptée par le signataire"
                                            },
                                            "motif": {
                                                "type": "string"
                                            }
                                        }
                                    }
                                }
                            },
                            "example": {
                                "evenement": "signature.faite",
                                "date": "2026-11-10T09:12:33+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": {
                                    "id": 17,
                                    "ref_externe": "ce_4521"
                                },
                                "seance": {
                                    "id": 41,
                                    "ref_externe": "prev_889",
                                    "date": "2026-11-10",
                                    "demi_journee": "matin"
                                },
                                "participant": {
                                    "id": 203,
                                    "ref_externe": "stag_120",
                                    "role": "stagiaire"
                                },
                                "signature": {
                                    "statut": "signee",
                                    "canal": "appareil",
                                    "authentification": "appareil reconnu, validé par SMS le 10/11/2026 à 09:02 (appareil n° 51)",
                                    "date": "2026-11-10T09:12:33+01:00",
                                    "tardive": 0,
                                    "ecart_minutes": 0,
                                    "mention_version": "2026-10",
                                    "motif": ""
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_signature_faite",
                "security": []
            }
        },
        "seance.cloturee": {
            "post": {
                "summary": "Une séance est clôturée : documents disponibles",
                "description": "En-têtes : `X-Presence-Evenement: seance.cloturee`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "cloture": {
                                        "type": "object",
                                        "properties": {
                                            "date": {
                                                "type": "string"
                                            },
                                            "par": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "documents": {
                                        "$ref": "#/components/schemas/Cloture"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "seance.cloturee",
                                "date": "2026-11-10T09:12:33+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": {
                                    "id": 17,
                                    "ref_externe": "ce_4521"
                                },
                                "seance": {
                                    "id": 41,
                                    "ref_externe": "prev_889",
                                    "date": "2026-11-10",
                                    "demi_journee": "matin"
                                },
                                "cloture": {
                                    "date": "2026-11-10T12:40:02+01:00",
                                    "par": "formateur"
                                },
                                "documents": {
                                    "sandbox": 0,
                                    "empreinte": "eeb53b1c...",
                                    "empreinte_pdf": "2b17f078...",
                                    "empreinte_preuves": "87a6c657...",
                                    "empreinte_precedente": "0000...",
                                    "numero": 12,
                                    "horodatage": {
                                        "prestataire": "Universign",
                                        "date": "2026-11-10 12:40:03",
                                        "erreur": "",
                                        "mode": "par_seance"
                                    },
                                    "compteurs": {
                                        "concernes": 10,
                                        "signes": 8,
                                        "attestes": 1,
                                        "absents": 1,
                                        "attendus": 0
                                    },
                                    "pdf": "https://app.emargia.fr/api.php?action=seance/telecharger&seance_id=41&type=pdf",
                                    "preuve_pdf": "https://app.emargia.fr/api.php?action=seance/telecharger&seance_id=41&type=preuve",
                                    "preuves": "https://app.emargia.fr/api.php?action=seance/telecharger&seance_id=41&type=preuves"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_seance_cloturee",
                "security": []
            }
        },
        "absence.justificatif": {
            "post": {
                "summary": "Un justificatif d'absence a été joint ou remplacé (avant ou après clôture)",
                "description": "En-têtes : `X-Presence-Evenement: absence.justificatif`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "participant": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "role": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "justificatif": {
                                        "type": "object",
                                        "properties": {
                                            "empreinte": {
                                                "type": "string"
                                            },
                                            "type": {
                                                "type": "string",
                                                "enum": [
                                                    "jpg",
                                                    "pdf"
                                                ]
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "apres_cloture": {
                                                "type": "integer"
                                            },
                                            "url": {
                                                "type": "string"
                                            }
                                        }
                                    }
                                }
                            },
                            "example": {
                                "evenement": "absence.justificatif",
                                "date": "2026-11-10T09:12:33+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": {
                                    "id": 17,
                                    "ref_externe": "ce_4521"
                                },
                                "seance": {
                                    "id": 41,
                                    "ref_externe": "prev_889",
                                    "date": "2026-11-10",
                                    "demi_journee": "matin"
                                },
                                "participant": {
                                    "id": 203,
                                    "ref_externe": "stag_120",
                                    "role": "stagiaire"
                                },
                                "justificatif": {
                                    "empreinte": "5d41402a...",
                                    "type": "jpg",
                                    "date": "2026-11-12T08:02:10+01:00",
                                    "apres_cloture": 1,
                                    "url": "https://app.emargia.fr/api.php?action=seance/telecharger&seance_id=41&type=justificatif&participant_id=203"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_absence_justificatif",
                "security": []
            }
        },
        "document.signataire": {
            "post": {
                "summary": "Un signataire a ouvert le document (statut lu) ou l'a signé (statut signe)",
                "description": "En-têtes : `X-Presence-Evenement: document.signataire`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "document": {
                                        "type": "object"
                                    },
                                    "signataire": {
                                        "$ref": "#/components/schemas/Signataire"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "document.signataire",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "document": {
                                    "id": 9,
                                    "ref_externe": "conv_118",
                                    "titre": "Convention de formation CONV-2026-118",
                                    "statut": "en_cours"
                                },
                                "signataire": {
                                    "id": 31,
                                    "ref_externe": "client",
                                    "nom": "Martin",
                                    "prenom": "Paul",
                                    "statut": "signe",
                                    "date_signature": "2026-11-12 10:02:08",
                                    "canal": "sms"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_document_signataire",
                "security": []
            }
        },
        "document.signe": {
            "post": {
                "summary": "Toutes les parties ont signé : PDF final, preuves, chaîne, horodatage disponibles",
                "description": "En-têtes : `X-Presence-Evenement: document.signe`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "document": {
                                        "type": "object"
                                    },
                                    "final": {
                                        "type": "object"
                                    },
                                    "signataires": {
                                        "type": "array",
                                        "items": {
                                            "$ref": "#/components/schemas/Signataire"
                                        }
                                    }
                                }
                            },
                            "example": {
                                "evenement": "document.signe",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "document": {
                                    "id": 9,
                                    "ref_externe": "conv_118",
                                    "titre": "Convention de formation CONV-2026-118",
                                    "statut": "signe"
                                },
                                "final": {
                                    "empreinte_pdf": "2b17f078...",
                                    "empreinte_preuves": "87a6c657...",
                                    "empreinte": "eeb53b1c...",
                                    "numero": 4,
                                    "apposition": 1,
                                    "cachet": 0,
                                    "pdf_url": "https://app.emargia.fr/api.php?action=document/telecharger&document_id=9&type=final",
                                    "preuves_url": "https://app.emargia.fr/api.php?action=document/telecharger&document_id=9&type=preuves"
                                },
                                "signataires": []
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_document_signe",
                "security": []
            }
        },
        "document.refuse": {
            "post": {
                "summary": "Un signataire a refusé : la demande est close",
                "description": "En-têtes : `X-Presence-Evenement: document.refuse`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "document": {
                                        "type": "object"
                                    },
                                    "signataire": {
                                        "$ref": "#/components/schemas/Signataire"
                                    },
                                    "motif": {
                                        "type": "string"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "document.refuse",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "document": {
                                    "id": 9,
                                    "ref_externe": "conv_118",
                                    "titre": "Convention de formation CONV-2026-118",
                                    "statut": "en_cours"
                                },
                                "motif": "Montant incorrect",
                                "signataire": {
                                    "id": 31,
                                    "ref_externe": "client",
                                    "nom": "Martin",
                                    "prenom": "Paul",
                                    "statut": "refuse"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_document_refuse",
                "security": []
            }
        },
        "document.expire": {
            "post": {
                "summary": "Date limite dépassée sans toutes les signatures",
                "description": "En-têtes : `X-Presence-Evenement: document.expire`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "document": {
                                        "type": "object"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "document.expire",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "document": {
                                    "id": 9,
                                    "ref_externe": "conv_118",
                                    "titre": "Convention de formation CONV-2026-118",
                                    "statut": "en_cours"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_document_expire",
                "security": []
            }
        },
        "document.annule": {
            "post": {
                "summary": "Demande annulée par l'organisme",
                "description": "En-têtes : `X-Presence-Evenement: document.annule`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "document": {
                                        "type": "object"
                                    },
                                    "motif": {
                                        "type": "string"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "document.annule",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "document": {
                                    "id": 9,
                                    "ref_externe": "conv_118",
                                    "titre": "Convention de formation CONV-2026-118",
                                    "statut": "en_cours"
                                },
                                "motif": ""
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_document_annule",
                "security": []
            }
        },
        "document.envoi_erreur": {
            "post": {
                "summary": "Invitation ou relance impossible (mail refusé, SMS en échec)",
                "description": "En-têtes : `X-Presence-Evenement: document.envoi_erreur`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "document": {
                                        "type": "object"
                                    },
                                    "signataire": {
                                        "$ref": "#/components/schemas/Signataire"
                                    },
                                    "erreur": {
                                        "type": "string"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "document.envoi_erreur",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "document": {
                                    "id": 9,
                                    "ref_externe": "conv_118",
                                    "titre": "Convention de formation CONV-2026-118",
                                    "statut": "en_cours"
                                },
                                "erreur": "Crédit SMS insuffisant"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_document_envoi_erreur",
                "security": []
            }
        },
        "solde.faible": {
            "post": {
                "summary": "Le crédit prépayé est passé sous le seuil du compte (une fois, jusqu'à la prochaine recharge), ou une action vient d'être refusée faute de crédit (`bloque` = 1). Envoyé au compte et, pour un sous-compte, aussi à son éditeur avec `sous_compte`",
                "description": "En-têtes : `X-Presence-Evenement: solde.faible`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "solde": {
                                        "type": "number"
                                    },
                                    "seuil": {
                                        "type": "number"
                                    },
                                    "bloque": {
                                        "type": "integer"
                                    },
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "presence",
                                            "signature",
                                            ""
                                        ]
                                    },
                                    "sous_compte": {
                                        "type": "object"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "solde.faible",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "solde": 4.32,
                                "seuil": 10,
                                "bloque": 0,
                                "type": "presence"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_solde_faible",
                "security": []
            }
        },
        "solde.recharge": {
            "post": {
                "summary": "Une recharge vient d'être créditée (achat en ligne confirmé, recharge par l'éditeur ou par le service) ; `production_activee` = 1 si c'était la première",
                "description": "En-têtes : `X-Presence-Evenement: solde.recharge`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "lot": {
                                        "$ref": "#/components/schemas/Lot"
                                    },
                                    "solde": {
                                        "type": "number"
                                    },
                                    "production_activee": {
                                        "type": "integer"
                                    },
                                    "sous_compte": {
                                        "type": "object"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "solde.recharge",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "lot": {
                                    "id": 12,
                                    "montant_ht": 100,
                                    "solde": 100,
                                    "prix_presence": 0.16,
                                    "prix_signature": 0.32,
                                    "statut": "actif",
                                    "origine": "editeur",
                                    "ref_externe": "FAC-2026-0412",
                                    "date_achat": "2026-11-12 10:02:10",
                                    "date_expiration": "2028-11-12"
                                },
                                "solde": 104.32,
                                "production_activee": 0
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_solde_recharge",
                "security": []
            }
        },
        "lot.expiration": {
            "post": {
                "summary": "Une recharge expire dans moins d'un mois avec un reliquat",
                "description": "En-têtes : `X-Presence-Evenement: lot.expiration`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "lot": {
                                        "$ref": "#/components/schemas/Lot"
                                    }
                                }
                            },
                            "example": {
                                "evenement": "lot.expiration",
                                "date": "2026-11-12T10:02:10+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": null,
                                "seance": null,
                                "lot": {
                                    "id": 7,
                                    "montant_ht": 50,
                                    "solde": 3.6,
                                    "prix_presence": 0.18,
                                    "prix_signature": 0.36,
                                    "statut": "actif",
                                    "origine": "site",
                                    "date_achat": "2024-12-01 09:00:00",
                                    "date_expiration": "2026-12-01"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_lot_expiration",
                "security": []
            }
        },
        "envoi.erreur": {
            "post": {
                "summary": "Un envoi automatique n'a pas pu partir (crédit prépayé épuisé, SMS en échec, heure dépassée)",
                "description": "En-têtes : `X-Presence-Evenement: envoi.erreur`, `X-Presence-Signature: sha256=<HMAC-SHA256(corps, secret_webhook)>`. Répondre 200.",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "evenement": {
                                        "type": "string"
                                    },
                                    "date": {
                                        "type": "string",
                                        "example": "2026-11-10T09:12:33+01:00"
                                    },
                                    "compte_id": {
                                        "type": "integer"
                                    },
                                    "ref_client": {
                                        "type": "string"
                                    },
                                    "sandbox": {
                                        "type": "integer"
                                    },
                                    "dossier": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "seance": {
                                        "type": "object",
                                        "properties": {
                                            "id": {
                                                "type": "integer"
                                            },
                                            "ref_externe": {
                                                "type": "string"
                                            },
                                            "date": {
                                                "type": "string"
                                            },
                                            "demi_journee": {
                                                "type": "string"
                                            }
                                        }
                                    },
                                    "erreur": {
                                        "type": "string"
                                    },
                                    "participants": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        }
                                    }
                                }
                            },
                            "example": {
                                "evenement": "envoi.erreur",
                                "date": "2026-11-10T09:12:33+01:00",
                                "compte_id": 3,
                                "ref_client": "client_45|3",
                                "sandbox": 0,
                                "dossier": {
                                    "id": 17,
                                    "ref_externe": "ce_4521"
                                },
                                "seance": {
                                    "id": 41,
                                    "ref_externe": "prev_889",
                                    "date": "2026-11-10",
                                    "demi_journee": "matin"
                                },
                                "erreur": "Crédit prépayé insuffisant pour 10 participant(s) sur cette séance (solde 0,80 € HT) : rechargez le compte avant d'envoyer les demandes",
                                "participants": []
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Bien reçu"
                    }
                },
                "operationId": "webhook_envoi_erreur",
                "security": []
            }
        }
    },
    "components": {
        "securitySchemes": {
            "cleApi": {
                "type": "apiKey",
                "in": "header",
                "name": "X-Api-Key",
                "description": "Clé de production pr_... ou de bac à sable ps_..."
            }
        },
        "schemas": {
            "Erreur": {
                "type": "object",
                "properties": {
                    "succes": {
                        "type": "integer",
                        "enum": [
                            0
                        ]
                    },
                    "erreur": {
                        "type": "string",
                        "example": "Séance clôturée"
                    },
                    "code": {
                        "type": "string",
                        "example": "seance_cloturee"
                    }
                }
            },
            "Options": {
                "type": "object",
                "description": "Options par défaut du compte, surchargeables par dossier",
                "properties": {
                    "envoi_auto": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "description": "Envoi automatique des demandes à l'heure de début (cron)"
                    },
                    "minutes_avant": {
                        "type": "integer",
                        "description": "Minutes avant l'heure de début pour l'envoi automatique"
                    },
                    "attestation_formateur": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "description": "Le formateur peut attester une présence sans code (motif obligatoire, mention sur la feuille)"
                    },
                    "sms_mode": {
                        "type": "string",
                        "enum": [
                            "formation",
                            "jour",
                            "seance"
                        ],
                        "description": "formation (défaut) : un SMS par personne et par formation, puis téléphone reconnu 30 jours ; jour : un SMS par personne et par jour (le lien couvre matin et après-midi) ; seance : un SMS par personne et par demi-journée. Dans un dossier, `null` retire l'option propre (retour au paramètre du compte)"
                    },
                    "sms_par_jour": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "deprecated": true,
                        "description": "Ancienne option : 1 = sms_mode jour, 0 = sms_mode seance (ignorée si sms_mode est fourni)"
                    },
                    "fenetre_avant": {
                        "type": "integer",
                        "description": "Signature possible N minutes avant le début de la séance, jamais avant (30 par défaut) ; avant : code trop_tot"
                    },
                    "fenetre_apres": {
                        "type": "integer",
                        "description": "Signature « dans le créneau » jusqu'à N minutes après la fin (120 par défaut) ; au-delà : hors créneau"
                    },
                    "hors_creneau": {
                        "type": "string",
                        "enum": [
                            "marquer",
                            "refuser"
                        ],
                        "description": "marquer (défaut) : signature acceptée, marquée « hors créneau » sur la feuille et dans les preuves (tardive = 1) ; refuser : code hors_creneau"
                    },
                    "canal_mail": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "description": "Participants sans mobile : lien et code envoyés par mail (preuve plus faible, tracée)"
                    },
                    "recu_mail": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "description": "Mail de confirmation au participant après signature"
                    },
                    "conservation_annees": {
                        "type": "integer",
                        "description": "Durée de conservation des preuves (10 par défaut)"
                    },
                    "modele_feuille": {
                        "type": "string",
                        "enum": [
                            "classique",
                            "compacte",
                            "detaillee"
                        ],
                        "description": "Gabarit de la feuille d'émargement : classique (défaut), compacte (une page), detaillee (durée, preuve, taux de présence)"
                    },
                    "couleur": {
                        "type": "string",
                        "description": "Couleur d'accent des documents, hexadécimal sans #, ex. 1e3a5f ; vide = gris neutre",
                        "example": "1e3a5f"
                    },
                    "mentions": {
                        "type": "string",
                        "description": "Texte libre imprimé sous le tableau des feuilles et sur les certificats (600 caractères max)"
                    },
                    "pied": {
                        "type": "string",
                        "description": "Texte ajouté au pied de page (200 caractères max)"
                    },
                    "modele_certificat": {
                        "type": "string",
                        "enum": [
                            "sobre",
                            "encadre"
                        ],
                        "description": "Gabarit du certificat de réalisation"
                    },
                    "signataire": {
                        "type": "string",
                        "description": "Nom et fonction du signataire des certificats",
                        "example": "Marie Dupont, directrice"
                    },
                    "minutes_demi_journee": {
                        "type": "integer",
                        "description": "Durée retenue pour une séance sans horaires : demi-journée (210 par défaut)"
                    },
                    "minutes_journee": {
                        "type": "integer",
                        "description": "Durée retenue pour une séance sans horaires : journée (420 par défaut)"
                    }
                }
            },
            "SeanceEntree": {
                "type": "object",
                "required": [
                    "date"
                ],
                "properties": {
                    "ref_externe": {
                        "type": "string",
                        "description": "Votre identifiant de la séance, unique dans le dossier (défaut : date|demi_journee)",
                        "example": "prev_889"
                    },
                    "date": {
                        "type": "string",
                        "example": "2026-11-10"
                    },
                    "demi_journee": {
                        "type": "string",
                        "enum": [
                            "matin",
                            "apres_midi",
                            "journee"
                        ],
                        "description": "Déduite des horaires si absente"
                    },
                    "heure_debut": {
                        "type": "string",
                        "example": "09:00"
                    },
                    "heure_fin": {
                        "type": "string",
                        "example": "12:30"
                    },
                    "participants": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "ref_externe des participants de cette séance seulement (absent : tous)"
                    }
                }
            },
            "ParticipantEntree": {
                "type": "object",
                "required": [
                    "ref_externe",
                    "nom"
                ],
                "properties": {
                    "ref_externe": {
                        "type": "string",
                        "example": "stag_120"
                    },
                    "role": {
                        "type": "string",
                        "enum": [
                            "stagiaire",
                            "formateur"
                        ],
                        "default": "stagiaire"
                    },
                    "nom": {
                        "type": "string",
                        "example": "Durand"
                    },
                    "prenom": {
                        "type": "string",
                        "example": "Marie"
                    },
                    "mobile": {
                        "type": "string",
                        "example": "06 12 34 56 78"
                    },
                    "mail": {
                        "type": "string",
                        "example": "m.durand@exemple.fr"
                    },
                    "societe": {
                        "type": "string",
                        "example": "SARL Martin"
                    }
                }
            },
            "DossierEntree": {
                "type": "object",
                "required": [
                    "ref_externe",
                    "intitule"
                ],
                "properties": {
                    "ref_externe": {
                        "type": "string",
                        "description": "Votre identifiant de la formation, unique par compte et par mode",
                        "example": "ce_4521"
                    },
                    "intitule": {
                        "type": "string",
                        "example": "Habilitation électrique B0 H0V"
                    },
                    "reference": {
                        "type": "string",
                        "example": "CONV-2026-118"
                    },
                    "organisme": {
                        "type": "string",
                        "description": "Nom de l'organisme tel qu'imprimé sur la feuille (défaut : nom du compte)"
                    },
                    "lieu": {
                        "type": "string"
                    },
                    "date_debut": {
                        "type": "string",
                        "example": "2026-11-10"
                    },
                    "date_fin": {
                        "type": "string",
                        "example": "2026-11-12"
                    },
                    "options": {
                        "$ref": "#/components/schemas/Options"
                    },
                    "champs": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Champ"
                        },
                        "description": "Éléments à collecter auprès des participants avant leur première signature (une fois par participant, ou à chaque séance avec champs_par_seance). Réponses dans dossier/lire (participants[].reponses), les preuves de séance et le webhook signature.faite"
                    },
                    "champs_par_seance": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "default": 0
                    },
                    "seances": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/SeanceEntree"
                        }
                    },
                    "participants": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ParticipantEntree"
                        }
                    }
                }
            },
            "Compteurs": {
                "type": "object",
                "properties": {
                    "concernes": {
                        "type": "integer"
                    },
                    "signes": {
                        "type": "integer"
                    },
                    "attestes": {
                        "type": "integer"
                    },
                    "absents": {
                        "type": "integer"
                    },
                    "attendus": {
                        "type": "integer"
                    }
                }
            },
            "Horodatage": {
                "type": "object",
                "properties": {
                    "prestataire": {
                        "type": "string"
                    },
                    "date": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Date certifiée par le jeton RFC 3161 (null si en attente : le cron réessaie)"
                    },
                    "erreur": {
                        "type": "string"
                    },
                    "mode": {
                        "type": "string",
                        "enum": [
                            "par_seance",
                            "quotidien"
                        ]
                    }
                }
            },
            "Cloture": {
                "type": "object",
                "properties": {
                    "date": {
                        "type": "string"
                    },
                    "par": {
                        "type": "string",
                        "enum": [
                            "organisme",
                            "formateur",
                            "auto"
                        ]
                    },
                    "numero": {
                        "type": "integer",
                        "description": "Rang dans la chaîne du compte (0 en bac à sable)"
                    },
                    "empreinte": {
                        "type": "string",
                        "description": "SHA-256(empreinte_pdf . empreinte_preuves . empreinte_precedente) : horodatée, chaînée"
                    },
                    "empreinte_pdf": {
                        "type": "string"
                    },
                    "empreinte_preuves": {
                        "type": "string",
                        "description": "Imprimée sur la feuille (QR code)"
                    },
                    "empreinte_preuve_pdf": {
                        "type": "string",
                        "description": "Empreinte du dossier de preuve PDF (depuis la v1.9 : empreinte = SHA-256(pdf . preuves . preuve_pdf . precedente))"
                    },
                    "empreinte_precedente": {
                        "type": "string"
                    },
                    "horodatage": {
                        "$ref": "#/components/schemas/Horodatage"
                    },
                    "compteurs": {
                        "$ref": "#/components/schemas/Compteurs"
                    },
                    "pdf_url": {
                        "type": "string",
                        "description": "À appeler avec la clé API"
                    },
                    "preuves_url": {
                        "type": "string"
                    },
                    "preuve_pdf_url": {
                        "type": "string",
                        "description": "Dossier de preuve PDF lisible (séance, participants, chronologie, authentification, empreintes) ; vide pour les séances clôturées avant la v1.9"
                    }
                }
            },
            "SeanceLecture": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "ref_externe": {
                        "type": "string"
                    },
                    "date": {
                        "type": "string"
                    },
                    "date_fr": {
                        "type": "string"
                    },
                    "demi_journee": {
                        "type": "string"
                    },
                    "libelle_demi": {
                        "type": "string"
                    },
                    "heure_debut": {
                        "type": "string"
                    },
                    "heure_fin": {
                        "type": "string"
                    },
                    "statut": {
                        "type": "string",
                        "enum": [
                            "a_venir",
                            "ouverte",
                            "cloturee",
                            "annulee"
                        ]
                    },
                    "envoi_auto": {
                        "type": "integer"
                    },
                    "minutes_avant": {
                        "type": "integer"
                    },
                    "date_envoi_demandes": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "lien": {
                        "type": "string",
                        "description": "Lien de séance (tablette, QR code, mail) ; vide une fois clôturée"
                    },
                    "avertissements": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "Points d'attention : dimanche, jour férié, séance « journée » (deux présences par personne, une seule signature)"
                    },
                    "compteurs": {
                        "$ref": "#/components/schemas/Compteurs"
                    },
                    "participants": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "string",
                            "enum": [
                                "attendue",
                                "signee",
                                "attestee",
                                "absent"
                            ]
                        },
                        "description": "participant_id => statut"
                    },
                    "liens_sandbox": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "string"
                        },
                        "description": "Bac à sable seulement : participant_id => lien personnel en cours"
                    },
                    "cloture": {
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/Cloture"
                            },
                            {
                                "type": "null"
                            }
                        ]
                    }
                }
            },
            "ParticipantLecture": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "ref_externe": {
                        "type": "string"
                    },
                    "role": {
                        "type": "string"
                    },
                    "nom": {
                        "type": "string"
                    },
                    "prenom": {
                        "type": "string"
                    },
                    "societe": {
                        "type": "string"
                    },
                    "mobile_masque": {
                        "type": "string",
                        "example": "+33 6 ** ** ** 78"
                    },
                    "a_mobile": {
                        "type": "integer"
                    },
                    "a_mail": {
                        "type": "integer"
                    },
                    "mobile_origine": {
                        "type": "string",
                        "enum": [
                            "api",
                            "site",
                            "admin",
                            "import",
                            "correction",
                            "formateur",
                            "test",
                            ""
                        ],
                        "description": "Qui a fourni le numéro (preuve : le numéro vient du logiciel de l'OF, d'une saisie, d'une correction...)"
                    },
                    "mobile_date": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "mobile_purge": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Date d'effacement RGPD du mobile en clair (N mois après la fin de la formation) ; la preuve garde le numéro masqué"
                    },
                    "nb_sms": {
                        "type": "integer",
                        "description": "SMS réellement partis pour cette personne sur la formation"
                    },
                    "appareil": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "Téléphone reconnu en cours (vérifié par SMS) : id, date_creation, date_expiration, nb_signatures ; null sinon"
                    },
                    "actif": {
                        "type": "integer",
                        "description": "0 : retiré de la formation après synchronisation (ses signatures restent)"
                    },
                    "seances": {
                        "type": [
                            "array",
                            "null"
                        ],
                        "items": {
                            "type": "integer"
                        },
                        "description": "Ids des séances si le participant n'est pas sur toutes"
                    },
                    "reponses": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Reponse"
                        },
                        "description": "Éléments collectés (champs du dossier)"
                    },
                    "reponses_date": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "statuts": {
                        "type": "object",
                        "description": "seance_id => {statut, date, canal (sms_lien, sms_code, mail_lien, mail_code, appareil, formateur, attestation), tardive (1 = hors créneau), motif, justificatif (0/1), justificatif_apres_cloture, justificatif_url}"
                    }
                }
            },
            "DossierLecture": {
                "type": "object",
                "properties": {
                    "succes": {
                        "type": "integer",
                        "enum": [
                            1
                        ],
                        "description": "Toujours 1 en cas de succès"
                    },
                    "dossier": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "integer"
                            },
                            "ref_externe": {
                                "type": "string"
                            },
                            "intitule": {
                                "type": "string"
                            },
                            "reference": {
                                "type": "string"
                            },
                            "organisme": {
                                "type": "string"
                            },
                            "lieu": {
                                "type": "string"
                            },
                            "date_debut": {
                                "type": "string"
                            },
                            "date_fin": {
                                "type": "string"
                            },
                            "statut": {
                                "type": "string",
                                "enum": [
                                    "ouvert",
                                    "clos"
                                ]
                            },
                            "sandbox": {
                                "type": "integer"
                            },
                            "options": {
                                "$ref": "#/components/schemas/Options"
                            },
                            "options_propres": {
                                "type": "object",
                                "description": "Options posées sur le dossier lui-même (le reste vient du compte)"
                            },
                            "sms_envoyes": {
                                "type": "integer",
                                "description": "SMS partis pour ce dossier (liens et codes)"
                            },
                            "mobiles_doublons": {
                                "type": "array",
                                "items": {
                                    "type": "object"
                                },
                                "description": "Groupes de participants actifs partageant le même mobile : mobile_masque, participants[] ; un téléphone n'est reconnu que pour une personne"
                            }
                        }
                    },
                    "seances": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/SeanceLecture"
                        }
                    },
                    "participants": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ParticipantLecture"
                        }
                    },
                    "sans_mobile": {
                        "type": "array",
                        "items": {
                            "type": "integer"
                        },
                        "description": "Ids des participants actifs qui ne pourront pas recevoir de code"
                    }
                }
            },
            "DossierCreation": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/DossierLecture"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "existant": {
                                "type": "integer",
                                "description": "1 si le dossier existait déjà (création idempotente : rien n'est modifié)"
                            },
                            "seances_ids": {
                                "type": "object",
                                "additionalProperties": {
                                    "type": "integer"
                                },
                                "description": "ref_externe => id"
                            },
                            "participants_ids": {
                                "type": "object",
                                "additionalProperties": {
                                    "type": "integer"
                                },
                                "description": "ref_externe => id"
                            },
                            "rapport": {
                                "type": "object",
                                "properties": {
                                    "ajoutes": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        }
                                    },
                                    "modifies": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        }
                                    },
                                    "desactives": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        }
                                    },
                                    "ignores": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        },
                                        "description": "Avec motif : cloturee, identité modifiée après signature..."
                                    },
                                    "conflits": {
                                        "type": "array",
                                        "items": {
                                            "type": "object"
                                        },
                                        "description": "Séance signée dont la date a changé chez vous"
                                    }
                                }
                            },
                            "sans_mobile": {
                                "type": "array",
                                "items": {
                                    "type": "object"
                                }
                            },
                            "mobiles_invalides": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "properties": {
                                        "ref_externe": {
                                            "type": "string"
                                        },
                                        "mobile": {
                                            "type": "string"
                                        }
                                    }
                                }
                            },
                            "avertissements": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                },
                                "description": "À afficher à l'utilisateur : séance un dimanche ou un jour férié, séance « journée », même mobile pour plusieurs participants"
                            }
                        }
                    }
                ]
            },
            "Envoi": {
                "type": "object",
                "properties": {
                    "succes": {
                        "type": "integer",
                        "enum": [
                            1
                        ],
                        "description": "Toujours 1 en cas de succès"
                    },
                    "seance_id": {
                        "type": "integer"
                    },
                    "sandbox": {
                        "type": "integer"
                    },
                    "envoyes": {
                        "type": "integer"
                    },
                    "erreurs": {
                        "type": "integer"
                    },
                    "participants": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "participant_id": {
                                    "type": "integer"
                                },
                                "nom": {
                                    "type": "string"
                                },
                                "etat": {
                                    "type": "string",
                                    "enum": [
                                        "envoye",
                                        "sandbox",
                                        "deja_envoye",
                                        "appareil_reconnu",
                                        "deja_signe",
                                        "absent",
                                        "sans_mobile",
                                        "erreur_sms"
                                    ]
                                },
                                "canal": {
                                    "type": "string",
                                    "enum": [
                                        "sms",
                                        "mail"
                                    ]
                                },
                                "seances": {
                                    "type": "array",
                                    "items": {
                                        "type": "integer"
                                    },
                                    "description": "Séances couvertes par le lien"
                                },
                                "lien": {
                                    "type": "string",
                                    "description": "Bac à sable seulement : le lien personnel qui aurait été envoyé par SMS"
                                },
                                "erreur": {
                                    "type": "string"
                                }
                            }
                        }
                    },
                    "lien_seance": {
                        "type": "string"
                    }
                }
            },
            "Verification": {
                "type": "object",
                "properties": {
                    "succes": {
                        "type": "integer",
                        "enum": [
                            1
                        ],
                        "description": "Toujours 1 en cas de succès"
                    },
                    "reconnu": {
                        "type": "integer"
                    },
                    "sandbox": {
                        "type": "integer"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "fichier",
                            "empreinte_seance",
                            "empreinte_pdf",
                            "empreinte_preuves"
                        ]
                    },
                    "message": {
                        "type": "string"
                    },
                    "organisme": {
                        "type": "string"
                    },
                    "intitule": {
                        "type": "string"
                    },
                    "date_fr": {
                        "type": "string"
                    },
                    "demi_journee": {
                        "type": "string"
                    },
                    "nb_signatures": {
                        "type": "integer"
                    },
                    "nb_attestes": {
                        "type": "integer"
                    },
                    "nb_absents": {
                        "type": "integer"
                    },
                    "nb_attendus": {
                        "type": "integer"
                    },
                    "date_cloture": {
                        "type": "string"
                    },
                    "horodatage": {
                        "type": "object"
                    },
                    "chaine": {
                        "type": "object"
                    },
                    "integrite": {
                        "type": "object",
                        "description": "Recalcul des empreintes des fichiers conservés : pdf, preuves, chaine (1 ok, 0 anomalie, null fichier absent)"
                    }
                }
            },
            "Palier": {
                "type": "object",
                "properties": {
                    "montant_ht": {
                        "type": "number",
                        "example": 100
                    },
                    "prix_presence_ht": {
                        "type": "number",
                        "example": 0.16
                    },
                    "prix_signature_ht": {
                        "type": "number",
                        "example": 0.32
                    },
                    "remise_pct": {
                        "type": "integer",
                        "example": 20
                    },
                    "nb_presences": {
                        "type": "integer",
                        "example": 625
                    },
                    "nb_signatures": {
                        "type": "integer",
                        "example": 312
                    },
                    "libelle": {
                        "type": "string"
                    }
                }
            },
            "Lot": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "montant_ht": {
                        "type": "number"
                    },
                    "solde": {
                        "type": "number",
                        "description": "Reste à consommer sur ce lot (€ HT)"
                    },
                    "prix_presence": {
                        "type": "number"
                    },
                    "prix_signature": {
                        "type": "number"
                    },
                    "statut": {
                        "type": "string",
                        "enum": [
                            "actif",
                            "en_attente",
                            "epuise",
                            "expire",
                            "annule"
                        ]
                    },
                    "origine": {
                        "type": "string",
                        "enum": [
                            "site",
                            "editeur",
                            "admin",
                            "api"
                        ]
                    },
                    "ref_externe": {
                        "type": "string"
                    },
                    "moyen_paiement": {
                        "type": "string"
                    },
                    "commentaire": {
                        "type": "string"
                    },
                    "date_achat": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "date_expiration": {
                        "type": [
                            "string",
                            "null"
                        ]
                    }
                }
            },
            "Mouvement": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "date": {
                        "type": "string"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "achat",
                            "presence",
                            "signature",
                            "remboursement",
                            "expiration",
                            "ajustement"
                        ]
                    },
                    "quantite": {
                        "type": "integer",
                        "description": "Demi-journées ou documents"
                    },
                    "tarif": {
                        "type": "number",
                        "description": "Prix unitaire appliqué (celui du lot)"
                    },
                    "montant": {
                        "type": "number",
                        "description": "Signé : négatif pour un débit"
                    },
                    "solde_lot_avant": {
                        "type": "number"
                    },
                    "solde_lot_apres": {
                        "type": "number"
                    },
                    "lot_id": {
                        "type": "integer"
                    },
                    "lot_montant": {
                        "type": "number"
                    },
                    "libelle": {
                        "type": "string"
                    },
                    "dossier_id": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "seance_id": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "participant_id": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "document_id": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "participant": {
                        "type": "string"
                    },
                    "document": {
                        "type": "string"
                    }
                }
            },
            "Facture": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "numero": {
                        "type": "string"
                    },
                    "brouillon": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "description": "1 : créée en brouillon dans la facturation du service (essais, Stripe en mode test) : ni numérotée, ni archivée"
                    },
                    "objet": {
                        "type": "string"
                    },
                    "montant_ht": {
                        "type": "number"
                    },
                    "montant_tva": {
                        "type": "number"
                    },
                    "montant_ttc": {
                        "type": "number"
                    },
                    "statut": {
                        "type": "string",
                        "enum": [
                            "a_regler",
                            "prelevement_en_cours",
                            "payee",
                            "echec",
                            "annulee"
                        ]
                    },
                    "moyen_paiement": {
                        "type": "string"
                    },
                    "date_facture": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "date_paiement": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "erreur": {
                        "type": "string"
                    },
                    "lot_id": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    }
                }
            },
            "Champ": {
                "type": "object",
                "required": [
                    "type",
                    "libelle"
                ],
                "description": "Élément à collecter auprès du signataire (document) ou du participant (dossier) avant sa signature, comme la collecte Oodrive",
                "properties": {
                    "cle": {
                        "type": "string",
                        "description": "Identifiant technique (lettres, chiffres, _) ; défaut champ_1, champ_2...",
                        "example": "num_secu"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "text",
                            "textarea",
                            "number",
                            "date",
                            "email",
                            "phone",
                            "iban",
                            "secu",
                            "file",
                            "checkbox",
                            "select"
                        ]
                    },
                    "libelle": {
                        "type": "string",
                        "example": "Numéro de sécurité sociale"
                    },
                    "obligatoire": {
                        "type": "integer",
                        "enum": [
                            0,
                            1
                        ],
                        "default": 1
                    },
                    "aide": {
                        "type": "string",
                        "description": "Texte d'aide sous le libellé"
                    },
                    "valeur": {
                        "type": "string",
                        "description": "Valeur pré-remplie"
                    },
                    "choix": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "Type select : les valeurs proposées"
                    },
                    "pour": {
                        "description": "Document : liste de ref_externe de signataires (vide = tous). Dossier : stagiaire (défaut), formateur ou tous",
                        "oneOf": [
                            {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            {
                                "type": "string"
                            }
                        ]
                    }
                }
            },
            "Reponse": {
                "type": "object",
                "description": "Valeur collectée",
                "properties": {
                    "cle": {
                        "type": "string"
                    },
                    "libelle": {
                        "type": "string"
                    },
                    "type": {
                        "type": "string"
                    },
                    "valeur": {
                        "type": "string",
                        "description": "checkbox : 1/0 ; date : AAAA-MM-JJ ; phone : E.164 ; file : nom du fichier"
                    },
                    "fichier": {
                        "type": "string",
                        "description": "Type file : chemin conservé"
                    },
                    "fichier_empreinte": {
                        "type": "string"
                    },
                    "url": {
                        "type": "string",
                        "description": "Type file : URL de téléchargement (clé API)"
                    }
                }
            },
            "SignataireEntree": {
                "type": "object",
                "required": [
                    "nom"
                ],
                "properties": {
                    "ref_externe": {
                        "type": "string",
                        "description": "Votre identifiant du signataire (défaut sig_1, sig_2...)"
                    },
                    "ordre": {
                        "type": "integer",
                        "description": "Ordre de signature (mode séquentiel)"
                    },
                    "nom": {
                        "type": "string"
                    },
                    "prenom": {
                        "type": "string"
                    },
                    "mail": {
                        "type": "string",
                        "description": "Invitation par mail ; mail ou mobile obligatoire"
                    },
                    "mobile": {
                        "type": "string",
                        "description": "Code à usage unique par SMS (sinon par mail) ; invitation par SMS si pas de mail"
                    },
                    "qualite": {
                        "type": "string",
                        "example": "Directeur"
                    },
                    "societe": {
                        "type": "string"
                    },
                    "position": {
                        "type": "object",
                        "description": "Emplacement de la signature dans le PDF (emplacement = positions) : page, x et largeur en % de la largeur, y en % de la hauteur (haut du cadre)",
                        "properties": {
                            "page": {
                                "type": "integer"
                            },
                            "x": {
                                "type": "number"
                            },
                            "y": {
                                "type": "number"
                            },
                            "largeur": {
                                "type": "number"
                            }
                        }
                    }
                }
            },
            "Signataire": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "ref_externe": {
                        "type": "string"
                    },
                    "ordre": {
                        "type": "integer"
                    },
                    "nom": {
                        "type": "string"
                    },
                    "prenom": {
                        "type": "string"
                    },
                    "qualite": {
                        "type": "string"
                    },
                    "societe": {
                        "type": "string"
                    },
                    "mail": {
                        "type": "string"
                    },
                    "mobile_masque": {
                        "type": "string"
                    },
                    "statut": {
                        "type": "string",
                        "enum": [
                            "attente",
                            "invite",
                            "lu",
                            "signe",
                            "refuse"
                        ],
                        "description": "attente : pas encore invité (séquentiel) ; invite ; lu : a ouvert le document ; signe ; refuse"
                    },
                    "date_invitation": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "date_lecture": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "date_signature": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "nb_relances": {
                        "type": "integer"
                    },
                    "canal": {
                        "type": "string",
                        "description": "Canal du code à usage unique : sms, mail"
                    },
                    "motif_refus": {
                        "type": "string"
                    },
                    "lien": {
                        "type": "string",
                        "description": "Lien personnel de signature, renvoyé en bac à sable et quand envoi_auto = 0 (vous diffusez vous-même)"
                    },
                    "reponses": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Reponse"
                        },
                        "description": "Éléments collectés avant sa signature"
                    }
                }
            },
            "Document": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "ref_externe": {
                        "type": "string"
                    },
                    "titre": {
                        "type": "string"
                    },
                    "statut": {
                        "type": "string",
                        "enum": [
                            "en_cours",
                            "signe",
                            "refuse",
                            "expire",
                            "annule"
                        ]
                    },
                    "sandbox": {
                        "type": "integer"
                    },
                    "nb_pages": {
                        "type": "integer"
                    },
                    "sequentiel": {
                        "type": "integer"
                    },
                    "date_limite": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "envoi_auto": {
                        "type": "integer"
                    },
                    "emplacement": {
                        "type": "string",
                        "enum": [
                            "page",
                            "positions"
                        ]
                    },
                    "empreinte_original": {
                        "type": "string"
                    },
                    "nb_signataires": {
                        "type": "integer"
                    },
                    "nb_signes": {
                        "type": "integer"
                    },
                    "champs": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Champ"
                        }
                    },
                    "signataires": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Signataire"
                        }
                    },
                    "final": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "description": "Après signature complète : empreintes, numéro de chaîne, horodatage, URLs du PDF signé et des preuves",
                        "properties": {
                            "empreinte_pdf": {
                                "type": "string"
                            },
                            "empreinte_preuves": {
                                "type": "string"
                            },
                            "empreinte": {
                                "type": "string"
                            },
                            "numero": {
                                "type": "integer"
                            },
                            "apposition": {
                                "type": "integer",
                                "description": "1 : signatures apposées dans les pages du PDF ; 0 : pages de signature ajoutées seulement"
                            },
                            "cachet": {
                                "type": "integer",
                                "description": "1 : cachet serveur PAdES apposé"
                            },
                            "horodatage": {
                                "type": "object"
                            },
                            "pdf_url": {
                                "type": "string"
                            },
                            "preuves_url": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        }
    }
}