What changed in this law between two dates?
For due diligence and regulatory watch an agent needs the text delta of a statute between two dates, not two full texts to diff itself. One call returns the structured segments and a unified patch.
An agent doing due diligence or regulatory watch eventually asks the sharpest
question about a statute: what does it say now that it did not say before? The
naive answer — fetch the full text at date A, fetch it at date B, diff the two —
is exactly the part an agent cannot do reliably at scale or across corpus updates.
One paid call — GET /legal/diff — resolves the version
in force at each date and returns the text delta: a structured segment list
and a git-style unified patch, for French and EU law alike.
The problem: a reliable legal diff is hard to reconstruct
Diffing two legal texts yourself looks easy and is not. You first have to resolve
the correct version in force at each date — not just any text the model
remembers — then tokenize and align them in a way that survives whitespace,
punctuation and consolidation quirks, and do it consistently as the underlying
corpus is refreshed. Two independent /legal/article calls glued together with a
local diff drift apart the moment dates land on version boundaries.
GET /legal/diff does the resolution and the alignment in one place, so the delta
is computed on the consolidated text as stored — the same way every time.
The call: the delta between date A and date B
Supply a French identity (code + article) or an EU identity (celex
or eli, optional article), a required from (date A, the older snapshot), and
an optional to (date B, defaults to today):
GET /legal/diff?code=code-civil&article=1240&from=2000-01-01&to=2026-01-01
GET /legal/diff?celex=32016R0679&article=17&from=2018-01-01
It returns the two resolved versions and the diff between them:
| Field | What the agent learns |
|---|---|
changed | true when the two resolved texts differ |
from_version | The version in force at from (id, etat, dates); null if uncovered |
to_version | The version in force at to; null if uncovered |
diff | The delta payload (see below); zeroed when changed: false |
The diff block is designed for both a reasoning agent and a human reviewer:
| Field | What it carries |
|---|---|
segments | Word-level structured diff: [ { op, text } ], op ∈ equal/insert/delete |
unified | Git-style unified patch (line-granularity) |
added | Total characters added |
removed | Total characters removed |
An agent walks segments to reason about what changed; a reviewer reads
unified like any code review. added/removed give a cheap magnitude signal
for triage.
”Nothing changed” is a successful answer
The honesty point: when from and to resolve to the same version, or the texts
are identical, the response is a 200 with changed: false and an empty,
zeroed diff. An unchanged article is an answer to the question, not an error —
which matters for a watch loop that wants to confirm stability as much as detect
change.
Per the x402 golden rule, the agent pays for the answer to its question. The
4xx range is reserved for what the service genuinely cannot process:
INVALID_REF (no usable identity), MISSING_PARAM (no from), INVALID_DATE,
UNKNOWN_ARTICLE, UNKNOWN_ACT.
Boundary semantics: a date outside all versions is empty text
A subtle, deliberate behaviour: if a date falls outside every known version —
before the article’s first version, or after it was fully repealed — the service
treats that side as empty text rather than erroring. The diff then reads as an
insertion from nothing (“added since”) or a deletion to nothing (“removed
since”), and the empty side is signalled by from_version: null or
to_version: null.
{ "from_version": null,
"to_version": { "version_id": "v-courant", "etat": "vigueur", "date_debut": "2016-10-01" },
"changed": true,
"diff": { "added": 123, "removed": 0, "segments": [ { "op": "insert", "text": "…" } ], "unified": "@@ -0,0 +1 @@\n+…" } }
This keeps the golden rule intact: a known article with a valid reference is
always a 200, even when one date is uncovered. Read from_version/to_version
for null before interpreting a large one-sided delta as a real amendment.
Coverage honesty
The diff is computed purely on the consolidated text as stored in a local
snapshot. The service does not fetch sources at request time, search by
keyword, infer missing versions, or interpret the legal significance of a
change — it tells you the text moved, not what the move means. etat is carried
verbatim, and provenance.freshness.as_of tells you which dump backed both
resolutions; an amendment newer than the snapshot will not appear until the store
is refreshed.
Where it sits in the x402 loop
Diff is the escalation step a monitor reaches for once it knows something changed:
- Discover the endpoint and call it; receive the
402. - Pay — sign the chosen rail and replay the request.
- Diff — read
changed, thensegments/unifiedand the magnitudes. - Branch —
changed: false→ confirm stability and move on;changed: true→ surface the delta for review, or feedsegmentsinto downstream reasoning.
Each paid call follows the same x402 pattern as every Invoket endpoint. The
Quickstart walks the whole discover → 402 → pay → replay
cycle with runnable snippets. Price and accepted rails are not pinned in this
article — they are served live by the
catalog; see the
endpoint reference for the current figure.
The cheap-watch, expensive-diff pattern
diff is the costly, detailed end of a three-endpoint family — pair it with the
cheap timeline poll so you only compute deltas when there is one to compute:
GET /legal/history— when did it change? The cheap, pollable signal (see When did this law last change?).GET /legal/article/GET /legal/eu-act— what does a version say at a date?GET /legal/diff— what changed between two dates?
For a portfolio of provisions,
POST /legal/diff/batch computes many deltas under
one x402 settlement instead of one per article.
Used for what it is — a reliable, reproducible text delta between two dates —
GET /legal/diff gives an agent the amendment itself, not two blobs to reconcile.
For the full field reference, boundary semantics and error codes, see the
GET /legal/diff documentation; for how agents discover
and call Invoket endpoints, see For agents.
French law data is derived from the LEGI dataset published by DILA under the Licence Ouverte / Etalab open licence; EU law data is derived from EUR-Lex / Cellar made available by the Publications Office of the European Union.