{"openapi":"3.1.0","info":{"title":"Infinite Critics","description":"\nUn jury de critiques incarnes juge une oeuvre sur pieces et rend un\nverdict structure : la note de chaque jure, sa critique signee, la note\ncollegiale et l'ecart entre eux.\n\nLe cycle est asynchrone. Soumettre met en file ; le panel met une\ntrentaine de secondes, parfois deux tours quand la note tombe trop pres\nde la barre pour etre tranchee sans bruit. On soumet, puis on sonde.\n\nDeux portes d'entree, et c'est delibere : `/signup` rend la cle d'API\ndans la meme reponse, pour un programme qui ne peut pas cliquer sur un\nlien de confirmation ; `/register` est celle des personnes.\n\nUne surface MCP est exposee sur `POST /v1/mcp`, avec la meme cle.\n","version":"0.1.0"},"paths":{"/v1/theque/arene/duel":{"get":{"tags":["theque-arene"],"summary":"Duel","description":"Un duel à juger, signé par le serveur.\n\nAuthentifié : le jeton est lié au votant, sinon deux personnes\npourraient se partager un duel et le second vote serait refusé sans\nqu'on sache pourquoi.\n\nSans `a` et `b`, la paire est tirée (voir `appariement`). Avec, elle\nest celle qu'on demande — le registre des combats a besoin de\nrouvrir une paire précise, voir `_paire_nommee`.","operationId":"duel_v1_theque_arene_duel_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"vertical","in":"query","required":true,"schema":{"type":"string","description":"salle : music, comic, novel, video","title":"Vertical"},"description":"salle : music, comic, novel, video"},{"name":"a","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"external_id d'une œuvre ; avec b, redemande CE combat précis au lieu d'en tirer un au hasard","title":"A"},"description":"external_id d'une œuvre ; avec b, redemande CE combat précis au lieu d'en tirer un au hasard"},{"name":"b","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"external_id de l'adversaire","title":"B"},"description":"external_id de l'adversaire"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/arene/vote":{"post":{"tags":["theque-arene"],"summary":"Voter","description":"Enregistrer un vote, et faire bouger les deux notes.\n\nLe jeton est consommé : un jeton rejoué ne trouve plus rien, ce qui\nferme la fenêtre entre deux requêtes simultanées avant même que la\ncontrainte UNIQUE ait à trancher.","operationId":"voter_v1_theque_arene_vote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoteBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/arene/combats":{"get":{"tags":["theque-arene"],"summary":"Combats","description":"Les combats tranchés, agrégés par paire.\n\nPublic : regarder les combats ne demande pas de compte, et c'est la\nseule fenêtre qu'un auteur a sur les duels de ses propres œuvres —\nil ne peut pas les trancher, il peut les voir.\n\n**Agrégé par paire, pas par vote.** Une ligne par vote affichait le\nmême affrontement autant de fois qu'il avait été tranché, sans\njamais dire combien de personnes avaient choisi quoi. Le registre\ncompte désormais les deux camps : c'est ce qui fait la différence\nentre « A a gagné » et « A a gagné 4 à 1 ».\n\n**Qui a voté quoi n'est jamais dit** — sauf au votant lui-même, sur\nson propre vote (`mon_choix`). Le décompte est public, le bulletin\nreste secret.\n\nLe compteur ne sort d'ici que pour des combats DÉJÀ tranchés. Le\nduel en cours n'en reçoit aucun, et c'est une règle de doctrine :\nannoncer « 68 % ont choisi A » avant le vote transforme un duel en\nsondage auquel on demande d'acquiescer.","operationId":"combats_v1_theque_arene_combats_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":12,"title":"Limit"}},{"name":"vertical","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"filtrer sur une salle","title":"Vertical"},"description":"filtrer sur une salle"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/arene/classement":{"get":{"tags":["theque-arene"],"summary":"Classement","description":"Le classement d'une salle. Public — seul le vote demande un compte.\n\nUN SEUL classement trié, avec un drapeau par ligne. Deux listes\nséparées — « les fiables » et « les provisoires » — donneraient deux\npalmarès dont le second ressemblerait à une seconde division, alors\nqu'une œuvre y est seulement moins mesurée.\n\nLe seuil voyage dans la réponse : aucun client ne doit le coder en\ndur, parce qu'il a été mesuré et qu'il peut l'être à nouveau.","operationId":"classement_v1_theque_arene_classement_get","parameters":[{"name":"vertical","in":"query","required":true,"schema":{"type":"string","title":"Vertical"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/signup":{"post":{"tags":["theque-auth"],"summary":"Signup","operationId":"signup_v1_theque_auth_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/register":{"post":{"tags":["theque-auth"],"summary":"Register","description":"Create an account with a password and open a session straight away.\n\nLogging in right after registering is a step that tells the person\nnothing, so it is skipped.","operationId":"register_v1_theque_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/login":{"post":{"tags":["theque-auth"],"summary":"Login","operationId":"login_v1_theque_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/logout":{"post":{"tags":["theque-auth"],"summary":"Logout","description":"Destroy the session presented in the Authorization header.\n\nOnly sessions: an API key is a long-lived credential a program\nholds, and silently revoking it here would break the caller's next\nrun without anyone asking for that.","operationId":"logout_v1_theque_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Aucune cle valide n'accompagne la demande.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/password":{"post":{"tags":["theque-auth"],"summary":"Set Password","description":"Set or change the password of the authenticated account.\n\nAlso the way an agent-created account gains a password: it already\nproved ownership with its API key, so no reset flow is involved.\n\n**Les autres sessions du compte sont fermées.** Jusqu'au 2026-08-27\nelles ne l'étaient pas : un intrus qui tenait un jeton volé le\ngardait après que la victime avait changé son mot de passe. Le\ngeste que tout le monde fait pour reprendre son compte ne le\nreprenait donc pas.\n\nCelle qui fait la demande survit — déconnecter quelqu'un qui vient\nde sécuriser son compte se lirait comme une panne, et cette\nsession-là vient précisément de prouver qu'elle connaît le nouveau\nmot de passe.\n\nLes clés d'API ne sont pas touchées, même raison qu'à `/logout` :\nc'est un identifiant long que tient un programme, et le révoquer\nici casserait son prochain passage sans que personne l'ait demandé.","operationId":"set_password_v1_theque_auth_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/me":{"get":{"tags":["theque-auth"],"summary":"Me","operationId":"me_v1_theque_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Aucune cle valide n'accompagne la demande.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"post":{"tags":["theque-auth"],"summary":"Update Profile","description":"Set the name the public rooms credit for this account.","operationId":"update_profile_v1_theque_auth_me_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/password/forgot":{"post":{"tags":["theque-auth"],"summary":"Forgot Password","description":"Mail a reset link, and never say whether the address is known.\n\n**Always answers {\"ok\": true}** -- unknown address, deactivated\naccount, mail provider refusing the send, all of it. Any other\nbehaviour turns this endpoint into a way to ask \"does this person\nhave an account here\", which is exactly what someone building a list\nwants and what an author who publishes under a pen name is entitled\nnot to reveal.\n\nThe send happens after the response, which is not only for speed: it\nkeeps the response time from depending on whether there was anything\nto send. A known address still costs one INSERT more than an unknown\none, so the timing is not perfectly flat -- it is a few milliseconds\nagainst a network round trip, rather than the whole round trip.","operationId":"forgot_password_v1_theque_auth_password_forgot_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/password/reset":{"post":{"tags":["theque-auth"],"summary":"Reset Password","description":"Set a new password from a mailed token, and open a session.\n\nLogging in right after proving you hold the link is a step that\ntells the person nothing -- same reasoning as /register.\n\n**Toutes les sessions ouvertes ailleurs sont fermées**, et c'est le\npoint de cette route autant que le mot de passe : une\nréinitialisation est le moment précis où quelqu'un essaie de\nchasser un intrus. Aucune n'est épargnée — le demandeur n'était pas\nconnecté, il l'est par la session ouverte juste après.\n\nCette révocation était notée ici comme une limite connue et\ndélibérée, faute d'index inverse dans `app/core/sessions.py`. Cet\nindex existe depuis le 2026-08-27 et la limite est levée.","operationId":"reset_password_v1_theque_auth_password_reset_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/email/verify/request":{"post":{"tags":["theque-auth"],"summary":"Request Email Verification","description":"Mail a confirmation link to the authenticated account's address.","operationId":"request_email_verification_v1_theque_auth_email_verify_request_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Aucune cle valide n'accompagne la demande.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auth/email/verify":{"post":{"tags":["theque-auth"],"summary":"Verify Email","description":"Confirm an address from a mailed token.\n\nUnauthenticated on purpose: the link is opened from a mail client,\noften in a browser where nobody is logged in. The token is the\nproof, and it is the only thing this route trusts.","operationId":"verify_email_v1_theque_auth_email_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/collection":{"get":{"tags":["theque-public"],"summary":"Collection","description":"Works the jury admitted and their authors chose to show.","operationId":"collection_v1_theque_collection_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":30,"title":"Limit"}},{"name":"vertical","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vertical"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/refuses":{"get":{"tags":["theque-public"],"summary":"Refuses","description":"The Salon des Refusés -- rejected works whose authors published\nthem anyway. Opt-in, like everything else here.","operationId":"refuses_v1_theque_refuses_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":30,"title":"Limit"}},{"name":"vertical","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vertical"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}":{"delete":{"tags":["theque-compte"],"summary":"Supprimer Oeuvre","description":"Erase one of one's own works, now.\n\n404 rather than 403 when the work belongs to someone else, matching\nthe rule the reading routes already follow: a 403 confirms that a\nwork with that id exists, which is itself a leak when ids are the\nonly thing an outsider can guess at.\n\nDeletion is allowed whatever the status, including while the jury is\nmid-panel. The right to erasure does not pause because a job is\nrunning, and the worker already tolerates a work vanishing under it\n(app/worker.py:42). One narrow window does not: see the message left\nin COORDINATION-CAHIERS.md about worker.py:86.\n\nThe reviews go with it, by the cascade declared in v1. The Arena\nvotes that involved it go too, which slightly moves its opponents'\nratings on the next replay -- the intended behaviour, already\ndocumented in theque_arene.py: a withdrawn work should stop\ninfluencing the Collection.","operationId":"supprimer_oeuvre_v1_theque_works__external_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["theque"],"summary":"Get Work","description":"Fetch a single work by its public id, verdict included.\n\nThis is the endpoint an agent polls after submitting: `status` moves\nqueued -> panel -> admitted|rejected, and `verdict` carries every\njuror's review once decided.\n\nL'auteur voit toujours la sienne. Les autres ne la voient qu'une fois\npubliee -- et 404 sinon, pour que la reponse ne serve pas a deviner\nquels identifiants existent.\n\nLe controle de visibilite etait ecrit a la main ici, et c'est CETTE\ncopie que /media, /plates et /texte ont recopiee sans sa troisieme\ncondition. Elle passe par charger_oeuvre_lisible() comme les autres :\ntant qu'une route de lecture garde sa propre version de la regle,\nelle reste un modele a recopier de travers.","operationId":"get_work_v1_theque_works__external_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["theque"],"summary":"Edit Work","description":"Retouche le titre, la présentation et la couverture d'une œuvre.","operationId":"edit_work_v1_theque_works__external_id__post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/conservation":{"get":{"tags":["theque-compte"],"summary":"Lire Conservation","description":"Whether this work is kept, and when it goes if it is not.\n\nA route of its own because the work payload is built in\ntheque_works.py, which belongs to another session's scope. Folding\n`purge_after` into it would be three lines and one call instead of\ntwo -- see the API contract in COORDINATION-CAHIERS.md, where that\nis asked for rather than done.","operationId":"lire_conservation_v1_theque_works__external_id__conservation_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["theque-compte"],"summary":"Regler Conservation","description":"Choose whether to keep this work. Reversible while it still exists.\n\nThe rule this applies, adopted as a general one for the product on\n2026-08-25: **a shift towards more privacy is automatic; a shift\ntowards less requires an explicit act.**\n\nSo the two directions are not symmetric, and deliberately so:\n\n  keep=false  accepted at any time, effective immediately. If the\n              work is published it is un-published in the same\n              breath -- that direction needs no second consent, and\n              a work due to vanish within the hour cannot stay in a\n              public room that would show an empty card.\n\n  keep=true   accepted only while the work still exists, which\n              under short retention means within the hour. After\n              that there is nothing left to keep and the author\n              resubmits.\n\nPublishing a work marked \"do not keep\" is refused by the publish\nroute, not silently flipped here: a privacy choice never reverses\nitself (CONCEPT.md §4 bis).","operationId":"regler_conservation_v1_theque_works__external_id__conservation_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConservationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/compte":{"delete":{"tags":["theque-compte"],"summary":"Demander Suppression","description":"Ask for the account to be erased. Takes effect now, completes later.\n\nImmediately: the account goes inactive -- which app/core/auth.py\nalready honours on every authenticated route, and which login\nalready refuses -- every work is un-published, and the session used\nto make the request is destroyed.\n\nUn-publishing rather than waiting is what makes the request honest.\nThe public rooms do not filter on is_active (theque_collection.py),\nso a deactivated account would otherwise keep its works on display,\ncredited by name, for a month.\n\nIdempotent: asking twice returns the same deadline rather than\npushing it back. A second click must not extend a window, and must\nnot shorten one either.","operationId":"demander_suppression_v1_theque_compte_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Aucune cle valide n'accompagne la demande.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/compte/annulation":{"post":{"tags":["theque-compte"],"summary":"Annuler Suppression","description":"Change one's mind, and get the account back.\n\nThis route cannot use get_current_user, and that is the whole reason\nit looks different from every other authenticated endpoint here.\nRequesting closure sets is_active to false, and app/core/auth.py\nresolves an inactive account to None -- so the very act of asking to\nleave locks the person out of the route that would let them stay.\nLogin refuses inactive accounts too (theque_auth.py:209).\n\nSo there are two doors, both leading only to an account that has a\npending request:\n\n  - a token still in hand (session or API key), resolved here\n    without the is_active filter;\n  - email and password, for the far more likely case where the\n    session was destroyed by the closure request itself and the\n    month has since gone by.\n\nNeither door opens anything else. An account with no pending request\nis a 404 through both, so this cannot be used as a way to log in\naround the deactivation, nor to find out who holds an account here.\n\nWorks are NOT re-published. Publishing is an explicit act\n(CONCEPT.md §4 bis) and restoring it silently would put someone's\nwork back on public display without them asking a second time. The\nauthor re-publishes what they choose, through the route that already\nexists.","operationId":"annuler_suppression_v1_theque_compte_annulation_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnnulationBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/compte/purge/sante":{"get":{"tags":["theque-compte"],"summary":"Sante Purge","description":"Whether the deletion sweep is still running.\n\nPublic, and deliberately thin. This exists so that a monitor can\nreach it without holding a credential -- the failure it guards\nagainst is the sweep dying quietly, and a check nobody can run is\nthe same as no check. So it carries no activity figures: how many\naccounts closed this week is not something an anonymous caller\nshould be able to watch. Those numbers are in the container logs\nand in the sweep's own Redis record.\n\n`retard` true means no sweep has completed within\nSEUIL_RETARD_HEURES, or none ever has -- one hour since short\nretention tightened it from two missed days to six missed passes.\nThat is the state worth an alert: everything else about this\nfeature can look perfectly healthy -- requests accepted,\naccounts deactivated, works un-published -- while nothing is\nactually being erased. The promise looks kept for a month before\nanyone notices.","operationId":"sante_purge_v1_theque_compte_purge_sante_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/jures/{slug}":{"get":{"tags":["theque-public"],"summary":"Jure","description":"La fiche d'un juré : son identité, ses chiffres, son suivi.","operationId":"jure_v1_theque_jures__slug__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/jures/{slug}/verdicts":{"get":{"tags":["theque-public"],"summary":"Verdicts Du Jure","description":"Ses dernières évaluations — celles que le public peut voir.\n\nPublique : lire l'historique d'un juré ne demande pas de compte, au\nmême titre que les salles. Ce qui demande un compte, c'est le suivi.\n\nUNE ŒUVRE QUI DISPARAÎT NE LAISSE NI TROU NI FANTÔME. Effacée\n(compte supprimé, œuvre supprimée, rétention courte échue), la\ncritique part avec elle : `theque_reviews.work_id` est en\n`ON DELETE CASCADE`, il n'y a pas de ligne à afficher sans son\nœuvre. Dépubliée, l'auteur ayant changé d'avis, la ligne existe\nencore mais la requête ne la retient pas — et comme le filtrage est\nen SQL, elle disparaît du jeu au lieu de laisser une case vide dans\nune page de taille fixe.","operationId":"verdicts_du_jure_v1_theque_jures__slug__verdicts_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}},{"name":"avant","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Avant"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/jures/{slug}/suivi":{"post":{"tags":["theque-public"],"summary":"Suivre","description":"Suivre un juré : ses verdicts publics rejoignent le Bulletin.\n\nCompte connecté obligatoire : un suivi est une donnée personnelle\nrattachée à quelqu'un — c'est la liste de ce qu'il lit — et un suivi\nanonyme ne pourrait de toute façon pas être empêché de se répéter.\n\nIdempotent par la contrainte SQL, pas par une vérification écrite\nici : deux requêtes simultanées passeraient toutes les deux un test\nen Python et insèreraient toutes les deux. Le nom de la contrainte\nest cité, donc le renommer en base casse ce code bruyamment au lieu\nde laisser passer des doublons.\n\n`ON CONFLICT DO NOTHING` et non `DO UPDATE` : re-suivre un juré déjà\nsuivi ne doit pas remettre le repère de lecture à aujourd'hui, ce\nqui effacerait silencieusement les nouveautés pas encore lues.","operationId":"suivre_v1_theque_jures__slug__suivi_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["theque-public"],"summary":"Ne Plus Suivre","description":"Ne plus suivre. La ligne est supprimée, pas désactivée.\n\nLe compte d'abonnés dit alors combien de personnes suivent ce juré\naujourd'hui, jamais combien ont cliqué un jour. Même règle que le\nrepêchage — et une position qu'on ne peut pas retirer n'est pas une\nposition, c'est un piège.","operationId":"ne_plus_suivre_v1_theque_jures__slug__suivi_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/suivis/verdicts":{"get":{"tags":["theque-public"],"summary":"Bulletin","description":"Le Bulletin : les verdicts publics des jurés que je suis.\n\nC'est ce qui rend le bouton « Suivre » honnête. Rien n'est poussé —\nni e-mail, ni notification : le lecteur vient lire, et trouve ce qui\nest arrivé depuis sa dernière visite. Une institution publie un\nbulletin ; on va le consulter, il ne vous alerte pas.\n\n`nouveaux` est un compte, par juré et au total, jamais une pastille\napproximative. Il se mesure contre le repère de lecture déposé au\ndernier `POST /suivis/vu`.","operationId":"bulletin_v1_theque_suivis_verdicts_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}},{"name":"avant","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Avant"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/suivis/vu":{"post":{"tags":["theque-public"],"summary":"Marquer Lu","description":"Déposer le repère de lecture du Bulletin.\n\nUn POST explicite et non un effet de bord du GET : une lecture qui\nécrit est un piège pour la session suivante, et elle rend le compte\ndes nouveautés impossible à afficher deux fois de suite.\n\nLe repère est le plus haut id de critique existant, tous jurés\nconfondus, posé sur toutes les lignes de ce lecteur d'un coup : « j'ai\nlu mon Bulletin » vaut pour la page entière, pas juste pour les jurés\ndont un verdict figurait dessus.","operationId":"marquer_lu_v1_theque_suivis_vu_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Aucune cle valide n'accompagne la demande.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/jury":{"get":{"tags":["theque-public"],"summary":"Jury","operationId":"jury_v1_theque_jury_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/auteurs/{handle}":{"get":{"tags":["theque-profils"],"summary":"Profil Public","description":"La page d'un auteur, telle qu'un visiteur la voit.","operationId":"profil_public_v1_theque_auteurs__handle__get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/profil":{"post":{"tags":["theque-profils"],"summary":"Maj Profil","description":"Met à jour son propre profil. Chaque champ omis reste inchangé.","operationId":"maj_profil_v1_theque_profil_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfilBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/progression/{external_id}/version":{"post":{"tags":["theque-progression"],"summary":"Soumettre Version","description":"Resoumettre une œuvre corrigée, rattachée à la version précédente.\n\nL'ordre des contrôles n'est pas indifférent : tout ce qui peut\nrefuser gratuitement passe AVANT ce qui coûte. Un texte identique au\ncaractère près est renvoyé sans qu'un seul appel facturable soit\nparti.","operationId":"soumettre_version_v1_theque_progression__external_id__version_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionSubmit"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/progression/{external_id}/synthese":{"post":{"tags":["theque-progression"],"summary":"Produire Synthese","description":"Nommer ce qui a été corrigé et ce qui reste. Un appel, une fois.\n\nRéservée à l'auteur : elle coûte, et personne d'autre ne doit\npouvoir déclencher une dépense sur son œuvre.\n\nIDEMPOTENTE. Déjà calculée, elle est renvoyée telle quelle sans\nrepayer. C'est ce qui empêche un double clic, un rechargement ou un\nfront bavard de transformer une fonctionnalité à 0,15 centime en\nfuite.\n\nSUBORDONNÉE AUX MÊMES CONDITIONS QUE LE RESTE. Panels différents,\nmodèle différent, maillon purgé, note absente : refusé, avec la\nraison. Quasi identique : refusé aussi, puisque la décision du\npropriétaire est qu'aucune progression n'y soit célébrée -- et une\nliste de « défauts corrigés » entre deux textes quasi identiques\nserait précisément une progression célébrée.\n\nUNE EXCEPTION QUE JE N'AI PAS PRISE, ET QUI MÉRITE D'ÊTRE POSÉE.\nLa synthèse compare des CRITIQUES, pas des notes : un panel différent\nlui est en principe moins fatal qu'à une soustraction de moyennes,\npuisqu'on pourrait la restreindre aux jurés communs, comme le fait\ndéjà progression_par_jure(). Ce serait défendable, mais ce n'est pas\nmesuré -- le banc l'a éprouvée sur des panels identiques, et rien ne\ndit qu'elle reste honnête avec deux jurés d'un côté et trois de\nl'autre. Le banc qui trancherait est le même que celui du 2026-08-25,\navec un juré retiré d'une des deux séries. En attendant, la règle\nstricte s'applique.","operationId":"produire_synthese_v1_theque_progression__external_id__synthese_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/progression/{external_id}":{"get":{"tags":["theque-progression"],"summary":"Lire Progression","description":"La lignée entière, et ce qui a progressé entre les deux dernières.\n\nOn peut la demander par n'importe laquelle de ses versions : c'est\nla lignée qui est l'objet, pas la version.\n\nVisibilité, en deux temps. D'abord l'œuvre demandée : les trois\nconditions communes (publiée, pas 18+, auteur actif), 404 sinon.\nEnsuite la lignée : un tiers ne voit que les versions publiées — une\nv1 que l'auteur a gardée pour lui reste à lui.","operationId":"lire_progression_v1_theque_progression__external_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/public/critiques":{"get":{"tags":["theque-public-vote"],"summary":"Critiques","description":"How the readers stand on each juror's critique of one work.\n\nPublic: the counts are part of what publishing a work means. Only\ncasting a vote needs an account.","operationId":"critiques_v1_theque_public_critiques_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"work","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":32,"title":"Work"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/public/critiques/avis":{"post":{"tags":["theque-public-vote"],"summary":"Poser Un Avis","description":"Agree or disagree with one juror's critique.\n\nIdempotent by design: sending the same opinion twice leaves one row,\nand sending the opposite one flips it. The UNIQUE constraint is what\nmakes that true under concurrency -- the ON CONFLICT clause names it\nso that a rename in the migration breaks loudly here instead of\nquietly letting duplicates through.","operationId":"poser_un_avis_v1_theque_public_critiques_avis_post","security":[{"APIKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvisBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["theque-public-vote"],"summary":"Retirer Un Avis","description":"Withdraw one's opinion on a critique.\n\nDeleting the row rather than storing a third \"no opinion\" state:\nthe count then always says how many people currently hold a view,\nnever how many ever clicked.","operationId":"retirer_un_avis_v1_theque_public_critiques_avis_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"work","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":32,"title":"Work"}},{"name":"persona","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"title":"Persona"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/public/repechages":{"get":{"tags":["theque-public-vote"],"summary":"Repechages","description":"Repechage counts for a page of the Salon des Refuses.\n\nBatched because the Salon is a grid: one request per card would\nturn a page of thirty into thirty round trips. Unknown or\nnon-public ids are simply absent from the answer rather than\nraising -- a grid must not fail whole because one work was\nun-published between the two calls.","operationId":"repechages_v1_theque_public_repechages_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"works","in":"query","required":true,"schema":{"type":"string","description":"external ids, comma separated -- one page of the Salon","title":"Works"},"description":"external ids, comma separated -- one page of the Salon"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/public/repechages/{external_id}":{"post":{"tags":["theque-public-vote"],"summary":"Repecher","description":"Stand behind a rejected work, against the Jury's verdict.\n\nPast regles.RESCUE_THRESHOLD supporters the work is rescued, and\nthat is a real change of state, not a milestone on a counter\n(owner's decision, 2026-08-24). The state lives in\ntheque_work_rescues rather than in theque_works.status -- see the\nmodel for why the Jury's column is left alone.","operationId":"repecher_v1_theque_public_repechages__external_id__post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["theque-public-vote"],"summary":"Annuler Un Repechage","description":"Withdraw one's repechage. See the model for why this is allowed.\n\nWithdrawing lowers the count and never un-rescues: a rescue is a\ndated event, not a live comparison. The count keeps being published\nbeside the state, so a rescue that has lost its supporters is\nvisible rather than hidden.","operationId":"annuler_un_repechage_v1_theque_public_repechages__external_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/public/repeches":{"get":{"tags":["theque-public-vote"],"summary":"Repeches","description":"Les Repêchés — the works the public pulled back out, most recent\nfirst. Public, like the rooms it sits beside.\n\nA room of its own, deliberately NOT the Collection: the Collection\nis what the Jury admitted, and the seal exists only by its verdict.\nA rescued work carries the public's gold, never the Jury's red.\n\nSame three filters as the public rooms: published, still rejected by\nthe Jury, no 18+ without an age check. A work whose author\nun-publishes it leaves this list like any other, even though its\nrescue row survives -- the rescue records what happened, publication\ndecides what is shown.\n\nDeliberately thinner than the Collection's payload: identity, cover,\nand the public's count. The critiques already have a route\n(GET /theque/works/{id}), and duplicating that shape here would mean\ntwo places to keep in step with a router this session does not own.","operationId":"repeches_v1_theque_public_repeches_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":30,"title":"Limit"}},{"name":"vertical","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vertical"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/stats":{"get":{"tags":["theque-public"],"summary":"Stats","operationId":"stats_v1_theque_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/uploads":{"post":{"tags":["theque-uploads"],"summary":"Create Upload","description":"Hand back a presigned PUT URL for one file.","operationId":"create_upload_v1_theque_uploads_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works":{"get":{"tags":["theque"],"summary":"List Works","description":"List the caller's own works, most recent first.\n\nOwner-scoped, never a global feed. The public view of what has been\npublished lives at GET /theque/collection.","operationId":"list_works_v1_theque_works_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":30,"title":"Limit"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"vertical","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vertical"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["theque"],"summary":"Submit Work","description":"Crée une œuvre et la met en file quand elle est jugeable.\n\nLa règle vit dans `app/critics/soumission.py`, parce qu'elle a deux\nportes : celle-ci et l'outil MCP `submit_work`. Ce qui reste ici est\nce qui appartient à HTTP — traduire un refus en code de statut, et\nl'œuvre en JSON.","operationId":"submit_work_v1_theque_works_post","security":[{"APIKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkSubmit"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/media":{"get":{"tags":["theque"],"summary":"Get Media Url","description":"Signed, short-lived URL to play or download the submitted file.\n\nSame visibility rule as the work itself: the owner always, anyone\nelse only once it is published and not 18+. Returned per request\nrather than stored on the work, so the link dies with the hour and\ncannot outlive an un-publication.","operationId":"get_media_url_v1_theque_works__external_id__media_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/plates":{"get":{"tags":["theque"],"summary":"Get Plates","description":"Signed URLs for a comic's plates, in reading order.\n\nSame visibility rule as the work itself: the owner always, anyone\nelse only once it is published and not 18+. Signing is a local HMAC,\nso handing out sixty links costs nothing and reaches no network.\n\nThe list order is the reading order -- it is the order the plates\nwere uploaded in, and the same order the Jury read them in. Sorting\nit anywhere else would show a reader a different book from the one\nthat was judged.","operationId":"get_plates_v1_theque_works__external_id__plates_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/texte":{"get":{"tags":["theque"],"summary":"Get Texte","description":"The text of a novel, for reading.\n\nNovels only. For a comic the dossier is the cartel -- a line or two\nof context -- and for a music submission it is the symbolic dossier:\nmeasurements, transcribed lyrics, a pitch reading. Neither is the\nwork, and serving them here would hand a reader the analysis instead\nof the thing analysed.\n\nSame visibility rule as the work itself: the owner always, anyone\nelse only once published and not 18+, 404 otherwise so the response\ncannot be used to probe which ids exist.","operationId":"get_texte_v1_theque_works__external_id__texte_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/cancel":{"post":{"tags":["theque"],"summary":"Cancel Work","description":"Retire une œuvre pendant son examen, à la demande de son auteur.\n\nUne analyse vidéo enchaîne un décodage, une transcription puis quatre\njurés ; l'auteur qui trouve l'attente trop longue n'avait aucun moyen\nd'en sortir. Il en a un.\n\nRien d'autre n'est effacé ici : poser `purge_after` à maintenant confie\nl'effacement au balayage de purge, qui sait déjà inscrire les clés R2\nau journal **avant** de supprimer la ligne. Refaire cette séquence à la\nmain ici, c'est se donner une deuxième chance de laisser des fichiers\norphelins facturés à vie -- le piège est documenté deux fois dans ce\ndépôt.\n\nLes trois étapes coûteuses relisent ce statut : le constructeur de\ndossier avant et après son travail, le jury à l'entrée et au retour du\npanel. Une annulation arrivée à n'importe quel moment est donc vue\navant la dépense suivante, jamais après.","operationId":"cancel_work_v1_theque_works__external_id__cancel_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/contestation":{"post":{"tags":["theque"],"summary":"Contester Motion","description":"Contester une motion de disqualification, et rouvrir l'examen.\n\n`suspended` était un état TERMINAL : une motion gelait l'œuvre et\nrien ne pouvait la dégeler — ni le propriétaire, ni l'auteur, ni le\nJury. Aucun rôle habilité, aucune route, aucun délai. Pendant ce\ntemps l'écran promettait à l'auteur une décision « tant que la\nquestion n'est pas tranchée », c'est-à-dire jamais.\n\nEt l'accusation est faillible. Mesuré : le radar reconnaît Sintel 8\nfois sur 8 et un film d'amateur inconnu 0 fois sur 4 — il mesure la\nnotoriété, pas la titularité. Un auteur qui travaille dans un univers\nconnu déclenche exactement le même signal qu'un voleur.\n\nArbitrage du propriétaire, 2026-08-27 : l'auteur conteste, et sa\ncontestation relance un second examen.\n\nDEUX GARANTIES, ET CE SONT ELLES QUI FONT TENIR LE MÉCANISME.\n\n**Une seule contestation par œuvre, jamais deux**, y compris après\nune motion confirmée. Le jugement n'est pas reproductible — c'est\nmesuré salle par salle — donc autoriser deux essais reviendrait à\nautoriser à relancer les dés jusqu'à ce que la motion ne tombe pas.\nLe recours deviendrait une machine à sous. `conteste_le` non nul\nferme la porte définitivement.\n\n**La déclaration part au Jury** (voir `app/worker.py`), prépendue au\ndossier. Sans elle, le second examen serait le même dossier passé une\nseconde fois devant le même panel : ce qui change entre les deux\nexamens doit être l'INFORMATION, pas la chance.\n\nCe que cette route N'ÉTABLIT PAS, et c'est délibéré : rien\njuridiquement. Elle rouvre un examen, elle ne prouve pas une\ntitularité et ne décide d'aucune responsabilité — ces questions\nrelèvent d'un juriste.","operationId":"contester_motion_v1_theque_works__external_id__contestation_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContestationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/theque/works/{external_id}/publish":{"post":{"tags":["theque"],"summary":"Set Published","description":"Publish or un-publish one's own work.\n\nRevocable by design (CONCEPT.md §8: \"tout est révocable\") -- passing\npublished=false pulls it back out of the public listings. A work can\nonly be published once the jury has ruled: publishing a pending\nsubmission would put an unjudged work in a room that means \"the jury\nhas spoken\".","operationId":"set_published_v1_theque_works__external_id__publish_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/billing/plans":{"get":{"tags":["billing"],"summary":"Plans","description":"Public : ce qui est en vente, et ce qu'un crédit achète.\n\nLe barème est annoncé ICI et non dans les produits : c'est lui qui\nbouge si le coût des modèles bouge, et l'annoncer à côté du\ncatalogue permet à un client de calculer avant d'acheter.","operationId":"plans_v1_billing_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"503":{"description":"Le prestataire de paiement n'est pas configure ou ne repond pas.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}}}},"/v1/billing/checkout":{"post":{"tags":["billing"],"summary":"Checkout","description":"Fabrique la page de paiement et rend son adresse.\n\nLe price_id doit appartenir à NOTRE catalogue : accepter n'importe\nquel prix Stripe laisserait acheter un produit d'un autre produit de\nla maison et créditer des critiques en échange.","operationId":"checkout_v1_billing_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/billing/portal":{"post":{"tags":["billing"],"summary":"Portal","description":"Ouvre le portail client Stripe et rend son adresse.\n\nPOURQUOI CETTE ROUTE EXISTE. Sans elle, un client abonne ne pouvait\npas resilier : ni route chez nous, ni portail branche. Il aurait\nfallu qu'un humain le fasse a la main dans le tableau de bord, sur\ndemande. Vendre un abonnement qu'on ne peut pas arreter soi-meme\nn'est pas defendable -- trouve le 2026-08-28, avant la premiere\nvente reelle et non apres.\n\nPOURQUOI LE PORTAIL DE STRIPE plutot qu'une route « annuler » a\nnous. Un abonnement ne se resume pas a sa resiliation : il faut\naussi changer de carte quand elle expire, retrouver ses factures,\nvoir la prochaine echeance. Ecrire tout cela reviendrait a refaire\nun espace client, et a en porter la responsabilite legale. Le\nportail rend les quatre d'un coup, heberge chez eux.\n\nMEME FORME QUE LE PAIEMENT, et pour la meme raison : l'appelant est\nsouvent un PROGRAMME. Il demande l'adresse, la tend a son humain,\nqui agit dans un navigateur. Rien de sensible ne traverse nos\nserveurs.\n\nLe compte doit avoir un client Stripe -- donc avoir achete au moins\nune fois. Sans achat, il n'y a rien a gerer, et le dire est plus\nhonnete que d'ouvrir un portail vide.","operationId":"portal_v1_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Aucune cle valide n'accompagne la demande.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"409":{"description":"Ce compte n'a pas de client chez le prestataire de paiement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"503":{"description":"Le prestataire de paiement n'est pas configure ou ne repond pas.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/billing/webhook":{"post":{"tags":["billing"],"summary":"Webhook","description":"Reçoit les événements Stripe. Signé, vérifié, idempotent.\n\nRend 200 même pour les événements qu'on ignore : un webhook qui\néchoue sur un type inconnu force Stripe à rejouer pour rien, et\nfinit désactivé pour excès d'échecs.","operationId":"webhook_v1_billing_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Signature absente, invalide, ou horodatage hors tolerance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"503":{"description":"Le prestataire de paiement n'est pas configure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}}}},"/v1/trial":{"post":{"tags":["essai"],"summary":"Ouvrir Un Essai","description":"Remet un jeton d'essai anonyme. Aucune authentification.\n\nL'appelant garde le jeton et le présente ensuite comme n'importe\nquelle clé : `Authorization: Bearer <token>`, sur l'API comme sur\nle MCP. Il n'est montré qu'une fois et n'est pas récupérable — il\nn'y a pas d'adresse à qui le renvoyer, et c'est le prix de l'essai\nsans inscription.","operationId":"ouvrir_un_essai_v1_trial_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"503":{"description":"L'essai gratuit n'est pas configure sur ce deploiement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}}}},"/v1/critique/":{"post":{"tags":["critique-get"],"summary":"Demander Une Critique Post","description":"La MEME chose, en POST, texte dans le corps.\n\nPOURQUOI LES DEUX. Le GET existe parce qu'un assistant ne sait\nsouvent que recuperer une adresse. Mais une adresse se tronque — par\nun mandataire, un client, un champ de saisie — et le texte arrive\nalors ampute, voire absent. Le POST met le texte a l'abri : meme\nlogique, meme reponse, aucune limite de longueur d'URL. Un client qui\na le choix devrait prendre celui-ci.","operationId":"demander_une_critique_post_v1_critique__post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CritiqueBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["critique-get"],"summary":"Demander Une Critique","description":"Soumet un texte au jury et rend l'adresse ou lire le verdict.\n\nRepond immediatement. Le panel met une trentaine de secondes ; c'est\nl'appelant qui revient, sur l'adresse `poll`.","operationId":"demander_une_critique_v1_critique__get","parameters":[{"name":"text","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":50},{"type":"null"}],"description":"Le texte a juger. 50 caracteres au minimum — en deca, le jury n'a pas de quoi lire. L'adresse encodee ne porte que 9000 caracteres : au-dela, passez par POST, qui accepte jusqu'a 400000 caracteres.","title":"Text"},"description":"Le texte a juger. 50 caracteres au minimum — en deca, le jury n'a pas de quoi lire. L'adresse encodee ne porte que 9000 caracteres : au-dela, passez par POST, qui accepte jusqu'a 400000 caracteres."},{"name":"title","in":"query","required":false,"schema":{"type":"string","maxLength":120,"description":"Facultatif.","default":"Sans titre","title":"Title"},"description":"Facultatif."},{"name":"room","in":"query","required":false,"schema":{"type":"string","description":"novel (texte narratif) ou other. roman et autre acceptes.","default":"novel","title":"Room"},"description":"novel (texte narratif) ou other. roman et autre acceptes."},{"name":"lane","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"La file : priority (passe devant), normal (defaut), fill (attend les creux). Reservee a un compte credite ; un essai gratuit garde la voie par defaut. Absente, rien ne change.","title":"Lane"},"description":"La file : priority (passe devant), normal (defaut), fill (attend les creux). Reservee a un compte credite ; un essai gratuit garde la voie par defaut. Absente, rien ne change."},{"name":"turbo","in":"query","required":false,"schema":{"type":"boolean","description":"Mode turbo : la MEME critique, rendue plus vite. Les quatre jures siegent ensemble au lieu de se suivre, donc l'oeuvre attend le plus lent et non la somme des quatre. Ni la critique, ni la note, ni le prix ne changent.","default":false,"title":"Turbo"},"description":"Mode turbo : la MEME critique, rendue plus vite. Les quatre jures siegent ensemble au lieu de se suivre, donc l'oeuvre attend le plus lent et non la somme des quatre. Ni la critique, ni la note, ni le prix ne changent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/critique":{"post":{"tags":["critique-get"],"summary":"Demander Une Critique Post","description":"La MEME chose, en POST, texte dans le corps.\n\nPOURQUOI LES DEUX. Le GET existe parce qu'un assistant ne sait\nsouvent que recuperer une adresse. Mais une adresse se tronque — par\nun mandataire, un client, un champ de saisie — et le texte arrive\nalors ampute, voire absent. Le POST met le texte a l'abri : meme\nlogique, meme reponse, aucune limite de longueur d'URL. Un client qui\na le choix devrait prendre celui-ci.","operationId":"demander_une_critique_post_v1_critique_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CritiqueBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["critique-get"],"summary":"Demander Une Critique","description":"Soumet un texte au jury et rend l'adresse ou lire le verdict.\n\nRepond immediatement. Le panel met une trentaine de secondes ; c'est\nl'appelant qui revient, sur l'adresse `poll`.","operationId":"demander_une_critique_v1_critique_get","parameters":[{"name":"text","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":50},{"type":"null"}],"description":"Le texte a juger. 50 caracteres au minimum — en deca, le jury n'a pas de quoi lire. L'adresse encodee ne porte que 9000 caracteres : au-dela, passez par POST, qui accepte jusqu'a 400000 caracteres.","title":"Text"},"description":"Le texte a juger. 50 caracteres au minimum — en deca, le jury n'a pas de quoi lire. L'adresse encodee ne porte que 9000 caracteres : au-dela, passez par POST, qui accepte jusqu'a 400000 caracteres."},{"name":"title","in":"query","required":false,"schema":{"type":"string","maxLength":120,"description":"Facultatif.","default":"Sans titre","title":"Title"},"description":"Facultatif."},{"name":"room","in":"query","required":false,"schema":{"type":"string","description":"novel (texte narratif) ou other. roman et autre acceptes.","default":"novel","title":"Room"},"description":"novel (texte narratif) ou other. roman et autre acceptes."},{"name":"lane","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"La file : priority (passe devant), normal (defaut), fill (attend les creux). Reservee a un compte credite ; un essai gratuit garde la voie par defaut. Absente, rien ne change.","title":"Lane"},"description":"La file : priority (passe devant), normal (defaut), fill (attend les creux). Reservee a un compte credite ; un essai gratuit garde la voie par defaut. Absente, rien ne change."},{"name":"turbo","in":"query","required":false,"schema":{"type":"boolean","description":"Mode turbo : la MEME critique, rendue plus vite. Les quatre jures siegent ensemble au lieu de se suivre, donc l'oeuvre attend le plus lent et non la somme des quatre. Ni la critique, ni la note, ni le prix ne changent.","default":false,"title":"Turbo"},"description":"Mode turbo : la MEME critique, rendue plus vite. Les quatre jures siegent ensemble au lieu de se suivre, donc l'oeuvre attend le plus lent et non la somme des quatre. Ni la critique, ni la note, ni le prix ne changent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/critique/{work_id}/en-direct":{"get":{"tags":["critique-get"],"summary":"Lire En Direct","description":"La phrase de chaque juré, dès qu'il l'a écrite (2026-09-28).\n\nArthur : « en quelques secondes on a déjà un premier avis à lire, et ça\nfait patienter l'utilisateur ». Chaque juré ouvre sa critique par une\nphrase ; elle est rangée ici avant que le jury ait fini\n(`app/critics/jury/en_direct.py`). Ne bloque jamais : on lit ce qu'il y\na et on rend la main. Le verdict entier reste sur `GET /v1/critique/{id}`.\n\nMême règle d'accès que le verdict : le propriétaire, ou une œuvre\nd'essai ; un 404 sinon, sans dire si l'identifiant existe.","operationId":"lire_en_direct_v1_critique__work_id__en_direct_get","parameters":[{"name":"work_id","in":"path","required":true,"schema":{"type":"string","title":"Work Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/critique/{work_id}":{"post":{"tags":["critique-get"],"summary":"Lire La Critique","description":"Le verdict, quand il est rendu. GET ou POST, au choix.\n\nPOURQUOI LES DEUX SUR UNE LECTURE. Un GET est la forme juste pour\nlire, et reste celle que suivra un assistant qui n'a qu'un outil de\nrecuperation d'adresse. Mais un client qui vient de soumettre en\nPOST doit alors CHANGER DE METHODE en cours de route — et c'est\nexactement ce changement qui a coute une erreur 400 a Arthur le\n2026-09-01, un corps JSON laisse sur un GET etant ignore par HTTP.\nAccepter les deux supprime la manoeuvre : on garde POST du debut a\nla fin, ou GET si l'outil ne sait faire que cela. Le corps n'est\njamais lu ici.\n\nL'appartenance est verifiee comme partout ailleurs : une oeuvre ne se\nlit que depuis le reseau qui l'a soumise. Un 404 plutot qu'un refus,\npour ne pas dire a un inconnu quels identifiants existent.","operationId":"lire_la_critique_v1_critique__work_id__post","parameters":[{"name":"work_id","in":"path","required":true,"schema":{"type":"string","title":"Work Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["critique-get"],"summary":"Lire La Critique","description":"Le verdict, quand il est rendu. GET ou POST, au choix.\n\nPOURQUOI LES DEUX SUR UNE LECTURE. Un GET est la forme juste pour\nlire, et reste celle que suivra un assistant qui n'a qu'un outil de\nrecuperation d'adresse. Mais un client qui vient de soumettre en\nPOST doit alors CHANGER DE METHODE en cours de route — et c'est\nexactement ce changement qui a coute une erreur 400 a Arthur le\n2026-09-01, un corps JSON laisse sur un GET etant ignore par HTTP.\nAccepter les deux supprime la manoeuvre : on garde POST du debut a\nla fin, ou GET si l'outil ne sait faire que cela. Le corps n'est\njamais lu ici.\n\nL'appartenance est verifiee comme partout ailleurs : une oeuvre ne se\nlit que depuis le reseau qui l'a soumise. Un 404 plutot qu'un refus,\npour ne pas dire a un inconnu quels identifiants existent.","operationId":"lire_la_critique_v1_critique__work_id__get","parameters":[{"name":"work_id","in":"path","required":true,"schema":{"type":"string","title":"Work Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/critique/{work_id}/avis":{"post":{"tags":["critique-get"],"summary":"Deposer Un Avis","description":"Recueillir ce que l'auteur pense de la critique. Jamais l'oeuvre.\n\nLA PROMESSE PUBLIQUE NE BOUGE PAS. Le site dit « il n'entraine aucun\nmodele sur les OEUVRES SOUMISES ». Ce qui est recueilli ici n'est pas\nune oeuvre : c'est un jugement porte sur un texte que nous avons\necrit. Collecter n'est d'ailleurs pas entrainer — l'usage de ces avis\npour entrainer quoi que ce soit reste une question ouverte pour le\njuriste, et rien ne les lit encore.\n\nON NE DEMANDE PAS AVANT LE VERDICT. Un avis sur une critique qui\nn'existe pas encore ne mesure rien.","operationId":"deposer_un_avis_v1_critique__work_id__avis_post","parameters":[{"name":"work_id","in":"path","required":true,"schema":{"type":"string","title":"Work Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Avis"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/critique/{work_id}/version":{"post":{"tags":["critique-get"],"summary":"Soumettre Une Version","description":"Renvoyer une oeuvre retravaillee, rattachee a sa version d'avant.\n\nPOURQUOI CETTE PORTE EXISTE. Arthur, 2026-09-03 : « tout le produit\nmarche sur la reiteration de l'oeuvre — le fait de la renvoyer et de\ngarder la memoire de l'oeuvre va rendre ce produit exclusif ».\n\nOr la reiteration n'etait joignable que par\n`/v1/theque/progression/{id}/version`, qui EXIGE UNE CLE. Le parcours\ngratuit — celui qui marche, celui que les assistants empruntent — ne\npouvait donc pas soumettre de v2 du tout. La fonctionnalite qui fait\nla difference du produit etait construite, amelioree, et injoignable.\n\nL'APPARTENANCE se verifie comme pour la lecture d'un verdict : sur un\nessai anonyme, l'identifiant de l'oeuvre EST le secret — opaque,\nimprevisible, rendu une seule fois a celui qui a soumis. C'est la\ndecision du 2026-09-01, et elle s'applique ici sans changement : qui\npeut lire le verdict peut renvoyer une version.\n\nL'ORDRE DES CONTROLES n'est pas indifferent : tout ce qui refuse\ngratuitement passe AVANT ce qui coute.","operationId":"soumettre_une_version_v1_critique__work_id__version_post","parameters":[{"name":"work_id","in":"path","required":true,"schema":{"type":"string","title":"Work Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Version"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/depot/":{"post":{"tags":["depot"],"summary":"Ouvrir Un Depot Post","description":"Ouvre un dépôt. Aucun contenu d'utilisateur n'est exigé.","operationId":"ouvrir_un_depot_post_v1_depot__post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/DepotDemande"},{"type":"null"}],"title":"Corps"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["depot"],"summary":"Ouvrir Un Depot Get","description":"La même chose en GET, pour un assistant qui ne sait que lire.","operationId":"ouvrir_un_depot_get_v1_depot__get","parameters":[{"name":"room","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"roman, bd, video, musique, photo ou autre.","title":"Room"},"description":"roman, bd, video, musique, photo ou autre."},{"name":"title","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Facultatif.","title":"Title"},"description":"Facultatif."},{"name":"turbo","in":"query","required":false,"schema":{"type":"boolean","description":"La même critique, environ quatre fois plus vite : les jurés lisent ensemble au lieu de se suivre. Même prix.","default":false,"title":"Turbo"},"description":"La même critique, environ quatre fois plus vite : les jurés lisent ensemble au lieu de se suivre. Même prix."},{"name":"parent","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"L'identifiant de l'oeuvre dont celle-ci est la nouvelle version. La salle devient alors celle du parent.","title":"Parent"},"description":"L'identifiant de l'oeuvre dont celle-ci est la nouvelle version. La salle devient alors celle du parent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/depot":{"post":{"tags":["depot"],"summary":"Ouvrir Un Depot Post","description":"Ouvre un dépôt. Aucun contenu d'utilisateur n'est exigé.","operationId":"ouvrir_un_depot_post_v1_depot_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/DepotDemande"},{"type":"null"}],"title":"Corps"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["depot"],"summary":"Ouvrir Un Depot Get","description":"La même chose en GET, pour un assistant qui ne sait que lire.","operationId":"ouvrir_un_depot_get_v1_depot_get","parameters":[{"name":"room","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"roman, bd, video, musique, photo ou autre.","title":"Room"},"description":"roman, bd, video, musique, photo ou autre."},{"name":"title","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Facultatif.","title":"Title"},"description":"Facultatif."},{"name":"turbo","in":"query","required":false,"schema":{"type":"boolean","description":"La même critique, environ quatre fois plus vite : les jurés lisent ensemble au lieu de se suivre. Même prix.","default":false,"title":"Turbo"},"description":"La même critique, environ quatre fois plus vite : les jurés lisent ensemble au lieu de se suivre. Même prix."},{"name":"parent","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"L'identifiant de l'oeuvre dont celle-ci est la nouvelle version. La salle devient alors celle du parent.","title":"Parent"},"description":"L'identifiant de l'oeuvre dont celle-ci est la nouvelle version. La salle devient alors celle du parent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/depot/{jeton}/fichier":{"post":{"tags":["depot"],"summary":"Demander Une Adresse De Depot","description":"Les adresses signées où la page téléversera le fichier.\n\nAppelée par la page, jamais par l'assistant : c'est le navigateur de\nl'humain qui envoie les octets, directement au stockage.","operationId":"demander_une_adresse_de_depot_v1_depot__jeton__fichier_post","parameters":[{"name":"jeton","in":"path","required":true,"schema":{"type":"string","title":"Jeton"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FichierDemande"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/depot/{jeton}/octets":{"post":{"tags":["depot"],"summary":"Televerser Par Le Serveur","description":"Recevoir le fichier ICI, et le pousser au stockage nous-memes.\n\nPOURQUOI PAS L'ADRESSE SIGNEE, QUI EXISTE POURTANT. Constate le\n2026-09-03, sur les 24 planches d'Arthur : le navigateur envoie\ndirectement au stockage, donc en CROSS-ORIGIN, et le seau n'a aucune\nregle CORS — le prevol OPTIONS rend 403 et le navigateur abandonne\nsans rien dire. Le PUT lui-meme reussit hors navigateur, ce qui\nexplique pourquoi tous mes essais en curl passaient.\n\nLa regle CORS serait la correction propre, mais notre jeton R2 n'a\nque les droits d'objet, pas d'administration du seau. Faire passer\nles octets par nous, qui avons deja les droits, retire la question\nentierement : meme origine, aucun prevol, aucune adresse signee a\nexpirer, et un seul aller-retour au lieu de trois.\n\nLe cout est notre bande passante. Il est borne par `max_upload_mb`\net par le nombre de planches ; a l'echelle d'un depot humain — deux\ndouzaines d'images — il ne se voit pas.","operationId":"televerser_par_le_serveur_v1_depot__jeton__octets_post","parameters":[{"name":"jeton","in":"path","required":true,"schema":{"type":"string","title":"Jeton"}},{"name":"content_type","in":"query","required":true,"schema":{"type":"string","description":"Le type du fichier envoye.","title":"Content Type"},"description":"Le type du fichier envoye."},{"name":"index","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":59,"minimum":0},{"type":"null"}],"title":"Index"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/depot/{jeton}/pret":{"post":{"tags":["depot"],"summary":"Declarer Le Depot Termine","description":"Le fichier est en place : on attache et on met en file.","operationId":"declarer_le_depot_termine_v1_depot__jeton__pret_post","parameters":[{"name":"jeton","in":"path","required":true,"schema":{"type":"string","title":"Jeton"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PretDemande"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/depot/{jeton}/texte":{"post":{"tags":["depot"],"summary":"Deposer Le Texte","description":"Le texte est colle : on l'attache et on met en file.\n\nLE PENDANT DE `declarer_le_depot_termine`, POUR CE QUI N'A PAS\nD'OCTETS. Un texte ne se televerse pas : il tient dans le corps\nd'une requete, et la page l'envoie d'un seul appel.\n\nPOURQUOI CETTE ROUTE EXISTE. `POST /v1/critique` fabriquerait une\noeuvre NEUVE, dont l'assistant n'aurait jamais l'identifiant :\nl'adresse `poll` qu'il surveille pointe sur l'oeuvre ouverte par le\ndepot. Le texte doit donc rejoindre CELLE-LA, sans quoi l'assistant\nattendrait indefiniment un verdict rendu ailleurs.","operationId":"deposer_le_texte_v1_depot__jeton__texte_post","parameters":[{"name":"jeton","in":"path","required":true,"schema":{"type":"string","title":"Jeton"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TexteDepose"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/d/{jeton}":{"get":{"tags":["depot"],"summary":"Page De Depot","description":"La page où l'humain dépose. Rien d'autre ne s'y passe.\n\nElle est volontairement nue : un cadre, un bouton, une phrase de\nconfirmation. La personne qui l'ouvre ne connaît pas forcément\nl'informatique, ne reviendra jamais, et attend dans une conversation\nouverte à côté. Tout ce qui n'aide pas à déposer lui nuit.","operationId":"page_de_depot_d__jeton__get","parameters":[{"name":"jeton","in":"path","required":true,"schema":{"type":"string","title":"Jeton"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/mcp/":{"post":{"tags":["mcp"],"summary":"Point D Entree","description":"L'unique point d'entrée MCP.\n\nAuthentifié par la même clé que le reste de l'API : `get_current_user`\naccepte indifféremment une clé de programme et une session de\nnavigateur, donc rien de neuf n'a été inventé pour cette porte.","operationId":"point_d_entree_v1_mcp__post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"JSON malforme, lot vide, ou en-tete MCP-Protocol-Version refuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"403":{"description":"Requete emise par une page tierce (Origin ou Sec-Fetch refuse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"415":{"description":"Content-Type absent ou non conforme : application/json est exige.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/mcp":{"post":{"tags":["mcp"],"summary":"Point D Entree","description":"L'unique point d'entrée MCP.\n\nAuthentifié par la même clé que le reste de l'API : `get_current_user`\naccepte indifféremment une clé de programme et une session de\nnavigateur, donc rien de neuf n'a été inventé pour cette porte.","operationId":"point_d_entree_v1_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"JSON malforme, lot vide, ou en-tete MCP-Protocol-Version refuse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"403":{"description":"Requete emise par une page tierce (Origin ou Sec-Fetch refuse).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}},"415":{"description":"Content-Type absent ou non conforme : application/json est exige.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Refus"}}}}},"security":[{"APIKeyHeader":[]}]},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/beta/{chemin}":{"get":{"tags":["beta"],"summary":"Beta","description":"Sert un fichier du build, ou index.html pour toute autre route.\n\nLe repli sur index.html n'est pas une commodité : l'application\nutilise des URLs sans dièse (/beta/salon, /beta/jury/okonkwo), et\nsans lui un rechargement ou un lien partagé tomberait sur un 404\nalors que la route existe côté application.","operationId":"beta_beta__chemin__get","parameters":[{"name":"chemin","in":"path","required":true,"schema":{"type":"string","title":"Chemin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/beta":{"get":{"tags":["beta"],"summary":"Beta","description":"Sert un fichier du build, ou index.html pour toute autre route.\n\nLe repli sur index.html n'est pas une commodité : l'application\nutilise des URLs sans dièse (/beta/salon, /beta/jury/okonkwo), et\nsans lui un rechargement ou un lien partagé tomberait sur un 404\nalors que la route existe côté application.","operationId":"beta_beta_get","parameters":[{"name":"chemin","in":"query","required":false,"schema":{"type":"string","default":"","title":"Chemin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/":{"get":{"summary":"Racine","description":"La carte du service, pour qui n'a que l'adresse de l'API.\n\nUne IA a qui on donne `api.infinitecritics.com` recevait 404 ici :\naucune facon de trouver l'essai gratuit, le MCP, ni le catalogue.\nUn service agent-first dont la racine ne dit rien oblige un humain a\nfaire les presentations.","operationId":"racine__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]},"/v1/health":{"get":{"summary":"Health","description":"État du service, et ce qu'il sait faire aujourd'hui.\n\n`mail` dit si un courrier peut réellement partir — c'est-à-dire si\nle prestataire ACCEPTE la clé, pas seulement si une clé est posée.\nLa première version répondait `bool(cle)` : une clé révoquée ou mal\nrecopiée donnait `true`, le front rouvrait « mot de passe oublié »,\net l'on retombait sur le défaut que ce champ existe pour empêcher. Le front s'en sert\npour ne pas proposer « mot de passe oublié » quand aucun message ne\npartirait : une route qui répond n'est pas un service qui\nfonctionne, et promettre un courrier qui n'arrivera jamais est pire\nque d'annoncer l'indisponibilité.\n\nPoser la clé suffit alors à ouvrir la fonction, sans redéployer le\nfront — c'est tout l'intérêt de la faire dire ici plutôt que de la\ncoder en dur à l'écran.\n\nAucun secret ne sort d'ici : la présence, jamais la valeur.","operationId":"health_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"servers":[{"url":"https://api.infinitecritics.com","description":"Ce chemin n'est servi que sous ce sous-domaine : l'apex enumere ses routes et ne l'inclut pas."}]}},"components":{"schemas":{"AnnulationBody":{"properties":{"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"}},"type":"object","title":"AnnulationBody","description":"Credentials for the case where the session is already gone.\n\nBoth optional: a caller who still holds a valid token sends none of\nthis and authenticates with the header instead."},"Avis":{"properties":{"exactitude":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exactitude"},"utilite":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utilite"},"accord":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accord"},"jure":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jure"},"commentaire":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commentaire"},"source":{"type":"string","title":"Source","default":"assistant"}},"type":"object","title":"Avis","description":"Trois questions, et aucune ne demande « c'etait bien ? ».\n\nUn auteur mal note juge severement la critique qui l'a mal note. Une\nquestion unique mesurerait donc surtout la note recue, et un modele\nentraine dessus apprendrait a etre complaisant : l'inverse du\nproduit. On separe donc ce que le sens commun confond."},"AvisBody":{"properties":{"work":{"type":"string","maxLength":32,"minLength":1,"title":"Work"},"persona":{"type":"string","maxLength":64,"minLength":1,"title":"Persona"},"agree":{"type":"boolean","title":"Agree"}},"type":"object","required":["work","persona","agree"],"title":"AvisBody"},"CheckoutBody":{"properties":{"price_id":{"type":"string","title":"Price Id"}},"type":"object","required":["price_id"],"title":"CheckoutBody"},"ConservationBody":{"properties":{"keep":{"type":"boolean","title":"Keep"}},"type":"object","required":["keep"],"title":"ConservationBody","description":"`keep` is the whole choice. False means: erase it, an hour after\nit was submitted."},"ContestationBody":{"properties":{"declaration":{"type":"string","maxLength":1500,"minLength":40,"title":"Declaration"}},"type":"object","required":["declaration"],"title":"ContestationBody"},"CritiqueBody":{"properties":{"text":{"type":"string","title":"Text"},"title":{"type":"string","title":"Title","default":"Sans titre"},"room":{"type":"string","title":"Room","default":"novel"},"lane":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lane"},"turbo":{"type":"boolean","title":"Turbo","default":false}},"type":"object","required":["text"],"title":"CritiqueBody","description":"Le corps accepte par POST, pour que le texte n'ait pas a voyager\ndans l'adresse.\n\nLES NOMS FRANCAIS SONT ACCEPTES, et ce n'est pas une commodite.\nTrouve en relisant le service a froid le 2026-09-04 : un corps\n`{\"texte\": …, \"salle\": …}` recevait un 422 de validation brut, avec\nle nom du champ manquant et l'integralite de ce qui avait ete envoye.\nC'est exactement le refus opaque qui a coute du temps a Arthur le\n2026-09-01, corrige alors sur la porte GET et jamais sur celle-ci.\n\nOr « texte » et « salle » sont l'erreur NATURELLE d'un public qui\necrit en francais, sur un service dont toute la documentation est en\nfrancais. La refuser serait la meme pedanterie de nommage que\nrefuser `room=roman` — deja tranchee, dans l'autre sens, le\n2026-09-01."},"DepotDemande":{"properties":{"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"parent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent"},"turbo":{"type":"boolean","title":"Turbo","default":false},"lyrics":{"anyOf":[{"type":"string","maxLength":20000},{"type":"null"}],"title":"Lyrics"}},"type":"object","title":"DepotDemande"},"EditBody":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"cover_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Key"}},"type":"object","title":"EditBody","description":"Ce qu'un auteur peut retoucher après le verdict.\n\nNi le dossier, ni les planches, ni le fichier : ils sont ce que le\nJury a jugé, et les changer après coup rendrait la critique\ninexplicable. Seule la vitrine se règle."},"FichierDemande":{"properties":{"content_type":{"type":"string","title":"Content Type"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes"},"count":{"type":"integer","maximum":60.0,"minimum":1.0,"title":"Count","default":1}},"type":"object","required":["content_type"],"title":"FichierDemande"},"ForgotBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"ForgotBody"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Lien":{"properties":{"url":{"type":"string","maxLength":300,"title":"Url"},"label":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Label"}},"type":"object","required":["url"],"title":"Lien","description":"Une adresse, et rien de plus.\n\n`label` reste accepté pour ne pas casser les clients déployés,\nmais il est ignoré : le libellé affiché est rendu par le client à\npartir de la plateforme reconnue, ce qui le rend traduisible."},"LoginBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginBody"},"PasswordBody":{"properties":{"password":{"type":"string","maxLength":200,"minLength":8,"title":"Password"}},"type":"object","required":["password"],"title":"PasswordBody"},"PretDemande":{"properties":{"media_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Key"},"plates":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Plates"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"}},"type":"object","title":"PretDemande"},"ProfilBody":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Display Name"},"handle":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Handle"},"bio":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Bio"},"avatar_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Key"},"links":{"anyOf":[{"items":{"$ref":"#/components/schemas/Lien"},"type":"array"},{"type":"null"}],"title":"Links"}},"type":"object","title":"ProfilBody"},"ProfileBody":{"properties":{"display_name":{"type":"string","title":"Display Name"}},"type":"object","required":["display_name"],"title":"ProfileBody"},"PublishBody":{"properties":{"published":{"type":"boolean","title":"Published"}},"type":"object","required":["published"],"title":"PublishBody"},"RegisterBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","maxLength":200,"minLength":8,"title":"Password"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"invite_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite Code"},"accepted_terms_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accepted Terms Version"}},"type":"object","required":["email","password"],"title":"RegisterBody"},"ResetBody":{"properties":{"token":{"type":"string","maxLength":200,"minLength":16,"title":"Token"},"password":{"type":"string","maxLength":200,"minLength":8,"title":"Password"}},"type":"object","required":["token","password"],"title":"ResetBody"},"SignupBody":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"invite_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite Code"},"accepted_terms_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accepted Terms Version"}},"type":"object","required":["email"],"title":"SignupBody"},"TexteDepose":{"properties":{"text":{"type":"string","title":"Text"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"}},"type":"object","required":["text"],"title":"TexteDepose"},"UploadRequest":{"properties":{"content_type":{"type":"string","title":"Content Type"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes"},"count":{"type":"integer","maximum":60.0,"minimum":1.0,"title":"Count","default":1}},"type":"object","required":["content_type"],"title":"UploadRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyBody":{"properties":{"token":{"type":"string","maxLength":200,"minLength":16,"title":"Token"}},"type":"object","required":["token"],"title":"VerifyBody"},"Version":{"properties":{"text":{"type":"string","title":"Text"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"turbo":{"type":"boolean","title":"Turbo","default":false}},"type":"object","required":["text"],"title":"Version"},"VersionSubmit":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"dossier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dossier"},"media_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Key"},"cover_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Key"},"plates":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Plates"}},"type":"object","title":"VersionSubmit","description":"Ce qu'on fournit pour une nouvelle version.\n\nNi `vertical` ni l'auteur : tous deux héritent du parent, et ne sont\npas discutables. Une « progression » qui change de salle ou d'auteur\nn'est pas une progression."},"VoteBody":{"properties":{"token":{"type":"string","maxLength":128,"minLength":8,"title":"Token"},"winner_external_id":{"type":"string","maxLength":32,"minLength":1,"title":"Winner External Id"}},"type":"object","required":["token","winner_external_id"],"title":"VoteBody"},"WorkSubmit":{"properties":{"vertical":{"type":"string","title":"Vertical"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"dossier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dossier"},"media_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Key"},"cover_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Key"},"plates":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Plates"},"keep":{"type":"boolean","title":"Keep","default":true}},"type":"object","required":["vertical","title"],"title":"WorkSubmit","description":"Payload for POST /theque/works. user_id comes from the API key,\nnever from the body."},"Refus":{"title":"Refus","type":"object","properties":{"detail":{"title":"Detail","type":"string","description":"Ce qui a ete refuse, et pourquoi."}},"required":["detail"]}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"Authorization"}}},"servers":[{"url":"https://infinitecritics.com","description":"Domaine a preferer : certains outils de navigation d'assistants echouent sur les sous-domaines d'API. Il sert la critique, le depot, l'essai, le paiement et les pages de verdict. Les chemins qu'il ne sert pas portent leur propre adresse."}]}