Skip to content

Public API

Read-only API over the dossier.

Open CORS, cached at the edge, no key. The same rules as the site: unknown is not zero, and disputed claims stay in the label.

No key · CORS open · GET only

How to use the numbers

  • Unknown is not zero

    A null `affected` means the number is unknown or unpublished. It is never 0. Do not sum it, do not plot it as none, and do not treat a missing CSV cell as an empty incident.

  • Disputed scope stays disputed

    An incident with status "disputed" is a real breach whose headline scope is contested. Attacker-claimed figures live in `affectedLabel` text and never in the numeric field, and never in an aggregate.

  • No inferred attribution

    Attackers are only named where the cited source supports it. Do not infer a group from the method or the sector.

  • Not an exhaustive register

    This is a curated set of major publicly reported incidents, not every French breach. Cite the original `sourceUrl` on each record.

Endpoints

GET /api

Self-describing index: conventions, every endpoint, parameters and examples. Pass `?lang=fr` for French text. HTML docs stay at /docs.

Try this request

GET /api?lang=en

Query

en | fr

Response

Set the fields, then send. This calls the live API.

GET /api/incidents

List and search incidents. JSON includes full localized `detail` on each row (unlike MCP `list_incidents`, which returns compact rows). Unknown counts sort last when `sort=affected`, never as zero. CSV leaves the affected cell empty. Feed and llms routes also accept `?lang=fr`.

Try this request

GET /api/incidents?limit=5&lang=en

Query

Full-text across organisation, sector, method, data, risk, confidence, source and the longer incident-page fields.

government | company

critical | high | medium | low

confirmed | disputed | unknown

Exact sector name, case-insensitive.

e.g. 2026

ISO date, inclusive lower bound.

ISO date, inclusive upper bound.

date | affected | org (default date)

desc | asc (default desc)

1–200, default 100

default 0

json | csv

en | fr

The playground starts at 5 so this panel stays readable. The API default is 100.

Response

Set the fields, then send. This calls the live API.

GET /api/incidents/{id}

One incident with its localized `detail` (lead, timeline, how, taken, notTaken, impact, response, methodDisclosure, optional revision and quotes) plus `lastResearched`, `source` and `sources`. Each source has `kind` (primary | official | secondary), `publisher` and optional `published`. Unknown ids return 404 and list the valid ones. CSV is not offered here; list CSV stays compact and still leaves unknown `affected` empty.

Try this request

GET /api/incidents/ants?lang=en

Path

Incident id from the dossier.

Query

en | fr

Response

Set the fields, then send. This calls the live API.

GET /api/summary

ANSSI and CNIL national figures, plus counts derived from this list. Aggregates never include unknown or disputed attacker claims.

Try this request

GET /api/summary?lang=en

Query

en | fr

Response

Set the fields, then send. This calls the live API.

GET /api/sources

Every source, with `kind` (primary | official | secondary), `publisher`, optional `published` date, and the incidents that cite it.

Try this request

GET /api/sources

Examples

Response

Set the fields, then send. This calls the live API.

GET /api/patterns

Recurring weaknesses and the priority control for each.

Try this request

GET /api/patterns?lang=en

Query

en | fr

Response

Set the fields, then send. This calls the live API.

GET /api/recommendations

Guidance for organisations and the public. Public items include explainerSlug when a matching /learn guide exists.

Try this request

GET /api/recommendations?lang=en

Query

organizations | public

en | fr

Response

Set the fields, then send. This calls the live API.

Streamable HTTP MCP

The same read-only dataset, as a remote MCP server. No key. Stateless JSON over Streamable HTTP, so it runs on ordinary serverless functions and still speaks to 2025-era clients.

POST https://cyberwatch-france.timbenniks.dev/mcp

How to install

Point any MCP host at this URL. No token. After adding it, reload the host and confirm the tools appear.

Cursor

Cursor — save as .cursor/mcp.json in a project, or ~/.cursor/mcp.json for every workspace. Then Cursor Settings → Tools & MCP, enable the server.

{
  "mcpServers": {
    "france-cyberwatch": {
      "url": "https://cyberwatch-france.timbenniks.dev/mcp"
    }
  }
}
Add to Cursor

Claude

Claude Desktop — merge into claude_desktop_config.json (Claude → Settings → Developer). Claude.ai custom connectors can use the same URL.

{
  "mcpServers": {
    "france-cyberwatch": {
      "type": "http",
      "url": "https://cyberwatch-france.timbenniks.dev/mcp"
    }
  }
}

VS Code

VS Code — save as .vscode/mcp.json, or add the server in MCP: Add Server.

{
  "servers": {
    "france-cyberwatch": {
      "type": "http",
      "url": "https://cyberwatch-france.timbenniks.dev/mcp"
    }
  }
}

Tools

list_incidents

Search and filter incidents. Returns compact rows; unknown `affected` stays null, never 0. Unknown counts sort last when sort is affected.

Input — q, kind, severity, status, sector, year, from, to, sort (date|affected|org), order, limit (≤200), offset, lang (en|fr). Same filters as GET /api/incidents.

get_incident

One incident with localized detail, lastResearched, and every cited source. Unknown ids list the valid ones.

Input — id (required), lang (en|fr).

get_summary

ANSSI and CNIL national figures, plus counts from this list. Aggregates never include unknown or disputed attacker claims.

Input — lang (en|fr).

list_sources

Every source, with kind (primary | official | secondary), publisher, optional published date, and the incidents that cite it.

list_patterns

Recurring weaknesses and the priority control for each.

Input — lang (en|fr).

list_recommendations

Guidance for organisations and the public. Public items include explainerSlug when a matching /learn guide exists.

Input — audience (organizations | public), lang (en|fr).

Resources

  • cyberwatch://conventions

    How to use the numbers: unknown is not zero, disputed scope stays disputed, no inferred attribution.

  • cyberwatch://incident/{id}

    Full localized record for one incident, same payload as get_incident.

Prompts

  • explain_incident

    Ask the model to explain one record without inventing counts or attackers.

    Input — id, lang (en|fr).

  • brief_the_dossier

    A short briefing of the curated list, with the data rules stated first.

    Input — lang (en|fr).

WebMCP on this site

When a browser agent is already on a page, these tools run in the tab: they can filter the timeline, open a record, and read the dataset that was prerendered into the page. No extra server call.

How to enable it

WebMCP is a proposed web standard. Chrome exposes it behind a flag (and an origin trial). A WebMCP bridge or the Model Context Tool Inspector extension can also see tools registered on this page.

In Chrome 146+, open chrome://flags/#enable-webmcp-testing, set it to Enabled, and relaunch. Open this site, then inspect navigator.modelContext (or document.modelContext) in DevTools, or use the inspector extension.

chrome://flags/#enable-webmcp-testing

Model Context Tool Inspector (Chrome Web Store) lists the tools, lets you call them, and can chat against them.

If you use a WebMCP bridge (for example Cursor talking to the open tab), pin this origin and the page tools show up as web.* tools.

Page tools

get_page_context

Where the reader is: path, locale, page kind, and the open incident id if any.

list_incidents

Search the dataset already loaded in this page. Unknown affected stays null.

Input — q, kind, severity, status, sector, year, limit, lang.

get_incident

One record from the in-page dataset, in the current (or requested) language.

Input — id (required), lang.

get_summary

National ANSSI/CNIL figures and curated counts from the in-page dataset.

open_incident

Navigate this tab to the incident article.

Input — id (required).

open_page

Navigate this tab to a site section (home, incidents, guidance, numbers, docs, learn, quiz).

Input — page: home | incidents | guidance | numbers | docs | learn | quiz.

filter_timeline

Apply timeline filters and open /incidents (or /fr/incidents) with a shareable query string.

Input — q, kind, severity, status, sector, year.

reset_filters

Clear timeline filters and open the full incident list.

Files

Machine index

The same catalogue as JSON, with live example URLs.

/api

Raw dataset

The complete source file this site and the API are built from.

/data/france-cyberwatch-data.json