{"openapi":"3.1.0","info":{"title":"TheoFons — API du corpus","description":"\nLe corpus de **TheoFons** : Écriture, Magistère, Pères, et l'**apparat** qui les relie\n(`out` = ce qu'un passage cite, `in` = qui le cite). Lecture seule, sans clé, sans compte.\n\nCette page est le schéma brut. La documentation rédigée — adresses, conventions de psautier,\nsens de l'apparat, exemples — est ici : [français](https://theofons.net/fr/api) ·\n[English](https://theofons.net/en/api).\n\n### Versionnage\n\nLe préfixe **`/v1` est l'unité de contrat**, et le seul engagement pris.\n\n* **Sans préavis, à l'intérieur de `/v1`** : ajout d'un endpoint, d'un paramètre facultatif,\n  d'un champ dans une réponse. Un client qui ignore ce qu'il ne connaît pas ne casse pas.\n* **Jamais à l'intérieur de `/v1`** : retrait ou renommage d'un champ ou d'un paramètre,\n  changement de son type, de son sens ou de sa valeur par défaut. Ces changements-là créent\n  `/v2`, qui est servi **à côté** de `/v1` — l'ancien préfixe ne disparaît pas le jour où le\n  nouveau paraît.\n* Le corpus, lui, s'enrichit en continu : de nouveaux documents et de nouveaux liens d'apparat\n  apparaissent sans changer de version. La *forme* est un contrat, le *contenu* ne l'est pas.\n\n### Ce que ce schéma ne décrit pas\n\nCe schéma est **exhaustif pour l'API publique** : tout ce qui est offert à un client extérieur\nfigure ci-dessous. Le serveur porte par ailleurs la mécanique interne du site theofons.net, qui\nn'est pas publiée et ne fait l'objet d'aucune documentation ni d'aucun engagement.\n\nLe serveur **MCP** (Model Context Protocol), monté sur **`/mcp`**, expose `locus`, `loci` et\n`lectionnaire` comme outils à un client MCP — c'est une sous-application montée, pas une route,\ndonc OpenAPI ne la décrit pas. Il sert la même surface `/v1` et suit le même contrat.\n\n---\n\n*EN — The TheoFons corpus: Scripture, Magisterium, Church Fathers, and the apparatus linking them.\nRead-only, no key, no account. `/v1` is the contract: additive changes ship without notice,\nbreaking ones get a `/v2` served alongside. Prose documentation:\n[theofons.net/en/api](https://theofons.net/en/api).*\n","license":{"name":"CC BY-SA 4.0","url":"https://creativecommons.org/licenses/by-sa/4.0/"},"version":"1.0"},"paths":{"/v1/documents":{"get":{"tags":["v1 — API publique"],"summary":"Catalogue des documents du corpus (découverte des sigles)","description":"CATALOGUE du corpus magistériel — le point d'entrée de la DÉCOUVERTE : sans lui, un client doit\ndéjà connaître le sigle `ls` pour demander `ls/217`. Rend un tableau trié par sigle : titre et\nauteur dans `lang`, année, langues servies, et nombre de § par langue.\nEx. /v1/documents?lang=en. Les livres bibliques ne sont pas ici : ils s'adressent par leur propre\ncode (`jn/3/16`) et ne relèvent pas de ce catalogue.","operationId":"v1_documents_v1_documents_get","parameters":[{"name":"lang","in":"query","required":false,"schema":{"type":"string","description":"Langue du label, du texte et des titres : `fr` ou `en`.","default":"fr","title":"Lang"},"description":"Langue du label, du texte et des titres : `fr` ou `en`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/locus":{"get":{"tags":["v1 — API publique"],"summary":"Un locus : label, texte, apparat in/out","description":"UN locus (GET, cacheable). Ex. /v1/locus?ref=ps/51/3&psalter=aelf&text=1&apparatus=1&topN=20\n`psalter` = convention psaumes de l'APPELANT (aelf|vulgate|protestant, défaut aelf) : la réf entre et\nressort dans CETTE convention ; en interne on travaille en stockage (protestant).\n`topN` = nb max d'entrées d'apparat PAR SENS et PAR bucket (scripture/magisterium). `inTopN`/`outTopN`\nsurchargent `topN` pour un sens donné. Écriture triée par poids (attestations TSK), Magistère par §rank\n(degré entrant). Défaut 80, plafond dur 500.","operationId":"v1_locus_v1_locus_get","parameters":[{"name":"ref","in":"query","required":true,"schema":{"type":"string","description":"Adresse du locus. Biblique `livre/chapitre/verset` (`jn/3/16`, plage `mt/25/31-46`) ou magistérielle `sigle/§` (`cec/2056`, `sth/2-2/81/6`). Les sigles disponibles se découvrent par `/v1/documents`.","examples":["ps/51/3","cec/2056","gs/22"],"title":"Ref"},"description":"Adresse du locus. Biblique `livre/chapitre/verset` (`jn/3/16`, plage `mt/25/31-46`) ou magistérielle `sigle/§` (`cec/2056`, `sth/2-2/81/6`). Les sigles disponibles se découvrent par `/v1/documents`."},{"name":"lang","in":"query","required":false,"schema":{"type":"string","description":"Langue du label, du texte et des titres : `fr` ou `en`.","default":"fr","title":"Lang"},"description":"Langue du label, du texte et des titres : `fr` ou `en`."},{"name":"text","in":"query","required":false,"schema":{"type":"boolean","description":"Joindre le texte du passage (`text`) à la réponse.","default":false,"title":"Text"},"description":"Joindre le texte du passage (`text`) à la réponse."},{"name":"apparatus","in":"query","required":false,"schema":{"type":"boolean","description":"Joindre l'apparat (`apparat.out` / `apparat.in`) à la réponse.","default":false,"title":"Apparatus"},"description":"Joindre l'apparat (`apparat.out` / `apparat.in`) à la réponse."},{"name":"psalter","in":"query","required":false,"schema":{"type":"string","description":"Convention de numérotation des psaumes de l'APPELANT — la référence entre et ressort dans celle-ci. `aelf` (hébraïque, suscription comptée — défaut), `protestant` (hébraïque, suscription non comptée), `vulgate` (numérotation grecque, un rang de moins du Ps 10 au Ps 146). Sans effet hors des psaumes.","default":"aelf","title":"Psalter"},"description":"Convention de numérotation des psaumes de l'APPELANT — la référence entre et ressort dans celle-ci. `aelf` (hébraïque, suscription comptée — défaut), `protestant` (hébraïque, suscription non comptée), `vulgate` (numérotation grecque, un rang de moins du Ps 10 au Ps 146). Sans effet hors des psaumes."},{"name":"topN","in":"query","required":false,"schema":{"type":"integer","description":"Nombre maximal d'entrées d'apparat, par sens (`in`/`out`) et par nature (Écriture / Magistère). Les entrées arrivent triées par importance, les plus fortes d'abord : une liste tronquée est donc la part qui compte, pas une coupe arbitraire. Plafond dur : 500.","default":80,"title":"Topn"},"description":"Nombre maximal d'entrées d'apparat, par sens (`in`/`out`) et par nature (Écriture / Magistère). Les entrées arrivent triées par importance, les plus fortes d'abord : une liste tronquée est donc la part qui compte, pas une coupe arbitraire. Plafond dur : 500."},{"name":"inTopN","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Surcharge `topN` pour le seul sens `in`.","title":"Intopn"},"description":"Surcharge `topN` pour le seul sens `in`."},{"name":"outTopN","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Surcharge `topN` pour le seul sens `out`.","title":"Outtopn"},"description":"Surcharge `topN` pour le seul sens `out`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/loci":{"post":{"tags":["v1 — API publique"],"summary":"Lot de loci, paginé (même forme que /v1/locus)","description":"LOT de loci, PAGINÉ (POST). Body : {refs:[…], lang, text, apparatus, page, pageSize}. On dédoublonne\nen préservant l'ordre, on plafonne (refs ≤ 500, pageSize ≤ 100), et on renvoie une fenêtre + le curseur\nde pagination. Chaque résultat a la MÊME forme que /v1/locus.","operationId":"v1_loci_v1_loci_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LociReq"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/lectionnaire":{"get":{"tags":["v1 — API publique"],"summary":"Lectures du jour : fête et loci du lectionnaire romain","description":"Lectures du jour (GET, cacheable). date=YYYY-MM-DD (défaut aujourd'hui). Renvoie fête + LOCI ;\nle client résout texte/apparat via /v1/loci. Loci de psaume dans la convention `psalter` (défaut aelf).","operationId":"v1_lectionnaire_v1_lectionnaire_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"type":"string","description":"Jour voulu, `AAAA-MM-JJ`. Vide = aujourd'hui.","examples":["2026-12-25"],"default":"","title":"Date"},"description":"Jour voulu, `AAAA-MM-JJ`. Vide = aujourd'hui."},{"name":"lang","in":"query","required":false,"schema":{"type":"string","description":"Langue du label, du texte et des titres : `fr` ou `en`.","default":"fr","title":"Lang"},"description":"Langue du label, du texte et des titres : `fr` ou `en`."},{"name":"psalter","in":"query","required":false,"schema":{"type":"string","description":"Convention de numérotation des psaumes de l'APPELANT — la référence entre et ressort dans celle-ci. `aelf` (hébraïque, suscription comptée — défaut), `protestant` (hébraïque, suscription non comptée), `vulgate` (numérotation grecque, un rang de moins du Ps 10 au Ps 146). Sans effet hors des psaumes.","default":"aelf","title":"Psalter"},"description":"Convention de numérotation des psaumes de l'APPELANT — la référence entre et ressort dans celle-ci. `aelf` (hébraïque, suscription comptée — défaut), `protestant` (hébraïque, suscription non comptée), `vulgate` (numérotation grecque, un rang de moins du Ps 10 au Ps 146). Sans effet hors des psaumes."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["service"],"summary":"Sonde de vie et taille du corpus chargé","description":"Rend `{ok, corpus:{fr, en}}` — le nombre d'adresses indexées par langue. Sert au préchauffage\nnavigateur et à la surveillance ; ce n'est pas un endpoint de contrat (pas de préfixe `/v1`).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LociReq":{"properties":{"refs":{"items":{"type":"string"},"type":"array","title":"Refs","description":"Adresses à résoudre, 500 au plus. Dédoublonnées en préservant l'ordre. Adresse du locus. Biblique `livre/chapitre/verset` (`jn/3/16`, plage `mt/25/31-46`) ou magistérielle `sigle/§` (`cec/2056`, `sth/2-2/81/6`). Les sigles disponibles se découvrent par `/v1/documents`.","examples":[["jn/3/16","cec/2056","ps/51/3"]]},"lang":{"type":"string","title":"Lang","description":"Langue du label, du texte et des titres : `fr` ou `en`.","default":"fr"},"text":{"type":"boolean","title":"Text","description":"Joindre le texte du passage (`text`) à la réponse.","default":false},"apparatus":{"type":"boolean","title":"Apparatus","description":"Joindre l'apparat (`apparat.out` / `apparat.in`) à la réponse.","default":false},"psalter":{"type":"string","title":"Psalter","description":"Convention de numérotation des psaumes de l'APPELANT — la référence entre et ressort dans celle-ci. `aelf` (hébraïque, suscription comptée — défaut), `protestant` (hébraïque, suscription non comptée), `vulgate` (numérotation grecque, un rang de moins du Ps 10 au Ps 146). Sans effet hors des psaumes.","default":"aelf"},"topN":{"type":"integer","title":"Topn","description":"Nombre maximal d'entrées d'apparat, par sens (`in`/`out`) et par nature (Écriture / Magistère). Les entrées arrivent triées par importance, les plus fortes d'abord : une liste tronquée est donc la part qui compte, pas une coupe arbitraire. Plafond dur : 500.","default":80},"inTopN":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Intopn","description":"Surcharge `topN` pour le seul sens `in`."},"outTopN":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Outtopn","description":"Surcharge `topN` pour le seul sens `out`."},"page":{"type":"integer","title":"Page","description":"Page voulue, à partir de 1.","default":1},"pageSize":{"type":"integer","title":"Pagesize","description":"Taille de page, plafonnée à 100.","default":50}},"type":"object","required":["refs"],"title":"LociReq"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"v1 — API publique","description":"Surface stable, destinée aux clients extérieurs. Limitée à 120 requêtes/minute/IP."},{"name":"service","description":"Sonde d'exploitation. Hors contrat (pas de préfixe `/v1`)."}]}