# 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. Published 2026-06-28 · Updated 2026-08-16 · HTML version: https://invoket.com/blog/what-changed-in-this-law-between-two-dates --- 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`](/docs/api/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): ```http 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`. ```json { "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: 1. **Discover** the endpoint and call it; receive the `402`. 2. **Pay** — sign the chosen rail and replay the request. 3. **Diff** — read `changed`, then `segments` / `unified` and the magnitudes. 4. **Branch** — `changed: false` → confirm stability and move on; `changed: true` → surface the delta for review, or feed `segments` into downstream reasoning. Each paid call follows the same x402 pattern as every Invoket endpoint. The [Quickstart](/docs/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](https://api.invoket.com/catalog); see the [endpoint reference](/docs/api/legal-diff) 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`](/docs/api/legal-history) — *when* did it change? The cheap, pollable signal (see [When did this law last change? The version history of an article](/blog/when-did-this-law-last-change)). - [`GET /legal/article`](/docs/api/legal-article) / [`GET /legal/eu-act`](/docs/api/legal-eu-act) — *what does* a version say at a date? - [`GET /legal/diff`](/docs/api/legal-diff) — *what changed* between two dates? For a portfolio of provisions, [`POST /legal/diff/batch`](/docs/api/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](/docs/api/legal-diff); for how agents discover and call Invoket endpoints, see [For agents](/docs/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.