Aller au contenu
Figen AI API de conformité pour ROMEO

Guide de l'API Figen pour ROMEO#

Ce guide s'adresse aux développeurs qui branchent ROMEO sur l'API de Figen. Il se lit dans l'ordre : l'essentiel en une page, un premier appel, la façon de marquer un modèle, puis les parcours un par un, avec leurs commandes curl. Tous les exemples sont fictifs. Version du guide : API v1, novembre 2026.

1. L'essentiel en une page#

Ce que fait l'API#

Deux choses.

Contrôler les modèles. Inovéa dépose ses modèles de documents réglementaires : ce sont les modèles de base. Chaque cabinet peut en adapter une copie, le modèle du cabinet. L'API compare cette copie au modèle de base et dit ce qui a été supprimé ou modifié dans les mentions obligatoires.

Générer les documents. ROMEO envoie les données d'un dossier. L'API rend le document en Markdown et en HTML, rédige les commentaires libres quand le modèle en prévoit, puis contrôle le rendu.

Tout se passe de serveur à serveur, avec une clé d'API. Aucun utilisateur d'Inovéa ne se connecte chez Figen. Les textes circulent en Markdown ; le HTML est accepté en entrée et converti. Jamais de Word.

Les trois règles#

  1. Figen contrôle et alerte, ROMEO décide. L'API dit l'état d'un modèle et détaille ses alertes. Bloquer un cabinet ou le laisser continuer reste votre décision. Figen ne corrige jamais un modèle.
  2. Rien de vos clients n'est conservé. Le document généré part dans la réponse. Il ne reste chez nous qu'une ligne de journal, sans contenu.
  3. /v1 ne casse jamais sans préavis. Vous êtes prévenus avant tout changement, chaque évolution est consignée dans le journal des changements, et une rupture passe par une nouvelle version.

Le vocabulaire#

Mot Sens
Modèle de base Le modèle déposé par Inovéa. Il est versionné et fait référence.
Modèle du cabinet La copie adaptée par un cabinet (un CIF). cif_id est l'identifiant du cabinet, choisi par ROMEO.
Mention obligatoire Un passage que le cabinet ne peut ni supprimer ni dénaturer. Dans le modèle, une marque de clause la délimite.
Zone libre Tout ce qui n'est pas dans une clause. Le cabinet peut l'adapter.
Commentaire Un texte libre, rédigé par l'IA au moment de la génération ou fourni par ROMEO. Dans le modèle, une marque de bloc IA dit où il se place.
Contrôle La comparaison d'un texte avec sa référence. Il rend un statut et des alertes.
Alerte Un écart constaté. Elle est bloquante ou vaut simple information.
Dérive L'état d'un modèle de cabinet qui était valide quand le modèle de base a été republié. Il attend une revalidation.

Ce guide dit mention, cabinet, modèle et commentaire. Dans les marques, les champs et les codes de l'API, les mêmes choses s'appellent clause, cif, gabarit et bloc ou ia.

Où trouver quoi#

Adresse Contenu
/guide Ce guide, suivi du journal des changements de l'API.
/docs La référence complète des routes et de leurs schémas.
/openapi.json La même référence, au format OpenAPI.

2. Premier appel en deux minutes#

Il vous faut curl, jq (version 1.6 ou plus récente : le parcours 4.6 utilise son option --rawfile) et deux variables d'environnement. L'adresse vous est remise avec votre clé.

export ROMEO_URL="…"   # adresse de l'API, sans barre oblique à la fin
export ROMEO_CLE="…"   # votre clé : ne l'écrivez ni dans un dépôt, ni dans une adresse

Premier appel, sans clé. /v1/sante est la seule route de /v1 qui n'en demande pas.

curl -sS "$ROMEO_URL/v1/sante"
{"statut": "ok", "service": "inovea-romeo", "version": "0.1.0",
 "revision": "a653dd83277e562295e158df51fd88bae0c38a17", "environnement": "recette",
 "base_de_donnees": "ok", "ia": {"mode": "bedrock", "modele": "claude-opus-5-5"},
 "heure": "2026-11-10T09:00:00Z"}

revision identifie le code servi : elle change à chaque mise à jour du service, citez-la dans vos questions. Si la base de données ne répond pas, l'API répond 503 avec "statut": "degrade". ia.mode vaut bedrock ou factice : regardez-le avant vos essais. En mode factice, les jugements et les rédactions sont simulés et signés "modele_ia": "factice" : de quoi tester l'enchaînement des appels, pas juger de la qualité d'un avis.

Deuxième appel, avec la clé :

curl -sS "$ROMEO_URL/v1/moi" -H "Authorization: Bearer $ROMEO_CLE"
{"cle": {"nom": "ROMEO recette", "prefixe": "romeo_rec_ab12cd", "creee_le": "…",
         "derniere_utilisation": "…"},
 "environnement": "recette", "ia": {"mode": "bedrock", "modele": "claude-opus-5-5"}}

La réponse nomme la clé, sans jamais la répéter, et dit dans quel environnement elle travaille. recette est l'environnement de test. creee_le et derniere_utilisation sont des dates ; prévoyez aussi null : c'est ce que rend la clé de développement d'un service lancé en local.

Toutes les autres routes de /v1 attendent le même en-tête. Sans clé reconnue, l'API répond 401, avec une erreur de la forme décrite en partie 9. Pour lire une réponse à l'écran, ajoutez | jq à la commande. Dans les corps JSON que vous envoyez, un champ inconnu est refusé (400 requete_invalide) : une faute de frappe ne passe pas inaperçue. Seuls donnees et blocs portent des clés libres.

3. Le modèle de document#

Un modèle est un texte Markdown qui suit quelques conventions. Elles disent à l'API ce qui se remplace, ce qui est obligatoire et ce qui se rédige.

Les conventions de marquage#

Convention Syntaxe Rôle
Variable {{ org_orias }}, {{ product.name }} Remplacée par une donnée à la génération.
Condition, boucle {% if org_status_cif %}…{% endif %}, {% for product in products %}…{% endfor %} Syntaxe Jinja2 : un passage présent sous condition, ou répété.
Clause <!-- clause: mediation -->…<!-- /clause --> Délimite une mention obligatoire.
Bloc IA <!-- ia: justification_recommandation --> Emplacement d'un commentaire rédigé à la génération. Toujours hors d'une clause.
Note <!-- note: … --> Consigne au conseiller. Ignorée au contrôle, retirée à la génération.

Tout ce qui n'est pas dans une clause est une zone libre, que le cabinet peut adapter.

Une clause porte un identifiant (minuscules, chiffres et _, en commençant par une lettre) et trois attributs facultatifs, séparés par une barre verticale :

<!-- clause: immatriculation | exigence: identique | titre: Immatriculation -->
  • exigence dit jusqu'où le cabinet peut toucher au texte. Avec sens, la valeur par défaut, il peut reformuler tant que le sens reste le même. Avec identique, le texte se reprend mot pour mot.
  • titre donne à la mention un libellé lisible dans les rapports.
  • presence dit si le cabinet peut se passer de la mention. Sans cet attribut, elle est obligatoire. Avec presence: facultative, le cabinet peut la retirer en entier, pas la dénaturer.

Exemple, inventé lui aussi : une section qui ne concerne que les cabinets exerçant une seconde activité.

<!-- clause: activite_credit | presence: facultative | titre: Activité de crédit -->
Le cabinet exerce aussi une activité d'intermédiaire en crédit, pour laquelle il est immatriculé à part.
<!-- /clause -->

Un cabinet qui n'exerce pas cette activité supprime la section : le contrôle le note pour information (clause_facultative_retiree) et le modèle reste valide. Un cabinet qui la garde mais en change le sens reçoit la même alerte bloquante que pour toute autre mention.

Un bloc IA peut porter sa propre consigne de rédaction : <!-- ia: cle | consigne: … -->.

Les variables acceptent des filtres de mise en forme :

Filtre Valeur reçue Texte rendu
date 2026-12-15 15 décembre 2026
date("court") 2026-12-15 15/12/2026
euros 1234.5 1 234,50 €
pct 3.5 3,5 %
ouinon true Oui

S'y ajoutent nombre et les filtres courants de Jinja2, par exemple {{ org_orias | default("néant") }} ou {{ vigilance | join(", ") }}. Même sans filtre, un booléen rendu par une variable s'écrit « Oui » ou « Non », jamais « True ».

Un mini-modèle, fictif#

Le modèle ci-dessous est inventé pour ce guide. Il sert dans tous les parcours ; enregistrez-le dans un fichier modele.md pour les rejouer.

# Rapport de conseil

{{ org_name }} a préparé ce rapport pour {{ client_first_name }}.

<!-- note: Adaptez cette présentation à votre cabinet. -->
Notre cabinet accompagne des particuliers dans la gestion de leur patrimoine depuis 2009.

<!-- clause: immatriculation | exigence: identique | titre: Immatriculation -->
{{ org_name }} est immatriculé au registre des intermédiaires sous le numéro {{ org_orias }}.
<!-- /clause -->

{% if org_status_cif %}
<!-- clause: mediation | titre: Médiation -->
En cas de désaccord, écrivez d'abord au cabinet. Sans réponse satisfaisante sous deux mois, vous pouvez
saisir gratuitement le médiateur : {{ org_mediator_name }}.
<!-- /clause -->
{% endif %}

## Notre recommandation

<!-- ia: justification_recommandation | consigne: Expliquer en quelques phrases le choix proposé. -->

Ce qu'il faut y voir :

  • Le paragraphe de présentation est une zone libre. Le cabinet l'adapte comme il veut, et la note qui le précède ne sortira jamais dans un document.
  • immatriculation est une mention à reprendre mot pour mot. Le moindre changement de texte lève une alerte bloquante, sans appel à l'IA.
  • mediation est une mention dont le sens doit être conservé. Le cabinet peut la reformuler ; si le texte change, un juge dit si le sens a tenu.
  • La condition {% if org_status_cif %} entoure la mention de médiation. Quand la donnée vaut faux, la mention est écartée et dite non applicable : le document reste conforme. Placée à l'intérieur de la clause, la même condition laisserait une mention vide, et une alerte bloquante (clause_vide_au_rendu).
  • Cette condition fait partie de ce qui est contrôlé. Un cabinet qui la retire, la change, ou en ajoute une autour d'une mention reçoit une alerte bloquante (condition_modifiee), même si le texte de la mention n'a pas bougé : entourée d'une condition fausse, la mention disparaîtrait du document.
  • Le bloc IA sera remplacé par un commentaire, rédigé par l'IA ou fourni par ROMEO.

Pourquoi des commentaires HTML#

Les marques de clause, de bloc IA et de note sont des commentaires HTML, pour trois raisons.

  • Ils sont invisibles au rendu. Le cabinet voit son modèle comme le client verra le document.
  • Ils survivent au Markdown comme au HTML. La même marque vaut dans les deux formats que l'API accepte en entrée.
  • Ils permettent de recontrôler un document après coup. Un document généré garde ses marques de clause (option conserver_marques), toujours invisibles. S'il est retouché avant signature, l'API retrouve chaque mention.

Si un éditeur de texte retire les marques, le contrôle ne tombe pas pour autant. L'API cherche le texte de chaque mention dans tout le document. Retrouvé à l'identique, il donne une simple information (marque_absente). Retrouvé avec des changements, il est traité comme une mention modifiée. Introuvable, la mention est tenue pour supprimée.

Une limite connue de l'entrée en HTML : dans un tableau, une ligne qui porte une balise Jinja ou une cellule qui porte un filtre ({{ montant | euros }}) ne survit pas au passage par un éditeur HTML, parce que la barre verticale y sépare les cellules. Le Markdown reste donc le format de référence des modèles. Au dépôt d'un modèle de base, contenu_markdown vous rend ce que l'API a compris. Pour le modèle d'un cabinet, il se lit dans son détail (GET /v1/cifs/{cif_id}/modeles/{code}).

Ce que le contrôle tient pour identique#

La comparaison ignore ce qui ne change pas le fond : la typographie (apostrophes, guillemets, tirets), les espaces, le gras et l'italique, les notes, l'espacement à l'intérieur des expressions Jinja. Deux textes qui ne diffèrent que par cela sont identiques pour le contrôle.

Les données attendues à la génération#

ROMEO envoie un objet JSON à plat pour les valeurs simples (org_name, client_first_name, profile_risk_label…). S'y ajoutent products, une liste d'objets dont les clés sont celles des variables product.* sans le préfixe, puis totals, remuneration, vigilance (une liste de phrases) et les drapeaux de condition (org_status_cif, org_status_miobsp, realestateActive, personType…). Le modèle lit ce JSON tel quel.

Une valeur mal formée est refusée : 400 requete_invalide, avec le chemin de la clé dans details. C'est le cas de products qui n'est pas une liste d'objets, ou d'un booléen là où le catalogue attend un texte.

GET /v1/catalogue-variables liste les variables, leurs groupes, les drapeaux de condition et les blocs IA connus : près de 400 variables, une trentaine de drapeaux, une quinzaine de blocs IA. L'exemple n'en montre qu'un extrait.

curl -sS "$ROMEO_URL/v1/catalogue-variables" -H "Authorization: Bearer $ROMEO_CLE"
{"variables": [{"cle": "org_orias", "libelle": "N° ORIAS de l'organisation", "type": "text",
                "groupe": "cabinet", "source": "figen"}],
 "groupes": [{"id": "cabinet", "libelle": "Cabinet"}], "drapeaux": ["org_status_cif", "personType"],
 "blocs_ia": [{"cle": "justification_recommandation", "libelle": "…", "description": "…"}]}

Avec ?code=, la réponse porte en plus modele : ce que ce modèle utilise, mention par mention. Pour le mini-modèle, une fois déposé au parcours 4.1 (?code=rapport-exemple) :

{"modele": {"code": "rapport-exemple", "version": 1, "variables": ["org_name", "…"],
            "par_clause": {"immatriculation": ["org_name", "org_orias"], "mediation": ["org_mediator_name"]},
            "variables_inconnues": [], "blocs_ia": ["justification_recommandation"]}}

Une variable hors catalogue dans un modèle de base n'est pas refusée : elle est signalée dans variables_inconnues.

4. Les parcours#

Les sept parcours se suivent. Rejoués dans l'ordre avec le mini-modèle de la partie 3, ils donnent les résultats décrits. Ils utilisent le code rapport-exemple, pour ne pas toucher aux modèles d'Inovéa, qui portent des codes comme der-conseiller ou rapport-conseil. Le cabinet fictif s'appelle CIF-0042, le conseiller u-1234. Jouez-les sur l'environnement de test : /v1 n'a pas de route pour retirer un modèle de base, le modèle d'exemple y restera.

4.1 Déposer un modèle de base#

ROMEO → PUT /v1/modeles-de-base/{code}    le texte du modèle, son type, son profil, son libellé
ROMEO ← 201                               version créée, mentions, variables, anomalies, impact

Le code du modèle est le vôtre : c'est vous qui le choisissez, dans l'adresse.

jq -Rs '{type: "rapport_conseil", profil: "conseiller", libelle: "Rapport de conseil (exemple)",
         contenu: ., format: "markdown", notes_version: "Première version",
         personnalisable: true, publier: true}' modele.md \
  | curl -sS -X PUT "$ROMEO_URL/v1/modeles-de-base/rapport-exemple" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-
{"code": "rapport-exemple", "type": "rapport_conseil", "profil": "conseiller", "libelle": "…",
 "personnalisable": true, "version": 1, "statut": "publiee", "inchange": false, "simulation": false,
 "contenu_markdown": "…",
 "clauses": [{"id": "immatriculation", "titre": "Immatriculation", "exigence": "identique",
              "facultative": false, "lignes": [8, 10], "variables": ["org_name", "org_orias"]},
             {"id": "mediation", "titre": "Médiation", "exigence": "sens",
              "facultative": false, "lignes": [13, 16], "variables": ["org_mediator_name"]}],
 "variables": ["org_name", "…"], "variables_inconnues": [],
 "blocs_ia": [{"cle": "justification_recommandation", "consigne": "…", "ligne": 21}],
 "notes": [{"texte": "…", "ligne": 5}], "anomalies": [],
 "impact": {"modeles_cif": 0, "restent_valides": 0, "a_corriger": []}}

À lire dans cette réponse :

  • 201 annonce une nouvelle version. Si le contenu est celui de la dernière version, l'API répond 200 avec "inchange": true : aucune nouvelle version n'est créée. Redéposer le contenu d'une version plus ancienne en crée une nouvelle. Une version déposée ne change plus.
  • inchange ne parle que du contenu. type, profil et libelle sont remplacés à chaque dépôt, même avec "inchange": true. personnalisable, s'il est omis, garde sa valeur enregistrée (true à la création).
  • clauses est ce que l'API a compris de vos marques. Vérifiez que toutes vos mentions obligatoires y sont.
  • anomalies relève les défauts de forme. Un simple avertissement n'empêche pas le dépôt. Une anomalie de gravité erreur le refuse : 400 gabarit_invalide, rien n'est stocké.
  • personnalisable dit si les cabinets ont le droit d'adapter ce modèle. publier décide si la version est publiée ou reste en brouillon ("statut": "brouillon").
  • Un brouillon n'a aucun effet sur les cabinets, et son impact n'est qu'une projection. Pour le publier, redéposez le même contenu avec publier: true : "inchange": true, mais "statut": "publiee".
  • Tant qu'un modèle de base n'a aucune version publiée, le dépôt d'un cabinet, les trois routes de /v1/documents et le contrôle qui ne précise pas reference.version répondent 404 introuvable.

Les valeurs admises pour type, profil et format sont dans la partie 9. Pour relire ce qui est déposé :

curl -sS "$ROMEO_URL/v1/modeles-de-base" -H "Authorization: Bearer $ROMEO_CLE"
{"modeles": [{"code": "rapport-exemple", "type": "rapport_conseil", "profil": "conseiller", "libelle": "…",
  "personnalisable": true, "version": 1, "statut": "publiee", "publiee_le": "…", "nb_clauses": 2,
  "modeles_cif": {"valide": 0, "alertes": 0, "derivant": 0}}]}

modeles_cif compte les modèles de cabinet par état. GET /v1/modeles-de-base/{code} rend le détail de la version publiée courante (à défaut, du dernier brouillon), et ?version=1 celui d'une version précise. Le paramètre ne s'écrit pas sans valeur : ?version= rend 400. La forme est celle du dépôt, sans impact, avec en plus "versions": [{"version": 1, "statut": "publiee", "cree_le": "…", "notes_version": "…"}].

4.2 Déposer le modèle d'un cabinet et lire le contrôle#

ROMEO → POST /v1/controles                      facultatif : contrôle à blanc pendant la saisie
ROMEO → PUT /v1/cifs/{cif_id}/modeles/{code}    dépôt du modèle du cabinet
ROMEO ← statut du modèle et contrôle complet    dans la même réponse
ROMEO   montre le rapport au cabinet et décide de bloquer ou non

Le cabinet part du modèle de base et l'adapte. Ici, il touche à la mention de médiation :

sed 's/saisir gratuitement le médiateur/saisir le médiateur, à vos frais/' modele.md > modele-cabinet.md

Pendant la saisie, ROMEO peut demander un contrôle à blanc. Le modèle du cabinet ne change pas d'état :

jq -Rs '{reference: {code: "rapport-exemple"}, contenu: ., format: "markdown", mode: "modele"}' \
    modele-cabinet.md \
  | curl -sS -X POST "$ROMEO_URL/v1/controles" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-

La réponse est un contrôle, avec "id": null et "enregistre": false. Le dépôt, lui, enregistre le modèle et son contrôle. cif_id est votre identifiant du cabinet. Aucun appel préalable ne crée le cabinet : le premier dépôt suffit.

jq -Rs '{contenu: ., format: "markdown"}' modele-cabinet.md \
  | curl -sS -X PUT "$ROMEO_URL/v1/cifs/CIF-0042/modeles/rapport-exemple" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-
{"cif_id": "CIF-0042", "code": "rapport-exemple", "version": 1, "statut": "alertes", "inchange": false,
 "base": {"version_controlee": 1, "version_validee": null, "version_courante": 1},
 "controle": {
   "id": "ctl_…", "enregistre": true, "cree_le": "…", "statut": "alertes",
   "resume": {"bloquantes": 1, "informations": 0, "clauses_attendues": 2, "clauses_intactes": 1,
              "clauses_reformulees": 0},
   "alertes": [{"code": "clause_alteree", "gravite": "bloquante", "niveau": "paragraphe",
     "message": "La mention « Médiation » a été modifiée et son sens n'est plus celui du modèle de base.",
     "clause_id": "mediation", "titre": "Médiation",
     "attendu": {"texte": "… vous pouvez\nsaisir gratuitement le médiateur : …", "lignes": [14, 15]},
     "constate": {"texte": "… vous pouvez\nsaisir le médiateur, à vos frais : …", "lignes": [14, 15]},
     "diff": [{"op": "egal", "texte": "… vous pouvez\nsaisir"}, {"op": "supprime", "texte": " gratuitement"},
              {"op": "egal", "texte": " le médiateur"}, {"op": "ajoute", "texte": ", à vos frais"},
              {"op": "egal", "texte": " : {{ org_mediator_name }}."}],
     "variable": null,
     "jugement": {"verdict": "sens_altere", "justification": "La saisine du médiateur n'est plus gratuite.",
                  "modele_ia": "claude-opus-5-5", "depuis_cache": true}}],
   "rapport_markdown": "…", "rapport_html": "…", "modele_annote": "…"}}

Cet exemple montre une IA branchée. Sur un service lancé en local, l'IA est simulée (ia.mode vaut factice, partie 2) : vous lirez "modele_ia": "factice" et une justification convenue.

À lire dans cette réponse :

  • statut est l'état du modèle du cabinet : valide sans alerte bloquante, alertes sinon. C'est sur lui que ROMEO décide.
  • base porte partout trois numéros de version du modèle de base : version_controlee, celle du dernier contrôle ; version_validee, la dernière sur laquelle le modèle du cabinet a été valide (null s'il ne l'a jamais été) ; version_courante, la version publiée.
  • controle dit pourquoi : le rapport prêt à afficher au cabinet (rapport_html) et, pour chaque alerte, ce qui était attendu, ce qui est constaté, la différence mot à mot et l'avis du juge. La partie 5 détaille ces champs. depuis_cache vaut ici true : le contrôle à blanc qui précède a déjà fait juger ce passage.

Si le modèle de base n'est pas personnalisable, le dépôt est refusé : 409 modele_non_personnalisable. Si un modèle de base passe à personnalisable: false après coup, la génération utilise le modèle de base pour tous les cabinets, même ceux dont le modèle était valide, et le dit ("repli_sur_base": true). Leur modèle est gardé : il resservira si le modèle de base redevient personnalisable.

Le cabinet corrige et redépose. Cette fois, il n'adapte que la zone libre :

sed 's/depuis 2009/depuis plus de quinze ans/' modele.md > modele-cabinet.md

Rejouez le PUT ci-dessus. Le modèle passe à "statut": "valide". Le contrôle garde une alerte zone_libre_modifiee, de gravité information : elle renseigne, elle ne bloque pas.

Pour relire l'état de tous les modèles d'un cabinet :

curl -sS "$ROMEO_URL/v1/cifs/CIF-0042/modeles" -H "Authorization: Bearer $ROMEO_CLE"
{"cif_id": "CIF-0042", "modeles": [{"code": "rapport-exemple", "type": "rapport_conseil", "libelle": "…",
  "utilise": "modele_cif", "suspendu": false, "statut": "valide", "version": 2,
  "base": {"version_controlee": 1, "version_validee": 1, "version_courante": 1},
  "dernier_controle": {"id": "ctl_…", "statut": "valide",
                       "resume": {"bloquantes": 0, "informations": 1, "…": "…"}, "cree_le": "…"},
  "maj_le": "…"}]}

Tous les modèles de base publiés figurent dans cette liste ; un modèle resté en brouillon n'y est pas. utilise dit lequel servirait à générer : modele_cif quand le modèle du cabinet est valide, modele_de_base quand le cabinet n'a rien déposé (statut vaut alors null), que son modèle n'est pas valide, ou que le modèle de base n'est plus personnalisable : son modèle est suspendu ("suspendu": true).

Trois autres routes complètent le parcours.

  • GET /v1/cifs/{cif_id}/modeles/{code} rend le détail d'un modèle et son dernier contrôle : la forme de la réponse du dépôt, plus contenu_markdown, suspendu et maj_le.
  • GET /v1/controles/{id} relit un contrôle enregistré.
  • DELETE /v1/cifs/{cif_id}/modeles/{code} fait revenir le cabinet au modèle de base et répond {"cif_id": "CIF-0042", "code": "rapport-exemple", "supprime": true}. Le modèle du cabinet, ses versions et ses contrôles sont supprimés : GET /v1/controles/{id} répond ensuite 404. Ne le jouez pas maintenant : la suite se sert du modèle déposé.

4.3 Mettre à jour un modèle de base#

1. PUT /v1/modeles-de-base/{code}, simulation: true    anomalies et impact, rien n'est stocké
2. PUT /v1/modeles-de-base/{code}, publier: true       nouvelle version ; les modèles de cabinet valides
                                                       passent à « derivant » et sont recontrôlés
3. GET /v1/cifs/{cif_id}/modeles/{code}                le contrôle joint dit ce qu'il reste à corriger
4. POST …/revalider                                    200 : retour à « valide » ; 409 : il reste à corriger

Inovéa raccourcit le délai de la mention de médiation :

sed 's/sous deux mois/sous un mois/' modele.md > modele-v2.md

Simuler d'abord. Le même PUT, avec simulation: true, analyse le texte et mesure l'effet sur les modèles des cabinets. Rien n'est stocké.

jq -Rs '{type: "rapport_conseil", profil: "conseiller", libelle: "Rapport de conseil (exemple)",
         contenu: ., format: "markdown", notes_version: "Délai de médiation ramené à un mois",
         personnalisable: true, publier: true, simulation: true}' modele-v2.md \
  | curl -sS -X PUT "$ROMEO_URL/v1/modeles-de-base/rapport-exemple" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-

La réponse a la même forme qu'au dépôt, avec "simulation": true. Le champ à regarder est impact :

{"impact": {"modeles_cif": 1, "restent_valides": 0,
            "a_corriger": [{"cif_id": "CIF-0042", "bloquantes": 1, "controle_id": null}]}}

Un modèle de cabinet est concerné, et il ne resterait pas valide : sa mention de médiation annonce encore deux mois. Vous savez qui prévenir avant de publier. En simulation, controle_id vaut null : aucun contrôle n'est enregistré. Après la publication, il désigne le contrôle stocké.

Publier ensuite. Rejouez la commande en passant simulation à false. L'API crée la version 2. Les modèles de cabinet qui étaient valides passent à derivant et sont recontrôlés aussitôt contre la nouvelle version. Avec "revalidation_automatique": true dans la requête, ceux qui n'ont aucune alerte bloquante redeviennent valide sans autre appel.

Lire la dérive. Le détail du modèle du cabinet porte ce nouveau contrôle, qui dit ce qu'il reste à corriger :

curl -sS "$ROMEO_URL/v1/cifs/CIF-0042/modeles/rapport-exemple" -H "Authorization: Bearer $ROMEO_CLE"
{"cif_id": "CIF-0042", "code": "rapport-exemple", "version": 2, "statut": "derivant", "…": "…",
 "controle": {"id": "ctl_…", "statut": "alertes", "resume": {"bloquantes": 1, "…": "…"}, "…": "…"},
 "contenu_markdown": "…", "maj_le": "…"}

Dans la liste des modèles du cabinet (GET /v1/cifs/CIF-0042/modeles), base montre l'écart, {"version_controlee": 2, "version_validee": 1, "version_courante": 2}, et utilise vaut modele_de_base.

Lever la dérive. ROMEO demande la revalidation :

curl -sS -X POST "$ROMEO_URL/v1/cifs/CIF-0042/modeles/rapport-exemple/revalider" \
  -H "Authorization: Bearer $ROMEO_CLE"

Si le contrôle joint est sans alerte bloquante, l'API répond 200 avec la forme du dépôt, et le modèle redevient valide. Ici, la mention de médiation du cabinet annonce encore deux mois : l'API répond 409.

{"erreur": {"code": "revalidation_impossible", "message": "…",
            "details": [{"controle_id": "ctl_…", "bloquantes": 1}]}}

Le cabinet doit reprendre son modèle et le redéposer. Le parcours 4.4 montre ce qui se passe en attendant, puis lève la dérive.

4.4 Générer un document simple#

ROMEO → POST /v1/documents/verifier-donnees    facultatif : ce qui manque, sans générer
ROMEO → POST /v1/documents                     code du modèle, cabinet, conseiller, données
ROMEO ← markdown, html, modèle utilisé, conformité, durée

Les données du dossier, fictives, dans un fichier donnees.json :

{"org_name": "Cabinet Dupont Patrimoine", "org_orias": "00000000", "org_status_cif": true,
 "org_mediator_name": "Médiateur fictif du patrimoine", "client_first_name": "Camille"}

Vérifier les données. Cet appel fait une génération à blanc, sans IA, et dit ce qui manque. Son corps ne porte que code, cif_id et donnees : il n'accepte pas options. Essayez-le en retirant une donnée :

jq 'del(.org_orias) | {code: "rapport-exemple", cif_id: "CIF-0042", donnees: .}' donnees.json \
  | curl -sS -X POST "$ROMEO_URL/v1/documents/verifier-donnees" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-
{"complet": false,
 "manquantes": [{"cle": "org_orias", "libelle": "…", "dans_clauses": ["immatriculation"], "bloquante": true}],
 "alertes": [{"code": "variable_non_resolue", "gravite": "bloquante", "niveau": "variable", "message": "…",
              "clause_id": "immatriculation", "variable": "org_orias", "…": "…"}],
 "conditions_non_renseignees": [], "inconnues": []}

bloquante vaut vrai parce que la donnée manque dans une mention obligatoire. libelle vaut null pour une variable hors catalogue. conditions_non_renseignees liste les drapeaux de condition absents, inconnues les clés envoyées que ni le catalogue ni le modèle ne connaissent. alertes a la forme des alertes de la génération : c'est ce qu'elle relèverait avec ces données, sans dépendre de l'IA (clause_vide_au_rendu, par exemple, pour une mention que les données laissent vide).

complet: true veut dire : aucune variable ne manque, et aucune de ces alertes n'est bloquante. Cela ne garantit pas un document conforme : les commentaires, rédigés ou fournis, ne sont jugés qu'à la génération.

Générer. Le modèle porte un bloc IA. Pour un rendu immédiat, on demande ici de ne pas le rédiger (blocs_ia: false) ; le parcours 4.5 s'en occupe.

jq '{code: "rapport-exemple", cif_id: "CIF-0042", conseiller_id: "u-1234", donnees: .,
     options: {formats: ["markdown", "html"], blocs_ia: false, reference_romeo: "DOC-1"}}' donnees.json \
  | curl -sS -X POST "$ROMEO_URL/v1/documents" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @- > reponse.json

jq -r .markdown reponse.json > document.md
jq -r .html reponse.json > document.html
jq 'del(.markdown, .html)' reponse.json
{"id": "doc_…", "code": "rapport-exemple", "markdown": "…", "html": "…",
 "modele": {"origine": "modele_de_base", "version_base": 2, "version_cif": null,
            "etat_modele_cif": "derivant", "repli_sur_base": true},
 "conformite": {"statut": "conforme",
                "alertes": [{"code": "bloc_ia_indisponible", "gravite": "information", "…": "…"}],
                "variables_non_resolues": [], "conditions_non_renseignees": [],
                "clauses_presentes": ["immatriculation", "mediation"], "clauses_non_applicables": []},
 "blocs_ia": [{"cle": "justification_recommandation", "statut": "indisponible", "…": "…"}],
 "produits_enrichis": [], "duree_ms": 2140}

À lire dans cette réponse :

  • modele dit quel modèle a servi, et pourquoi. Ici le modèle du cabinet dérive depuis le parcours 4.3. Seul un modèle valide sert à générer : l'API s'est repliée sur le modèle de base (repli_sur_base) et le dit.
  • conformite.statut vaut conforme s'il n'y a aucune alerte bloquante. Chaque mention obligatoire est soit présente, soit non applicable parce qu'une condition l'écarte.
  • Une donnée absente laisse dans le document le marqueur [À compléter : nom] et figure dans variables_non_resolues. Dans une mention obligatoire, c'est une alerte bloquante.
  • Le commentaire non rédigé laisse [Bloc à rédiger : justification_recommandation] et une alerte d'information.
  • html est le format d'affichage. Markdown et HTML gardent les marques de clause, invisibles, pour un contrôle ultérieur. En HTML, les marqueurs [À compléter : …] et [Bloc à rédiger : …] sont entourés de <mark class="a-completer">.

Les options de la requête, toutes facultatives :

Option Effet
formats Formats rendus : markdown, html. Par défaut, ["markdown"] seul : demandez html pour l'affichage. Un format non demandé revient à null dans la réponse, il n'en est pas absent.
si_modele_non_valide Que faire si le modèle du cabinet n'est pas valide. modele_de_base, la valeur par défaut : générer avec le modèle de base et le dire. generer : générer avec le modèle du cabinet malgré ses alertes. refuser : répondre 422 modele_non_valide.
conserver_marques Garder les marques de clause dans le document, pour pouvoir le recontrôler. Vrai par défaut.
blocs_ia Rédiger par l'IA les commentaires que ROMEO ne fournit pas. Vrai par défaut ; à faux, ils restent à rédiger.
verifier_blocs Soumettre au juge les commentaires fournis par ROMEO (parcours 4.5). Faux par défaut.
enrichir_produits Compléter, pour chaque produit qui porte un isin, les données absentes à partir du référentiel. Une valeur envoyée par ROMEO n'est jamais écrasée. Les ISIN complétés sont listés dans produits_enrichis. Vrai par défaut.
reference_romeo Votre référence du document. Elle est reprise dans le journal.

cif_id est facultatif : un consultant n'a pas de modèle propre, le modèle de base sert alors directement.

Lever la dérive, pour finir. Le cabinet repart de la nouvelle version du modèle de base et y reporte son adaptation :

sed 's/depuis 2009/depuis plus de quinze ans/' modele-v2.md > modele-cabinet.md

Rejouez le PUT du parcours 4.2. Le modèle du cabinet redevient valide, contrôlé contre la version 2, et c'est lui qui sert aux générations suivantes.

4.5 L'aller-retour du rapport de conseil#

Un rapport de conseil contient un commentaire libre. Le conseiller doit pouvoir le relire avant qu'il n'entre dans le document. D'où un aller-retour en deux appels :

ROMEO                                      API Figen
  1. POST /v1/documents/blocs         →    rédige les commentaires (l'appel qui attend l'IA)
                                      ←    blocs : cle, libelle, statut, texte, modele_ia
  2. le conseiller relit et corrige les textes dans ROMEO
  3. POST /v1/documents, avec blocs   →    insère les textes validés, rend le document
                                      ←    markdown, html, conformité (sans appel à l'IA, donc vite)

Rédiger les commentaires. Le corps est celui de POST /v1/documents, et vous pouvez l'envoyer tel quel : blocs, formats et les autres options du rendu y sont acceptés, puis ignorés. Toute autre clé est refusée (400 requete_invalide). Rien n'est stocké.

jq '{code: "rapport-exemple", cif_id: "CIF-0042", conseiller_id: "u-1234", donnees: .}' donnees.json \
  | curl -sS -X POST "$ROMEO_URL/v1/documents/blocs" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-
{"code": "rapport-exemple",
 "blocs": [{"cle": "justification_recommandation", "libelle": "…", "statut": "redige",
            "texte": "…", "modele_ia": "claude-opus-5-5"}],
 "duree_ms": 8200}

Le rédacteur ne s'appuie que sur les données envoyées et n'invente aucun chiffre. ROMEO affiche chaque texte au conseiller, qui le corrige.

Générer le document final. ROMEO renvoie les textes validés dans blocs, clé par clé :

jq --arg texte "Texte relu et validé par le conseiller." \
   '{code: "rapport-exemple", cif_id: "CIF-0042", conseiller_id: "u-1234", donnees: .,
     blocs: {justification_recommandation: $texte},
     options: {formats: ["markdown", "html"], reference_romeo: "DOC-2"}}' donnees.json \
  | curl -sS -X POST "$ROMEO_URL/v1/documents" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-

Dans la réponse, chaque commentaire de blocs_ia porte son texte et un statut. Vérifiez que chaque texte envoyé revient avec "statut": "fourni" : c'est la preuve qu'il est dans le document.

{"blocs_ia": [{"cle": "justification_recommandation", "statut": "fourni",
               "texte": "Texte relu et validé par le conseiller.", "…": "…"}], "…": "…"}
Statut Sens
redige Rédigé par l'IA.
fourni Texte envoyé par ROMEO, inséré dans le document.
indisponible Non rédigé : le document porte [Bloc à rédiger : cle] et l'alerte bloc_ia_indisponible.

Un commentaire que ROMEO ne fournit pas est rédigé par l'IA si blocs_ia est vrai, sinon laissé à rédiger. Un texte fourni vide n'est jamais rédigé à sa place par l'IA : le commentaire sort indisponible, avec son marqueur « à rédiger ». Une clé de blocs qui ne correspond à aucun commentaire du document lève l'alerte d'information bloc_fourni_inconnu : ce texte n'est pas dans le document.

Le texte fourni est lu comme du Markdown : paragraphes, listes, gras et liens sont rendus. Un titre devient un paragraphe en gras, une image est remplacée par son texte, le HTML s'affiche comme du texte, et aucune marque ne peut y être posée. Dans le document, chaque commentaire est encadré par <!-- ia-debut: cle --> et <!-- ia-fin: cle --> ; en HTML, il est en plus dans un <div class="bloc-ia" data-cle="cle">. Avec conserver_marques: false, ces repères disparaissent.

Avec "verifier_blocs": true, l'API soumet les textes fournis au juge, qui cherche une contradiction avec une mention obligatoire ou une promesse interdite : alerte ajout_contradictoire, ou ajout_a_juger si le juge n'a pas pu se prononcer. Les deux sont bloquantes.

Régler le délai d'attente. Un relais (Cloudflare) placé devant l'environnement de test coupe tout appel à 100 secondes. Réglez votre client sur 95 secondes au plus. Une coupure, délai dépassé ou code 524, n'est pas un refus de l'API : POST /v1/documents/blocs ne stocke rien, vous pouvez le relancer. Les commentaires d'un même document sont rédigés de front : la durée est celle du plus long, pas leur somme.

4.6 Contrôler un document retouché avant signature#

Un conseiller retouche parfois le document final. Avant la signature, ROMEO peut vérifier que les mentions obligatoires ont tenu.

ROMEO → POST /v1/controles, mode « document »    le modèle et les données d'origine, le texte retouché
ROMEO ← un contrôle                              mêmes alertes que pour un modèle

Reprenons le document du parcours 4.4 et retirons un mot de la mention de médiation :

sed 's/saisir gratuitement/saisir/' document.md > document-retouche.md

Figen n'a pas gardé le document d'origine. La référence se reconstruit donc à partir du modèle et des données : avec reference.donnees, l'API rend d'abord le modèle avec ces données, puis compare.

jq -n --slurpfile donnees donnees.json --rawfile retouche document-retouche.md \
   '{reference: {code: "rapport-exemple", donnees: $donnees[0]},
     contenu: $retouche, format: "markdown", mode: "document"}' \
  | curl -sS -X POST "$ROMEO_URL/v1/controles" \
      -H "Authorization: Bearer $ROMEO_CLE" -H "Content-Type: application/json" \
      --data-binary @-

La réponse est un contrôle, comme au parcours 4.2 : une alerte sur mediation, son diff, l'avis du juge. Si le document a été retouché en HTML, envoyez-le avec "format": "html".

reference n'accepte pas cif_id : un document généré avec le modèle d'un cabinet est comparé au modèle de base. Les zones libres adaptées par le cabinet et le commentaire inséré ressortent alors comme des passages modifiés, en simple information (zone_libre_modifiee) quand le juge les tient pour neutres.

ROMEO peut aussi fournir lui-même le document d'origine, s'il l'a gardé. La référence devient alors {"contenu": "…", "format": "markdown"}. type_document est facultatif ; avec reference.code, il vaut par défaut le type du modèle de base.

4.7 Suivre ce qui change#

L'API ne rappelle pas ROMEO. C'est ROMEO qui vient lire le fil des changements d'état, à son rythme.

ROMEO → GET /v1/evenements                 premier appel
ROMEO ← evenements, suivant                ROMEO garde la valeur de « suivant »
ROMEO → GET /v1/evenements?depuis=…        appels suivants : seulement ce qui est nouveau
curl -sS "$ROMEO_URL/v1/evenements?limite=1" -H "Authorization: Bearer $ROMEO_CLE"
{"evenements": [{"id": 1, "cree_le": "…", "type": "base_publiee", "code": "rapport-exemple", "cif_id": null,
                 "details": {"version": 1, "revalidation_automatique": false}}],
 "suivant": "1"}

Les événements viennent du plus ancien au plus récent, limite par appel : 100 par défaut, 1000 au plus. Vos numéros différeront si d'autres modèles ont déjà été déposés. Gardez suivant et passez-le dans depuis à l'appel suivant. Quand il n'y a rien de nouveau, la liste est vide et suivant ne change pas.

SUIVANT=1   # la valeur de « suivant » que vous venez de recevoir
curl -sS "$ROMEO_URL/v1/evenements?depuis=$SUIVANT" -H "Authorization: Bearer $ROMEO_CLE"
Type d'événement Ce qui s'est passé
base_publiee Une version d'un modèle de base a été publiée.
modele_cif_valide Le modèle d'un cabinet est valide.
modele_cif_alertes Le modèle d'un cabinet porte au moins une alerte bloquante.
modele_cif_derivant Le modèle d'un cabinet dérive : son modèle de base a changé.
modele_cif_revalide La dérive d'un modèle de cabinet a été levée.
modele_cif_supprime Un cabinet est revenu au modèle de base.

details précise l'événement : version et revalidation_automatique pour base_publiee ; controle_id, bloquantes et version_base pour un modèle de cabinet, plus sa version après un dépôt ou un appel à revalider ; version seule pour modele_cif_supprime. Après une publication avec revalidation_automatique, un modèle resté sans alerte bloquante donne directement modele_cif_revalide, avec "automatique": true, sans modele_cif_derivant avant.

Le journal des générations a ses filtres, tous facultatifs : cif_id, conseiller_id, depuis et limite (100 par défaut, 1000 au plus). Ici, depuis est une date ou une date et heure ISO 8601 (2026-11-10, 2026-11-10T09:00:00Z), pas un curseur, et les lignes viennent de la plus récente à la plus ancienne. Le journal ne contient aucun document, aucune donnée de client :

curl -sS "$ROMEO_URL/v1/journal?cif_id=CIF-0042&limite=20" -H "Authorization: Bearer $ROMEO_CLE"
{"generations": [{"id": "doc_…", "cree_le": "…", "code": "rapport-exemple", "cif_id": "CIF-0042",
                  "conseiller_id": "u-1234", "modele": {}, "statut": "conforme", "nb_alertes": 0,
                  "reference_romeo": "DOC-2", "duree_ms": 2140}]}

5. Lire un contrôle#

Les états#

Un contrôle est valide s'il n'a aucune alerte bloquante, alertes sinon. Un document généré suit la même règle : conforme ou alertes. Le modèle d'un cabinet connaît trois états :

État Sens
valide Contrôlé sans alerte bloquante contre la version courante du modèle de base.
alertes Au moins une alerte bloquante.
derivant Il était valide, le modèle de base a été republié. Il a été recontrôlé contre la nouvelle version (le contrôle joint dit ce qu'il reste à corriger) et attend une revalidation.

Seul un modèle valide sert à générer ; sinon l'option si_modele_non_valide décide (parcours 4.4). Un modèle valide peut porter des alertes d'information : elles disent ce que le cabinet a changé sans que cela pose de problème.

Les codes d'alerte#

Une alerte est bloquante ou vaut information. Seules les bloquantes font passer un modèle à alertes. Son niveau dit sur quoi elle porte : clause, paragraphe, variable, zone_libre ou document. Voici les codes à ce jour ; d'autres pourront s'ajouter (partie 8). Au contrôle d'un modèle, ou d'un document retouché :

Code Gravité Niveau Libellé Quoi faire
clause_supprimee bloquante clause Mention obligatoire supprimée Remettre la mention telle qu'elle figure dans le modèle de base.
clause_alteree bloquante paragraphe Mention obligatoire dont le sens a changé Reprendre la rédaction du modèle de base, ou reformuler sans en changer la portée.
clause_non_identique bloquante paragraphe Mention à reprendre mot pour mot, modifiée Rétablir le texte exact du modèle de base : cette mention ne se reformule pas.
clause_a_juger bloquante paragraphe Mention modifiée, jugement indisponible Relancer le contrôle plus tard : le juge n'a pas pu se prononcer et, dans le doute, le modèle reste bloqué.
variable_retiree bloquante variable Variable retirée d'une mention obligatoire Remettre la variable dans la mention : sans elle, l'information ne figurerait plus dans les documents.
condition_modifiee bloquante clause Condition modifiée dans ou autour d'une mention obligatoire Rétablir les conditions et les boucles du modèle de base, dans la mention comme autour d'elle : entourée d'une condition fausse, une mention disparaîtrait du document.
jinja_invalide bloquante document Syntaxe du modèle invalide : aucun document ne peut être généré Corriger la syntaxe à la ligne indiquée : tant qu'elle est fausse, aucun document ne peut être généré avec ce modèle. Un commentaire jamais fermé lève aussi cette alerte : une marque à laquelle il manque -->, par exemple.
ajout_contradictoire bloquante zone_libre Ajout qui contredit une mention obligatoire Retirer ou corriger l'ajout.
ajout_a_juger bloquante zone_libre Passage ajouté ou modifié, jugement indisponible Relancer le contrôle plus tard : tant que le passage n'a pas pu être jugé, le modèle reste bloqué.
jinja_ajoute bloquante clause ou document Variable, condition ou instruction ajoutée par le cabinet Retirer ce qui a été ajouté : une mention n'admet que les variables du modèle de base, et seules des conditions ou des boucles peuvent être ajoutées ailleurs.
mise_en_forme_masquante bloquante clause Mise en forme qui masque ou modifie une mention Retirer de la mention le texte barré, le HTML, l'image, le code ou les caractères invisibles : elle doit s'afficher telle qu'elle est écrite.
rendu_divergent bloquante clause ou document Mention qui ne s'afficherait pas comme elle a été contrôlée Repartir du modèle de base autour de cette mention : un élément placé hors d'elle en modifie ou en empêche l'affichage.
bloc_ia_modifie bloquante document Commentaire à rédiger ajouté, retiré ou modifié Rétablir les commentaires à rédiger et leurs consignes tels qu'ils sont dans le modèle de base.
clause_reformulee information paragraphe Mention reformulée, sens conservé Rien à faire : la reformulation est acceptée.
clause_facultative_retiree information clause Mention facultative retirée Rien à faire : le cabinet peut retirer cette mention quand elle ne le concerne pas.
marque_absente information clause Marque de clause absente, texte retrouvé Rien d'obligatoire ; remettre les marques facilite les contrôles suivants.
clause_deplacee information clause Mention déplacée Rien à faire ; vérifier que l'ordre reste logique à la lecture.
clause_inconnue information clause Clause inconnue du modèle de base Vérifier l'identifiant : il n'existe pas dans le modèle de base.
zone_libre_modifiee information zone_libre Zone libre modifiée Rien à faire : le cabinet est libre d'adapter ces passages.
marques_abimees information document Marques abîmées Corriger les marques signalées. Le contrôle a été mené malgré tout. Si une marque n'est plus fermée par --> (<!-- /clause seul, par exemple), s'y ajoute jinja_invalide, bloquante.

À la génération d'un document :

Code Gravité Niveau Libellé Quoi faire
variable_non_resolue bloquante dans une clause, information ailleurs variable Donnée manquante à la génération Compléter la donnée et régénérer ; bloquante si elle se trouve dans une mention obligatoire.
clause_vide_au_rendu bloquante clause Mention obligatoire vide après rendu Envoyer les données qui alimentent cette mention : une condition l'a vidée, elle sortirait vide dans le document.
condition_non_renseignee information variable Condition non renseignée, tenue pour fausse Envoyer la donnée de condition, vraie ou fausse, pour lever le doute.
bloc_ia_indisponible information document Commentaire non rédigé Rédiger le commentaire à la main, ou relancer la rédaction.
bloc_fourni_inconnu information document Texte fourni pour un commentaire que le document ne porte pas Vérifier la clé du commentaire : le texte n'a pas été inséré dans le document.

Ce que contient un contrôle#

Champ Contenu
statut valide ou alertes.
resume Le nombre d'alertes bloquantes et d'informations, et le compte des mentions : clauses_attendues, clauses_intactes, clauses_reformulees.
alertes La liste des alertes, détaillée ci-dessous.
rapport_markdown, rapport_html Le rapport en français, lisible tel quel par un conseiller : une ligne de synthèse, une section par alerte bloquante, puis les informations. Dans le Markdown, le diff s'écrit ~~supprimé~~ et **ajouté**.
modele_annote Le texte contrôlé, où chaque passage en cause est encadré par une marque d'alerte.

La marque d'alerte du modèle annoté s'écrit <!-- alerte: code | clause: id -->…<!-- /alerte -->.

Chaque alerte porte :

Champ Contenu
code, gravite, niveau Voir les tableaux ci-dessus.
message La phrase à montrer telle quelle.
clause_id, titre La mention concernée et son libellé, s'il y en a un.
attendu, constate Le texte du modèle de base et celui du texte contrôlé, avec leurs lignes.
diff La différence mot à mot : une suite d'opérations egal, supprime, ajoute.
variable La variable en cause, pour les alertes de niveau variable.
jugement L'avis du juge, quand il a été consulté : verdict, justification, modele_ia, depuis_cache.

Le juge : son rôle, ses limites#

Le contrôle commence toujours par une comparaison mot à mot, sans IA. Le juge n'intervient qu'ensuite, et seulement sur les passages modifiés. Une mention intacte ne lui est jamais soumise.

Passage modifié Verdict Alerte
Mention d'exigence sens sens_conserve clause_reformulee, information
Mention d'exigence sens sens_altere clause_alteree, bloquante
Mention d'exigence sens indisponible clause_a_juger, bloquante
Zone libre ajoutée ou modifiée neutre zone_libre_modifiee, information
Zone libre ajoutée ou modifiée contradictoire ajout_contradictoire, bloquante
Zone libre ajoutée ou modifiée indisponible ajout_a_juger, bloquante

Certains écarts ne se discutent pas, et le juge n'est pas consulté : une mention supprimée, une mention d'exigence identique modifiée, une variable retirée d'une mention, une condition modifiée. L'alerte est bloquante d'office.

Ce que le contrôle garantit sans l'IA#

Un modèle n'est jamais valide parce que le juge a été convaincu, absent ou contourné. Ces règles sont appliquées par le code, avant tout jugement, et bloquent d'office :

  • Ce qui est en commentaire est absent. Un texte de mention mis en commentaire est une mention supprimée, et ce que le contrôle tient pour absent n'est jamais dans le document généré.
  • Une mention doit s'afficher comme elle a été contrôlée (rendu_divergent). Le contrôle rend le modèle du cabinet avec des données d'épreuve et compare, mention par mention, le texte qui s'afficherait à celui qu'il a examiné. Un élément placé hors de la mention qui la cache, la remplace ou en change une valeur est ainsi repéré, quelle que soit l'astuce.
  • Le cabinet n'ajoute pas d'instructions au modèle (jinja_ajoute). Dans une mention, seules les variables et conditions du modèle de base sont admises. Ailleurs, le cabinet peut ajouter des conditions et des boucles, rien d'autre : une instruction qui redéfinirait une variable changerait le contenu d'une mention sans y toucher.
  • Pas de mise en forme qui masque (mise_en_forme_masquante) : texte barré, HTML brut, image, code ou caractères invisibles dans une mention ; et, partout dans le modèle, HTML brut, bloc de code ou caractères invisibles que le modèle de base ne porte pas. Dans un modèle ou un document envoyé en HTML, ce que l'éditeur a rendu invisible (attribut hidden, display:none…) est traité de même.
  • Les commentaires à rédiger sont ceux du modèle de base (bloc_ia_modifie) : un cabinet ne peut ni en ajouter, ni en retirer, ni en changer la consigne.
  • Un passage non jugé bloque (clause_a_juger, ajout_a_juger) : si le juge ne peut pas se prononcer sur une mention modifiée ou sur un passage ajouté, le modèle n'est pas validé.

Ces garanties portent sur ce que l'API rend. Pour qu'elles vaillent jusqu'à la signature, affichez et faites signer le HTML que l'API fournit (html) : c'est lui qui fait référence. Un document retouché se recontrôle avant signature (parcours 4.6). L'API ne lit pas vos feuilles de style : une mention cachée par une classe CSS de votre côté lui échappe.

Les champs Markdown rendus par l'API (markdown, modele_annote, contenu_markdown) reprennent des textes écrits par des cabinets ou venus de vos données. Affichez-les avec un rendu qui n'interprète pas le HTML brut, ou utilisez le HTML que l'API fournit (html, rapport_html), déjà neutralisé.

Ce que vous pouvez attendre d'un jugement :

  • Il est justifié. Chaque verdict vient avec une justification d'une ou deux phrases et le nom du modèle d'IA qui l'a rendu (modele_ia).
  • Il est mémorisé. Une même paire de textes n'est jugée qu'une fois. Si elle revient, l'avis sort de la mémoire et la réponse le dit : "depuis_cache": true.
  • Il est rejouable. Le même modèle redéposé reçoit le même avis. Deux cabinets qui font la même modification reçoivent la même réponse.
  • Dans le doute, il bloque. Si le juge ne peut pas se prononcer sur une mention modifiée ou sur un passage ajouté, le modèle n'est pas validé. Un avis indisponible n'est pas mémorisé : relancer le contrôle suffit.

Ses limites, pour être clairs. Le juge donne un avis sur le sens d'un texte. Il peut se tromper : sa justification est là pour qu'un humain puisse le vérifier. Et il ne décide de rien. Il alerte, ROMEO décide.

6. Ce que nous proposons de plus simple, et pourquoi#

Sur huit points, l'API fait un choix plus simple que ce qu'on attend souvent d'un service de ce genre. Pour chacun : l'idée de départ, notre proposition, et ce que vous y gagnez.

Idée Ce que vous imaginiez peut-être Ce que nous proposons Pourquoi
Vos identifiants plutôt que les nôtres Créer chaque modèle et chaque cabinet chez Figen, recevoir un identifiant Figen, le stocker dans ROMEO et tenir la correspondance. Les adresses portent vos clés : le code du modèle, votre cif_id, votre conseiller_id, votre reference_romeo. Aucune table de correspondance à tenir de votre côté. Un PUT sur votre clé crée ou met à jour, et redéposer le même contenu ne crée pas de nouvelle version (inchange).
Des réponses immédiates plutôt que des rappels Déposer un modèle, puis attendre que Figen rappelle ROMEO quand le contrôle est prêt. Le contrôle est dans la réponse du dépôt. Les changements d'état survenus entre deux appels se lisent dans /v1/evenements, quand vous le voulez. ROMEO n'a pas de point d'entrée à exposer ni de rappel perdu à rattraper. Un seul sens de circulation, facile à tester.
Simuler avant de publier Publier la nouvelle version d'un modèle de base, puis découvrir combien de cabinets se retrouvent en dérive. simulation: true sur le dépôt : la réponse donne les anomalies et l'effet sur les modèles des cabinets, sans rien stocker. Inovéa sait qui sera touché avant de publier, et peut prévenir les cabinets concernés.
Se replier sur le modèle de base Refuser de générer tant que le modèle du cabinet porte une alerte. Par défaut, la génération se replie sur le modèle de base et le dit (repli_sur_base). Avec si_modele_non_valide, ROMEO peut préférer refuser, ou générer malgré les alertes. Un conseiller n'est jamais bloqué devant son client parce que le modèle de son cabinet attend une correction. Le document sort quand même, avec les mentions dans la rédaction d'Inovéa.
Contrôler à blanc pendant la saisie Déposer le modèle pour connaître ses alertes, corriger, redéposer. POST /v1/controles contrôle n'importe quel texte sans le déposer. ROMEO peut montrer les alertes dans son éditeur, avant le dépôt. Le cabinet corrige au fil de l'eau et l'historique de ses dépôts reste propre.
Rédiger les commentaires à part Un seul appel qui rédige le commentaire et rend le document. Deux appels : POST /v1/documents/blocs rédige, POST /v1/documents rend. Les temps de réponse sont maîtrisés : le rendu ne dépend pas de l'IA et répond vite (parcours 4.5). ROMEO sait quand afficher une attente, et le conseiller relit le commentaire avant qu'il n'entre dans le document.
Vérifier les données sans générer Générer le document pour découvrir qu'il manque une donnée. POST /v1/documents/verifier-donnees dit ce qui manque, dans quelles mentions, et si c'est bloquant. ROMEO peut signaler au conseiller les champs à compléter avant de lancer la génération. L'appel se fait sans IA.
Des mentions qu'un cabinet peut retirer Un modèle par combinaison de statuts, ou des sections laissées en zone libre pour que chaque cabinet enlève celles qui ne le concernent pas. presence: facultative sur la clause : le cabinet peut la retirer en entier, pas la dénaturer. Les modèles d'un groupement contiennent des sections qu'un cabinet doit retirer quand elles ne le concernent pas, par exemple un statut qu'il n'a pas. Il fallait le permettre sans ouvrir la porte à leur réécriture : retirée, la section est signalée pour information ; gardée, elle est contrôlée comme les autres.

7. Données et confidentialité#

Ce qui est stocké#

Donnée Ce que nous gardons
Clés d'API Le nom, le préfixe et un hachage SHA-256 de la clé, avec ses dates. Jamais la clé elle-même.
Modèles de base Chaque version, avec son contenu, ses mentions et ses variables. Une version ne change plus.
Modèles des cabinets Chaque version déposée, son statut et la version de base sur laquelle elle a été validée. Un DELETE supprime le modèle du cabinet, ses versions et ses contrôles.
Contrôles Le statut, le résumé, les alertes, le rapport et le modèle annoté.
Jugements Le verdict, sa justification et le modèle d'IA, pour ne pas juger deux fois la même modification.
Journal des générations Une ligne sans contenu : identifiant, qui, quand, modèle et versions, statut, empreinte.
Événements Le fil des changements d'état.
Référentiel produits Un extrait du référentiel des fonds et contrats, par ISIN.

Ce qui ne l'est pas#

  • Les documents générés. Ils partent dans la réponse et ne sont pas gardés.
  • Les données de vos clients. Les donnees envoyées servent au rendu, puis disparaissent.
  • Les essais. Un contrôle à blanc (POST /v1/controles), un dépôt avec simulation: true et une rédaction de commentaires (POST /v1/documents/blocs) n'enregistrent ni le texte envoyé ni leur résultat. Seuls les avis du juge sur un passage de modèle sont mémorisés : empreintes des deux textes, verdict et justification, laquelle peut citer quelques mots du modèle.
  • Les avis du juge sur un document. Quand le texte jugé est un document généré ou un commentaire (contrôle d'un document retouché, option verifier_blocs), il peut contenir des données de client : rien n'est mémorisé, et l'avis n'existe que dans la réponse.

Où#

La machine et la base de données sont en France ; les modèles d'IA traitent dans l'Union européenne, pas seulement en France. Le modèle utilisé est Claude Opus, appelé par Amazon Bedrock (région eu-west-3).

Ce que voit l'IA#

  • Au contrôle : les passages modifiés et, pour y confronter les zones libres, les mentions obligatoires du modèle de base. Un texte déposé sans aucun changement ne donne lieu à aucun appel à l'IA.
  • À la rédaction d'un commentaire : les données envoyées pour ce document, et le document déjà rendu, qui sert de contexte. Si vous ne voulez pas d'IA sur un document, passez blocs_ia à faux, ou fournissez vous-mêmes les textes dans blocs.

Vos modèles#

Les modèles d'Inovéa sont confidentiels. Aucune page publique n'en montre une ligne : un modèle ne se lit que par des appels à l'API, avec une clé, et ce guide n'utilise que des exemples inventés. Les essais et les démonstrations se font sur des données fictives.

À confirmer#

  • La durée de conservation des modèles, des contrôles et du journal, et les modalités de suppression.

8. Nos engagements#

Versions, préavis, journal des changements#

  • Une API versionnée. Toutes les routes portent le préfixe /v1. Aucune rupture n'y sera introduite : un changement incompatible passe par une nouvelle version de l'API. Ajouter un champ à une réponse ou un code d'alerte n'est pas une rupture : décidez sur gravite (bloquante ou information), et traitez un code que vous ne connaissez pas encore selon sa gravité.
  • Un préavis. Vous êtes prévenus avant tout changement de l'API. Le délai de préavis et le canal d'annonce sont à confirmer.
  • Un journal des changements. Chaque évolution de /v1 est consignée dans CHANGELOG-API.md, remis avec ce guide. Sur le service, il se lit à la fin de la page /guide.

Environnement de test et temps de réponse#

  • Une plateforme de test. Vous disposez d'un environnement de test et de cette documentation. L'adresse vous est remise avec votre clé ; /v1/moi rappelle dans quel environnement une clé travaille. Les essais s'y font sur des données fictives. L'adresse de production est à confirmer.
  • Des temps de réponse maîtrisés. Le rendu d'un document est immédiat, la rédaction par l'IA est plus lente : c'est pour cela que les deux sont séparés (parcours 4.5). Les réponses de génération portent leur durée (duree_ms). Le relais placé devant l'environnement de test coupe tout appel à 100 secondes (parcours 4.5). Les temps de réponse garantis, la disponibilité du service et le maintien de cette limite en production sont à confirmer.

9. Référence rapide#

Les routes#

Authentification : Authorization: Bearer suivi de la clé, sur tout /v1 sauf /v1/sante.

Méthode Route Rôle
GET /v1/sante État du service, mode de l'IA, base de données. Sans clé. Répond aussi à HEAD, sans corps.
GET /v1/moi À qui appartient la clé, environnement.
GET /v1/catalogue-variables?code= Variables et blocs IA ; avec code, ce que ce modèle utilise.
GET /v1/modeles-de-base Liste des modèles de base et compte des modèles de cabinet par état.
PUT /v1/modeles-de-base/{code} Déposer une version (analyse, anomalies, impact). simulation: true ne stocke rien.
GET /v1/modeles-de-base/{code}?version= Détail d'une version ; sans version, la version publiée courante.
PUT /v1/cifs/{cif_id}/modeles/{code} Déposer le modèle d'un cabinet. La réponse contient le contrôle.
GET /v1/cifs/{cif_id}/modeles État de tous les modèles d'un cabinet, et lequel servirait à générer.
GET /v1/cifs/{cif_id}/modeles/{code} Détail et dernier contrôle.
POST /v1/cifs/{cif_id}/modeles/{code}/revalider Lever la dérive si le dernier contrôle est sans alerte bloquante.
DELETE /v1/cifs/{cif_id}/modeles/{code} Revenir au modèle de base : le modèle du cabinet, ses versions et ses contrôles sont supprimés.
POST /v1/controles Contrôle à blanc de n'importe quel texte. Rien n'est stocké.
GET /v1/controles/{id} Relire un contrôle stocké.
POST /v1/documents Générer un document et contrôler le rendu.
POST /v1/documents/blocs Rédiger seulement les commentaires libres, pour relecture dans ROMEO.
POST /v1/documents/verifier-donnees Dire ce qui manque dans les données, sans générer.
GET /v1/journal Journal des générations, sans contenu. Filtres : cif_id, conseiller_id, depuis (une date), limite.
GET /v1/evenements?depuis= Fil des changements d'état. depuis est le curseur suivant ; limite borne la réponse.

Les schémas complets sont dans /docs et /openapi.json. Un paramètre d'adresse (?code=, ?version=, ?depuis=) se donne avec une valeur ou s'omet. Les adresses s'écrivent sans barre finale : /v1/moi/ rend 404 introuvable.

Les valeurs admises#

Champ Valeurs
type der, lettre_mission, mandat_courtage, rapport_conseil, declaration_adequation, mandat_rto, autre
profil conseiller, consultant, tous
format markdown (par défaut), html
mode d'un contrôle modele (par défaut), document
si_modele_non_valide modele_de_base (par défaut), generer, refuser
Statut d'un commentaire redige, fourni, indisponible
code d'un modèle 2 à 63 caractères : minuscules sans accent, chiffres et tirets, sans tiret en tête
cif_id, conseiller_id 1 à 64 caractères : lettres sans accent, chiffres, ., _ et -
Clé d'un commentaire, dans blocs 1 à 64 caractères : minuscules sans accent, chiffres et _, une lettre en tête

Champs obligatoires : type, profil, libelle et contenu pour un modèle de base ; contenu pour le modèle d'un cabinet ; contenu et reference (avec code ou contenu, jamais les deux) pour un contrôle ; code pour les trois routes de /v1/documents. Tous les autres champs sont facultatifs.

Les limites#

Quoi Limite Au-delà
Corps d'une requête 2 Mo ; 32 niveaux d'imbrication 413 corps_trop_grand ; 400 requete_invalide
Texte d'un modèle ou d'un document (contenu) 500 000 caractères 413 corps_trop_grand
donnees.products 200 produits 400 requete_invalide
Document rendu 300 000 caractères 422 generation_impossible
libelle, notes_version, reference_romeo 300, 2 000 et 128 caractères 400 requete_invalide

Les erreurs#

Toutes les erreurs ont la même forme :

{"erreur": {"code": "modele_non_personnalisable", "message": "…", "details": []}}

code est fait pour votre programme, message pour un humain, details précise quand il y a lieu.

HTTP Code d'erreur Quand
400 requete_invalide Corps ou paramètres mal formés : JSON illisible, champ obligatoire absent, champ inconnu, valeur non admise, donnée mal formée. details nomme chaque champ refusé (champ, emplacement, message).
400 gabarit_invalide Modèle refusé pour sa forme : au dépôt d'un modèle de base, une anomalie de gravité erreur. details liste les anomalies. Rien n'est stocké.
400 format_incoherent Le format annoncé contredit le contenu : du Markdown annoncé html, par exemple.
401 cle_absente, cle_invalide En-tête Authorization absent ou mal formé ; clé inconnue ou révoquée.
404 introuvable Adresse, modèle de base, version, modèle de cabinet ou contrôle inconnu. details dit lequel (ressource).
405 methode_non_permise Méthode HTTP non prévue sur cette adresse.
409 modele_non_personnalisable Un cabinet dépose un modèle alors que le modèle de base ne l'autorise pas.
409 revalidation_impossible Revalidation demandée alors que le dernier contrôle porte une alerte bloquante, ou un passage que le juge n'a pas pu juger. details donne le contrôle et le nombre d'alertes bloquantes.
409 depots_simultanes Le même modèle est modifié par plusieurs appels à la fois : rejouez l'appel.
413 corps_trop_grand Corps de plus de 2 Mo, ou texte de plus de 500 000 caractères.
422 modele_non_valide Génération demandée avec si_modele_non_valide: "refuser" alors que le modèle du cabinet n'est pas valide.
422 generation_impossible Le modèle ne peut pas être rendu avec ces données : un document de plus de 300 000 caractères, par exemple. details dit pourquoi.
500 erreur_interne Défaut du service. details porte l'identifiant de la requête, à nous transmettre.
503 service_degrade /v1/sante seulement, quand la base de données ne répond pas.

Dans tous les cas, message dit ce qui ne va pas. Le 503 de /v1/sante garde le corps habituel de la route, avec "statut": "degrade", et y ajoute l'objet erreur. Une réponse qui vient du relais, et non de l'API, ne suit pas cette forme : une coupure (délai dépassé ou code 524, parcours 4.5), par exemple.

Chaque réponse porte un en-tête X-Request-Id : le vôtre si vous en envoyez un (1 à 64 lettres sans accent, chiffres, ., _ ou -), sinon celui que l'API crée. Gardez-le, et joignez-le à toute question.

Journal des changements de l'API#

Toute évolution de /v1 est consignée ici, la plus récente en premier. Aucune rupture n'est introduite dans /v1 : un changement incompatible passe par une nouvelle version de l'API, annoncée à l'avance.

v1 : corrections du 10 novembre 2026 (recette)#

Corrections faites après une relecture de l'API du point de vue d'un développeur client, avant toute mise en production. Les champs ajoutés ne retirent rien. Les corrections qui refusent ou changent ce qui passait à tort sont marquées « à vérifier de votre côté ».

Documents#

  • POST /v1/documents/verifier-donnees : la réponse porte un nouveau champ alertes, de la même forme que conformite.alertes d'un document. Il liste ce que la génération relèverait avec ces données (variable_non_resolue, clause_vide_au_rendu, condition_non_renseignee), hors alertes des commentaires à rédiger. complet suit désormais cette règle : complet: true veut dire que générer maintenant avec ce corps ne lèvera aucune alerte bloquante due aux données. Avant, complet pouvait valoir true alors qu'une mention obligatoire sortait vide (clause_vide_au_rendu).
  • POST /v1/documents/blocs : à vérifier de votre côté. La route accepte le corps complet de POST /v1/documents (blocs et toutes les options, qu'elle ignore si elle ne les lit pas) et refuse désormais toute autre clé, 400 requete_invalide, comme les autres routes. Avant, une clé inconnue, donc une faute de frappe, passait sans rien dire.
  • POST /v1/documents, /v1/documents/blocs, /v1/documents/verifier-donnees, et reference.donnees de POST /v1/controles : à vérifier de votre côté. Une donnée dont la forme contredit le catalogue de variables est refusée, 400 requete_invalide, et details[].champ donne son chemin (donnees.products.0.name) : products qui n'est pas une liste d'objets, totals, suitability ou remuneration qui n'est pas un objet, une liste ou un objet pour une valeur simple, un booléen pour autre chose qu'un oui/non. Avant, la valeur était rendue telle quelle ("org_orias": false s'écrivait « Non »). Restent admis : un nombre pour un texte, un texte pour un montant, une liste de phrases pour un texte, une clé hors catalogue, et null, qui vaut absent.
  • POST /v1/documents, champ blocs : à vérifier de votre côté. Un commentaire renvoyé vide (chaîne vide ou faite de blancs) dit que le conseiller l'a vidé : il reste à rédiger (statut: "indisponible", marqueur [Bloc à rédiger : cle], alerte bloc_ia_indisponible), sans appel à l'IA ni au juge pour cette clé. Avant, l'API le faisait rédiger par l'IA (statut: "redige"). null est désormais admis et vaut non fourni : le commentaire est rédigé comme si la clé n'était pas envoyée (avant : 400 requete_invalide).

Modèles de base et modèles des cabinets#

  • PUT /v1/modeles-de-base/{code} : à vérifier de votre côté. personnalisable omis ne vaut true qu'à la création du modèle ; au redépôt, il garde sa valeur enregistrée. Avant, un redépôt qui ne le répétait pas remettait true.
  • PUT /v1/modeles-de-base/{code} : inchange est précisé, sans changer de sens. inchange: true : aucune nouvelle version n'a été créée, le contenu est celui de la dernière version. type, profil, libelle et personnalisable suivent quand même ce dépôt, et un brouillon redéposé avec publier: true est publié : la réponse montre l'état d'après l'appel.
  • Modèle de cabinet suspendu : à vérifier de votre côté. Quand un modèle de base passe à personnalisable: false, les modèles déjà déposés par les cabinets sont gardés mais ne servent plus à générer : le modèle de base sert, quelle que soit l'option si_modele_non_valide, sans refus. La génération le dit par modele.repli_sur_base: true (modele.etat_modele_cif reste l'état réel du modèle du cabinet). Avant, ces modèles continuaient de servir. Si la base redevient personnalisable, ils resservent tels quels.
  • GET /v1/cifs/{cif_id}/modeles et GET /v1/cifs/{cif_id}/modeles/{code} (ainsi que la réponse de revalider) : nouveau champ suspendu. Dans la liste, utilise vaut alors modele_de_base.
  • Message de 400 gabarit_invalide au dépôt d'un modèle de base : accord corrigé (« 2 anomalies de forme qui empêchent de l'accepter »).

Service#

  • HEAD /v1/sante : 200, ou 503 si la base ne répond pas, sans corps, pour les sondes de surveillance.
  • GET /v1/sante : nouveau champ revision, l'identifiant de la révision du code servie. Il change à chaque mise à jour du service ; rien d'autre ne change dans la réponse.

v1 : version initiale (novembre 2026, recette)#

Première version de l'API, ouverte sur l'environnement de test. La date de mise en production est à confirmer.

Service#

  • GET /v1/sante : état du service, mode de l'IA, base de données (sans clé) ; 503 avec statut: "degrade" si la base ne répond pas.
  • GET /v1/moi : à qui appartient la clé, environnement.
  • GET /v1/catalogue-variables?code= : variables et blocs IA ; avec code, ce qu'un modèle utilise.

Modèles de base#

  • GET /v1/modeles-de-base : liste des modèles de base et compte des modèles de cabinet par état.
  • PUT /v1/modeles-de-base/{code} : déposer une version (analyse, anomalies, impact sur les modèles des cabinets) ; simulation: true ne stocke rien.
  • GET /v1/modeles-de-base/{code}?version= : détail d'une version.
  • Marquage : une clause peut porter presence: facultative (le cabinet peut la retirer en entier, pas la dénaturer) ; chaque clause des réponses porte facultative.

Modèles des cabinets#

  • PUT /v1/cifs/{cif_id}/modeles/{code} : déposer le modèle d'un cabinet ; la réponse contient le contrôle.
  • GET /v1/cifs/{cif_id}/modeles : état de tous les modèles d'un cabinet, et lequel servirait à générer.
  • GET /v1/cifs/{cif_id}/modeles/{code} : détail et dernier contrôle.
  • POST /v1/cifs/{cif_id}/modeles/{code}/revalider : lever la dérive si le dernier contrôle est sans alerte bloquante ; sinon 409 revalidation_impossible.
  • DELETE /v1/cifs/{cif_id}/modeles/{code} : revenir au modèle de base.

Contrôles#

  • POST /v1/controles : contrôle à blanc de n'importe quel texte, rien n'est stocké.
  • GET /v1/controles/{id} : relire un contrôle stocké.

Documents#

  • POST /v1/documents : générer un document et contrôler le rendu.
  • POST /v1/documents/blocs : rédiger seulement les commentaires libres d'un document, pour relecture dans ROMEO.
  • POST /v1/documents/verifier-donnees : dire ce qui manque dans les données, sans générer.

Suivi#

  • GET /v1/journal : journal des générations, sans contenu.
  • GET /v1/evenements?depuis= : fil des changements d'état.

Conventions#

  • Authentification par Authorization: Bearer sur tout /v1, sauf /v1/sante.
  • Erreurs de la forme {"erreur": {"code": "…", "message": "…", "details": […]}}, avec les codes HTTP 400, 401, 404, 405, 409, 413, 422, 500 ou 503.
  • Codes d'erreur : requete_invalide, gabarit_invalide, format_incoherent (400) ; cle_absente, cle_invalide (401) ; introuvable (404) ; methode_non_permise (405) ; modele_non_personnalisable, revalidation_impossible, depots_simultanes (409) ; corps_trop_grand (413) ; modele_non_valide, generation_impossible (422) ; erreur_interne (500) ; service_degrade (503, sur /v1/sante).
  • États d'un modèle de cabinet : valide, alertes, derivant. Seul un modèle valide sert à générer ; sinon l'option si_modele_non_valide décide.
  • Statuts d'un bloc de commentaire : redige, fourni, indisponible.

Codes d'alerte#

Les codes d'alerte sont décrits dans le guide (partie 5) : gravité, libellé et quoi faire. Un code peut s'ajouter sans changer de version : un client décide sur gravite.

  • Au contrôle : clause_supprimee, clause_alteree, clause_non_identique, clause_a_juger, variable_retiree, condition_modifiee, jinja_invalide, jinja_ajoute, mise_en_forme_masquante, rendu_divergent, bloc_ia_modifie, ajout_a_juger, ajout_contradictoire, clause_reformulee, clause_facultative_retiree, marque_absente, clause_deplacee, clause_inconnue, zone_libre_modifiee, marques_abimees.
  • À la génération : variable_non_resolue, clause_vide_au_rendu, condition_non_renseignee, bloc_ia_indisponible, bloc_fourni_inconnu.