# Ldata.fr — full agent / LLM manual This document is the complete machine-oriented guide. Prefer reading /llms.txt first, then this file when implementing tools. Site also publishes: /docs (human), /swagger (Swagger UI), /openapi.yaml (OpenAPI 3), /browse (HTML corpus), /api-tools.json (OpenAI-style function schemas). ================================================================================ 0. QUICK START — SEARCH EVERYTHING, THEN FETCH ================================================================================ GET /find?q=…[&corpora=caselaw,codes|all][&limit=3] One FTS5 query over every corpus, hits grouped by corpus (best first inside a corpus; scores are not comparable across corpora). One quota unit in total. Default corpora: caselaw, codes, statutes, admin_caselaw, constitutional, eu, collective_agreements, tax_doctrine, authorities, circulars, official_journal, legislative_dossiers. Opt-in: company_agreements, commercial_notices, public_procurement. Failing corpora are listed in meta.unavailable. Hit: { id: ":", corpus, title, date, url, snippet } GET /fetch/{corpus}:{id}[?as_of=YYYY-MM-DD][&full=true] Normalized document { id, corpus, title, text, url, metadata }. LEGI ids are codes:/ and statutes:/; as_of applies to them. MCP tools: `search` (arg `query`) and `fetch` (arg `id`). GET /sql/schema[?database=justice] POST /sql {database, sql, bind[], max_rows, max_cell_chars} (GET with the same params works) One read-only SQLite SELECT (WITH / EXPLAIN ok) on one corpus database: justice, legis, eu, admin, kali, bofip, constit, authorities, circ, jorf, dole, acco, bodacc, boamp. Never the users database. Refused (400): ATTACH, writes, pragmas other than table_info-style introspection, randomblob/zeroblob/ load_extension, more than one statement. Stops after 60 s → 422 sql_limit_exceeded (charged). Paged: max_rows (default 100, max 1000) + offset; has_more / next_offset tell how to continue (keep a stable ORDER BY). Meant for aggregations and targeted lists, not for dumping a corpus. Full-text pattern: SELECT d.* FROM documents_fts JOIN documents d ON d.rowid = documents_fts.rowid WHERE documents_fts MATCH 'terme' ORDER BY rank (justice: decisions_fts / decisions). Response: columns, rows, row_count, has_more, truncated_cells (long text cut at max_cell_chars, default 2000). MCP tools: `get_sql_schema`, `sql_query`. Use the corpus-specific endpoints below when you need their filters. ================================================================================ 1. PRODUCT ================================================================================ Ldata.fr indexes: A) French open judicial decisions (ordre judiciaire): - cc = Cour de cassation - ca = cours d'appel - tj = tribunaux judiciaires - tcom = tribunaux de commerce ~2 million decisions, full-text FTS5, French citation strings, verify hand-off. B) French legislation (LEGI open data): - Codes + TNC with a **version graph** (all ETAT; look up with `as_of=YYYY-MM-DD`) - ~1.5–2M article versions indexed; `legi_status` vs `in_force_on_as_of` are distinct - No silent fallback when `as_of` is outside coverage → 404 C) French administrative case law (DILA JADE fund): - CE (Conseil d'État), CAA (cours administratives d'appel), TA (tribunaux administratifs) - Full-text FTS5 over the JADE stock + daily increments; JADE's own TA versement is near-empty, so TA is backfilled from opendata.justice-administrative.fr (since 2022-06-30) - `GET /admin-caselaw/search`, `GET /admin-caselaw/decisions/{id}` — same find-here-cite-elsewhere contract; verify_url points to Légifrance (JADE hits) or opendata.justice-administrative.fr (TA backfill hits) D) EU law (EUR-Lex + CJUE + ECHR): - EUR-Lex consolidated legislation (French), CJUE case-law, ECHR (HUDOC) judgments - `GET /eu/search`, `GET /eu/documents/{id}` (id, CELEX number, or ECLI) - Licences: EUR-Lex © EU, reuse allowed (Decision 2011/833/EU); HUDOC © Council of Europe E) Collective bargaining agreements (DILA KALI fund, IDCC): - Conventions collectives nationales, textes attachés (avenants/accords), articles - `GET /collective-agreements/search`, `GET /collective-agreements/documents/{id}` - Only `article` documents carry body text; `convention`/`texte` return indexed titles F) Tax doctrine (BOFiP-Impôts, DGFiP open data): - Versioned, opposable administrative tax doctrine (`date_start`/`date_end` windows) - `GET /tax-doctrine/search`, `GET /tax-doctrine/documents/{id}` — both accept `as_of` G) Conseil constitutionnel (DILA CONSTIT fund): - DC, QPC, LP (lois de pays), AN/SEN (contentieux électoral), other contentieux since 1958 - `GET /constitutional/search`, `GET /constitutional/decisions/{id}` H) Independent administrative authorities (ADLC + CNIL): - ADLC (Autorité de la concurrence): every published decision since 1988, data.gouv.fr dataset - CNIL: deliberations/sanctions since ~1978, DILA CNIL fund - `GET /authorities/search`, `GET /authorities/documents/{id}` I) Circulaires et instructions des administrations (DILA CIRCULAIRES fund): - Legacy 2009-2014 bulk export (metadata-only) + per-drop increments since 2014 (full text) - Full text where a PDF was published: extracted with `pdftotext` at index time (`full_text: true`) - `GET /circulars/search`, `GET /circulars/documents/{id}` NOT covered (or incomplete): - Administrative case law (CE/CAA/TA) has no semantic/hybrid search — FTS only (same for D-I) - Criminal CA/TJ case law is not yet in open data (separate regulatory calendar) - TJ closed-session cases: motives often occulted at source (dispositif only) - Doctrine / JO as a separate product beyond BOFiP and circulaires - Semantic/hybrid embeddings (mode=semantic|hybrid) exist ONLY for judicial case-law (A), and start Cassation-first; check `/api/health` → `embeddings`. All other corpora are FTS-only. Data licence: Licence Ouverte 2.0 for DILA-sourced corpora (A, B, C, E, F, G, most of H). Pseudonymised at source. Do not re-identify parties. EU/HUDOC/ADLC corpora carry their own terms — see D and H above. ================================================================================ 2. HARD RULES FOR LLM TOOL USE ================================================================================ 1. FIND ≠ CITE. This API is a discovery layer. Before any formal citation, re-verify on official sources (Judilibre / Légifrance). Use verify_url / verify_note. 2. NO HALLUCINATED CASE LAW. If the API returns zero hits, say so. Do not fabricate arrêts. 3. QUOTE ONLY FROM `snippet` or `text` fields returned by the API. Strip highlight markers >>> <<< if present. 4. PREFER search over full text. Call decisions/{id} only for a shortlist (1–3 ids). Prefer not to request full=1 unless the user needs the full judgment. 5. POSSIBLY_REVERSED is a soft warning (CA + date level). Tell the user to verify; do not assert “this exact RG was quashed” solely from that flag. 6. RESPECT RATE LIMITS. On HTTP 429, stop, report remaining quota if known, suggest waiting until reset (X-RateLimit-Reset unix timestamp). 7. NUMBER INPUT: pass bare numbers (17/07602, 21-14.490). Do not prefix with "RG", "n°", "pourvoi". 8. jurisdiction codes only: cc, ca, tj, tcom. Location is free substring (court name or API code). ================================================================================ 3. AUTHENTICATION ================================================================================ Obtain a key: 1. Human signs up on the website 2. Dashboard → create API key 3. Key shown once; format oj_ Send on every authenticated request: Authorization: Bearer oj_xxxxxxxx Alternative: X-Api-Key: oj_xxxxxxxx No OAuth. No cookies for API. Plans (typical): - pro: 500 req/day, 30/min, max limit 50; vip: 2000 req/day, 60/min (no free tier: 402 subscription_required) - pro: higher - internal: highest Response headers (authenticated): X-RateLimit-Limit X-RateLimit-Remaining X-RateLimit-Reset (unix time, day boundary) ================================================================================ 4. BASE URL ================================================================================ {origin}/api All paths below are relative to that prefix unless noted. ================================================================================ 5. ENDPOINTS ================================================================================ -------------------------------------------------------------------------------- 5.1 GET /health — NO AUTH -------------------------------------------------------------------------------- Purpose: Check corpus availability and freshness before heavy search. Example: GET {origin}/api/health Example 200 body: { "status": "ok", "corpus": true, "decisions": 1993332, "by_jurisdiction": { "ca": 642452, "cc": 611585, "tj": 591421, "tcom": 147874 }, "max_decision_date": "2026-07-09", "file_mtime": "2026-07-15T10:38:03Z", "age_days": 7, "stale": false, "stale_after_days": 14, "last_update": { "id": 3, "finished_at": "2026-07-16T12:00:00Z", "decisions_count": 1993332, "trigger": "admin" } } status values: - ok — searchable and not stale - stale — searchable but age_days > stale_after_days (HTTP still 200) - degraded — corpus missing/error (HTTP 503) `status`/HTTP code reflect judicial case-law (A) only — the primary corpus. The other eight corpora never flip the top-level status; each degrades independently (see below). Also returns `legis: { available, instruments, articles, by_nature, age_days, stale, … }` (same top-level-sibling shape as case-law, for backwards compatibility). The other seven corpora (D-I above) are reported under `corpora`, one entry per corpus, keyed eu/admin/kali/bofip/constit/authorities/circ: { "corpora": { "eu": { "available": true, "documents": 138061, "max_date": "2026-07-27", "file_mtime": "…" }, "admin": { "available": true, "documents": 881960, "max_date": "2026-07-31", "file_mtime": "…" }, "kali": { "available": true, "documents": 346869, "max_date": "…", "file_mtime": "…" }, "bofip": { "available": true, "documents": 6338, "max_date": "…", "file_mtime": "…" }, "constit": { "available": true, "documents": 7379, "max_date": "…", "file_mtime": "…" }, "authorities": { "available": true, "documents": 16434, "max_date": "…", "file_mtime": "…" }, "circ": { "available": true, "documents": 35605, "max_date": "…", "file_mtime": "…" } } } A corpus not yet built, or unreadable, reports `{ "available": false }` there — never an error response for the whole /health call. Agent action: if degraded, do not pretend case-law search works. If stale, warn user corpus may lag. If legis.available is false, statute endpoints return 503. If a corpora.{name} entry is available:false, that corpus's search/documents endpoints return 503 too. -------------------------------------------------------------------------------- 5.1b GET /codes* — LEGISLATION (LEGI) -------------------------------------------------------------------------------- | Method | Path | Auth | Purpose | |--------|------|------|---------| | GET | /codes | no | Catalogue (codes by default) | | GET | /codes/{slug} | no | One instrument | | GET | /codes/{slug}/articles | yes | TOC (structure only) | | GET | /codes/{slug}/articles/{num} | yes | Full article text + vigueur | | GET | /codes/search?q=… | yes | FTS over statute bodies | Do NOT confuse `/caselaw/by-article/{code}` (case-law citing a visa) with `/codes/.../articles/{num}` (statute text). Legacy alias `/articles/{code}` = same as caselaw/by-article (prefer the caselaw path). Example: GET /api/codes/code-civil/articles/373-2-2 Authorization: Bearer oj_... Response includes: citation, text, structure.path, vigueur.*, verify_url (Légifrance). Naming (prefer these over short aliases): | Field | Meaning | |-------|---------| | instrument_slug | URL key for the code/TNC (`code-civil`) | | instrument_id | Stable LEGI cid (`LEGITEXT…`) | | legi_status | Raw LEGI ETAT (`VIGUEUR`, `ABROGE`, `MODIFIE`, …) — a source label | | in_force_on_as_of | True iff date_start..date_end covers `as_of` (authoritative for history) | | as_of | Requested day (YYYY-MM-DD) | No short aliases (`in_force`, `status`, `slug`, `date`). Do not treat `legi_status=ABROGE` alone as “never applicable on as_of”: use `in_force_on_as_of`. -------------------------------------------------------------------------------- 5.1c GET /statutes* — CONSOLIDATED LAWS / DECREES (LEGI TNC) -------------------------------------------------------------------------------- Same corpus as `/codes`, different surface: **never returns CODE**. LODA-equivalent (LOI, DECRET, ORDONNANCE, ARRETE, …). Lookup by slug, cid (`JORFTEXT…`/`LEGITEXT…`), or NOR. | Method | Path | Auth | Purpose | |--------|------|------|---------| | GET | /statutes | no | TNC catalogue (`nature`, `q`, `nor`) | | GET | /statutes/{id} | no | One text (slug, cid, or NOR) | | GET | /statutes/{id}/articles | yes | TOC | | GET | /statutes/{id}/articles/{num} | yes | Full article + vigueur | | GET | /statutes/search?q=… | yes | FTS over TNC bodies | A CODE id on this prefix 404s with a pointer to `/api/codes/{slug}`. `verify_url` is `https://www.legifrance.gouv.fr/loda/id/{cid}`. Example: GET /api/statutes/search?q=délégations+de+signature&nature=DECRET Authorization: Bearer oj_... -------------------------------------------------------------------------------- 5.2 GET /search — AUTH REQUIRED (CASE-LAW) -------------------------------------------------------------------------------- Purpose: Full-text (+ filters) discovery. Primary tool for agents. Must provide at least one of: q, number, article, cites, cited_by. Query parameters: | Name | Type | Notes | |------|------|--------| | q | string | FTS5 query | | jurisdiction | string | cc \| ca \| tj \| tcom | | location | string | substring court label/code | | number | string | case number | | article | string | visa reference | | outcome | string | cassation\|rejet\|irrecevabilite\|infirmation\|confirmation\|autre | | after | date | YYYY-MM-DD inclusive | | before | date | YYYY-MM-DD exclusive | | sort | string | authority (default) \| rank \| date | | syn | boolean | expand legal synonyms | | reversed | boolean | CA possibly quashed filter | | cites | string | decision id | | cited_by | string | cassation decision id | | limit | int | default 10, max by plan ≤ 50 | FTS tips: - Phrase: "pension alimentaire" - Boolean: pension AND alimentaire - Proximity: "avantage en nature" NEAR/20 pension (quote multi-word operands) - Prefix: subvention* - Accents optional (diacritics stripped in index) Search modes (mode=fts | semantic | hybrid): - fts (default): FTS5 keyword search, exact terms/articles/numbers. - semantic: vector search (bge-m3 cosine); q in plain language — fact patterns work ("un chien a mordu un passant" finds art. 1385 case-law). - hybrid: RRF fusion of both rankings — RECOMMENDED for natural-language queries; lexical hits (articles, pourvoi numbers) and conceptual hits both surface. semantic=1 is a legacy alias of mode=semantic. - semantic/hybrid accept only jurisdiction/after/before/limit/offset with q (400 otherwise). Hits gain "similarity" (cosine) when the vector leg saw them — a per-query ranking signal, not a calibrated score. - meta.semantic_corpus = embedded decisions matching your filters (grows over time, not the full corpus); meta.semantic_index = vec0 (native KNN) or scan. - If the embedding backend / vec0 index is down, the response falls back to FTS with meta.mode="fts" and meta.semantic_fallback set to a stable code (no_embeddings_store, empty_embeddings_store, model_mismatch, dimension_mismatch, embedding_backend_error, embeddings_store_error, vector_extension_unavailable, vector_index_unusable). - Large Ruby scans are refused (vector_* codes) so agents get a fast FTS fallback instead of a 30–60 s hang. Prefer mode=fts until embeddings cover the jurisdiction you need (`/api/health` → embeddings.by_jurisdiction). Example request: GET /api/search?q=%22pension%20alimentaire%22&jurisdiction=ca&after=2022-01-01&syn=true&limit=5 Authorization: Bearer oj_... Example 200 body (abridged): { "query": { "q": "\"pension alimentaire\"", "jurisdiction": ["ca"], "syn": true, "sort": "authority" }, "results": [ { "id": "6253cc00bd3db21cbdd8ecfb", "citation": "CA Lyon, 9 janv. 2012, RG 10/08334", "jurisdiction": "ca", "location": "Cour d'appel de Lyon", "decision_date": "2012-01-09", "number": "10/08334", "outcome": "...", "publication": null, "snippet": "... >>>pension<<< alimentaire ...", "verify_url": "https://www.courdecassation.fr/recherche-judilibre", "verify_note": "Vérifier : CA Lyon, 9 janv. 2012, RG 10/08334 (rechercher sur Judilibre/Légifrance)", "possibly_reversed": false } ], "meta": { "mode": "fts", "count": 5, "limit": 5, "offset": 0, "has_more": true, "took_ms": 42, "quota": { "plan": "pro", "remaining_day": 494, "limit_day": 500 } } } 400 example: { "error": "bad_request", "message": "provide q, number, article, cites, or cited_by" } 401 example: { "error": "Unauthorized", "message": "Invalid or missing API key" } 429 example: { "error": "rate_limit_exceeded", "kind": "day", "limit": 100, "message": "..." } -------------------------------------------------------------------------------- 5.3 GET /decisions/{id} — AUTH REQUIRED -------------------------------------------------------------------------------- Purpose: Load one decision by stable id (from search results). Query: - full=1 — full text (large). Default truncates (~50000 chars unless configured). Example: GET /api/decisions/6253cc00bd3db21cbdd8ecfb Authorization: Bearer oj_... Example 200 fields: id, source, jurisdiction, location, decision_date, number, outcome, publication, source_updated_on (Judilibre freshness, if stored), chamber, type, titre, citation, verify_url, verify_note, text, truncated, text_length, cites_pourvois, cites_edges, cited_by, possibly_reversed, reversal_signal, graph (note + reversed_by on CA / reverses_ca on CC — high-confidence find-aids, re-verify on Judilibre), quota 404: { "error": "not_found", "message": "No decision with id ..." } -------------------------------------------------------------------------------- 5.4 GET /caselaw/by-article/{code} — AUTH REQUIRED (JURISPRUDENCE) -------------------------------------------------------------------------------- Purpose: Judicial decisions that **cite** a statutory article (visa). NOT the law text. Canonical: /api/caselaw/by-article/373-2-2 Legacy alias: /api/articles/373-2-2 (same payload; prefer caselaw path) Also supports L132-1 style codes. Optional filters: jurisdiction, after, before, sort, limit (same as search). Response includes corpus=caselaw, visa=..., and meta.statute_text_hint pointing at /codes/... -------------------------------------------------------------------------------- 5.5 GET /me — AUTH REQUIRED -------------------------------------------------------------------------------- Purpose: Inspect plan and remaining quota for the current key. Example 200: { "email": "user@example.com", "plan": "pro", "plan_name": "Free", "api_key": { "name": "default", "prefix": "oj_abc12345", "last_used_at": null }, "quota": { "day_limit": 100, "day_used": 6, "day_remaining": 94, "rpm_limit": 10, "max_limit": 20 } } -------------------------------------------------------------------------------- 5.6 GET /eu/* — AUTH REQUIRED (EU LAW: EUR-LEX + CJUE + ECHR) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /eu/search?q=… | FTS; filters source=eurlex_law\|cjeu\|echr, after/before (document date) | | GET | /eu/documents/{id} | Full text by id, CELEX number, or ECLI (full=true lifts 50k-char cap) | Coverage: EUR-Lex consolidated legislation (French), CJUE case-law, ECHR (HUDOC) judgments. Every hit carries `url` — re-verify on EUR-Lex/HUDOC before citing. -------------------------------------------------------------------------------- 5.7 GET /admin-caselaw/* — AUTH REQUIRED (ADMINISTRATIVE CASE-LAW: CE/CAA/TA) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /admin-caselaw/search?q=… | FTS; filters source=ce\|caa\|ta\|autre, after/before | | GET | /admin-caselaw/decisions/{id} | Full text by CETATEXT id, or OJA_... for TA backfill | Coverage: DILA JADE fund (Conseil d'État, CAA) + TA backfilled from opendata.justice-administrative.fr (JADE's own TA versement is near-empty). `url` points to Légifrance for JADE hits, to the opendata platform for backfilled TA hits. -------------------------------------------------------------------------------- 5.8 GET /collective-agreements/* — AUTH REQUIRED (IDCC AGREEMENTS) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /collective-agreements/search?q=… | FTS; filters idcc, doc_type=convention\|texte\|article, after/before (date_start) | | GET | /collective-agreements/documents/{id} | Full text by DILA KALI id | Only `article` documents carry body text; `convention`/`texte` return their indexed titles. -------------------------------------------------------------------------------- 5.9 GET /tax-doctrine/* — AUTH REQUIRED (BOFIP TAX DOCTRINE) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /tax-doctrine/search?q=… | FTS; filters doc_type, serie, as_of=YYYY-MM-DD, after/before on document date | | GET | /tax-doctrine/documents/{id} | Full text by version id / BOI-… id / node id; optional as_of | Versioned, opposable doctrine (`date_start`/`date_end`); `as_of` selects the version opposable on that day (default: latest). -------------------------------------------------------------------------------- 5.10 GET /constitutional/* — AUTH REQUIRED (CONSEIL CONSTITUTIONNEL) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /constitutional/search?q=… | FTS; filters decision_type=dc\|qpc\|lp\|an\|sen\|autre, after/before | | GET | /constitutional/decisions/{id} | Full text by CONSTEXT id | Coverage: DILA CONSTIT fund since 1958 (DC, QPC, LP, contentieux électoral, other contentieux). -------------------------------------------------------------------------------- 5.11 GET /authorities/* — AUTH REQUIRED (ADLC + CNIL) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /authorities/search?q=… | FTS; filters authority=adlc\|cnil, doc_type (no fixed enum), after/before | | GET | /authorities/documents/{id} | Full text by document id (CNILTEXT id, or ADLC decision number e.g. 26-DCC-149) | ADLC (Autorité de la concurrence): every published decision since 1988, data.gouv.fr dataset. CNIL: deliberations/sanctions since ~1978, DILA CNIL fund. -------------------------------------------------------------------------------- 5.12 GET /circulars/* — AUTH REQUIRED (CIRCULAIRES ET INSTRUCTIONS) -------------------------------------------------------------------------------- | Method | Path | Purpose | |--------|------|---------| | GET | /circulars/search?q=… | FTS; filters doc_type=organisation_services\|directives_ministre\|interpretation_juridique\|autre, after/before (signature date) | | GET | /circulars/documents/{id} | Full record by ID_CIRCULAIRE | Circulaires are published as PDF; the body is extracted with `pdftotext` at index time. `full_text: true` on a hit/document means `text` is the actual body; `false` means metadata only (2009-2014 legacy bulk export, or a PDF that was missing/failed to extract). `url` always links the official PDF regardless of `full_text`. ================================================================================ 6. AGENT INTEGRATION PACK ================================================================================ | File | Role | |------|------| | GET /agent-system.txt | Ready-to-paste system prompt | | GET /api-tools.json | OpenAI-style tools v2 (+ x-returns, agent_hints) | | GET /llms.txt | Short index + playbooks | | GET /llms-full.txt | This file | | bin/rails agent:dev_key | Mint local API key (raw shown once) | | bin/rails agent:print_system_prompt | Print system pack to stdout | ### Dev key (agents / CI) ```bash bin/rails agent:dev_key # EMAIL=me@x.com PLAN=internal bin/rails agent:dev_key # ROTATE=1 bin/rails agent:dev_key # force new key export LDATA_API_KEY=oj_... curl -sH "Authorization: Bearer $LDATA_API_KEY" {origin}/api/me ``` ### Quota budget Every authenticated request costs **1 day unit** (search, get_decision, statute, TOC, me). Pro: 500/day, 30/min; VIP: 2000/day, 60/min; max 50 hits/request. Per turn: ≤5 searches, ≤3 get_decision (full=false), avoid health spam. ### Tools (api-tools.json v2) — 38 tools across the corpora health_check, search_caselaw (+ offset, multi jurisdiction), get_decision, search_caselaw_by_article (visa), list_codes, get_instrument, list_code_articles, get_statute_article, get_article_versions, search_legislation, list_statutes, get_statute, search_statutes, search_eu_law_and_caselaw, get_eu_document, search_admin_caselaw, get_admin_decision, search_collective_agreements, get_collective_agreement_document, search_tax_doctrine, get_tax_doctrine_document, search_constitutional_caselaw, get_constitutional_decision, search_authority_decisions, get_authority_document, search_circulars, get_circular_document, search_official_journal, get_official_journal_document, search_commercial_notices, get_commercial_notice, search_public_procurement, get_public_procurement_notice, get_quota Field contracts: see each tool's x-returns and agent_hints.naming in api-tools.json. ================================================================================ 7. MULTI-STEP WORKFLOWS (playbooks) ================================================================================ A. Topic research (civil / family) 1. health_check 2. search_caselaw q="pension alimentaire" jurisdiction=ca after=2022-01-01 syn=true limit=8 3. get_decision ×2–3 (no full) 4. present citation + verify_note + short paraphrase of snippet B. Exact case by number 1. search_caselaw number=17/07602 location=Versailles 2. if unique, get_decision C. Law as of a date 1. get_statute_article instrument_slug=code-civil number=373-2-2 as_of=2010-06-01 2. read vigueur.legi_status + vigueur.in_force_on_as_of 3. optional get_article_versions D. Visa → case-law (not statute text) 1. search_caselaw_by_article visa=373-2-2 jurisdiction=cc 2. or search_caselaw article=L132-1 3. statute text only via get_statute_article E. Empty results recovery - Drop jurisdiction filter - Simplify q (remove NEAR, use OR) - Try syn=true - Broaden dates - Still empty → tell user no hit in open corpus (do not invent) F. The other eight corpora (D-I in section 1) follow the same two-step shape: 1. search_* q="…" (+ that corpus's filters, see 5.6-5.12 / api-tools.json) 2. get_* on 1-3 ids for full text, present citation + url as the verify line No semantic/hybrid mode outside judicial case-law (A) — FTS only. ================================================================================ 8. HTML BROWSE (no API key, for humans or simple agents that can fetch HTML) ================================================================================ GET /browse Query params mirror search filters: q, jurisdiction, location, number, article, outcome, after, before, syn, reversed, sort, page GET /browse/{id} Decision HTML page; ?full=1 for longer text Prefer JSON API for tool use; use browse only if HTML is the only option. ================================================================================ 9. WHAT NOT TO DO ================================================================================ - Do not call endpoints outside /api for data access - Do not send session cookies as API auth - Do not scrape /browse HTML when the API is available - Do not request full=1 on many decisions in a loop - Do not claim the corpus is exhaustive for all French law - Do not store or re-publish full texts as a competing database without licence care ================================================================================ 10. SUPPORTING FILES ================================================================================ /llms.txt — short index + playbooks /llms-full.txt — this file /agent-system.txt — system prompt pack /api-tools.json — function schemas v2 + x-returns + agent_hints /openapi.yaml — OpenAPI bin/rails agent:dev_key — local API key for agents /openapi.yaml — OpenAPI 3 specification /swagger — classic Swagger UI HTML /docs — human prose documentation /browse — human corpus browser