GET /company/events

Returns the official BODACC legal announcements for a French company, looked up by SIREN: insolvency proceedings (safeguard, receivership, judicial liquidation), deregistrations, and business sales, as a dated timeline plus factual flags derived mechanically from it. The answer is served from a local store built from the official BODACC gazette published by DILA (history since 2008, 8.5M+ announcements), so a lookup resolves in milliseconds with no network call and no secret at request time.

Use it as the legal-health pre-action check before an agent invoices, extends credit to, or onboards a French counterparty - “is this company in liquidation?”, “has it been deregistered?”, “did it just sell its business?”. It pairs naturally with GET /company/resolve: resolve answers who the company is, events answers what has legally happened to it. See the live /catalog for the authoritative endpoint listing and price.

x402 golden rule: the agent pays for the answer to its question. A SIREN known to the Sirene register with no announcements at all is a successful answer -> 200 billed, empty list - “no known announcements” is precisely what a clean counterparty looks like (see the trust contract below). Requests the service cannot answer - a malformed SIREN, a failed checksum, an unknown family, or a well-formed SIREN that does not exist in the Sirene register (see the one 404) - leave the 200 range and are not billed.

Parameters

ParameterTypeRequiredDescription
sirenstringyes9-digit legal-unit identifier (Luhn-checked); its existence is checked in the Sirene register first (unknown -> 404, not billed)
familystringnoFilter the view: insolvency, deregistration or sale (default: all)
GET /company/events?siren=321875205&family=insolvency

siren is validated in two steps: strict form (9 digits), then the Luhn check digit (no SIREN is exempt - the La Poste SIREN 356000000 passes the standard check). A well-formed SIREN that fails its checksum is a 400 INVALID_CHECKSUM, distinct from any “not found”: it is almost certainly a typo, and the agent should fix the input.

Lookup is by SIREN only: BODACC announcements are published per legal unit, so there is no SIRET input, and no search by name in v1 - the endpoint serves agents that already hold an identifier (from an invoice, a contract, a /company/resolve answer).

The family filter changes what the agent looks at, never the facts: events, total and truncated describe the filtered view, while the flags are always derived from the company’s complete timeline. An unknown family value is a 400 INVALID_INPUT.

200 response - UnifiedResponse

{
  "data": { ... },
  "provenance": {
    "source": "dila-bodacc",
    "fetched_at": "2026-07-12T14:50:57Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-12T00:00:00Z" }
  }
}
  • provenance.source: dila-bodacc, the served BODACC store.
  • freshness.kind: snapshot; as_of is the extraction date of the BODACC dataset that backed the answer.

Fields of data

FieldTypeDescription
sirenstringThe queried SIREN, echoed
companyobjectMinimal Sirene identity echo: { name, natural_person { last_name, first_name }, status }; null only when the Sirene stock itself could not be read - see coverage.identity (a SIREN unknown to the register is a 404, not an echo-less 200)
eventsarrayAnnouncements, most recent first, in the view selected by family=
totalintHonest count of announcements in the view, before the payload cap
truncatedbooltrue when the view was capped to the 100 most recent announcements
flagsobjectFactual flags derived from the complete timeline - see below
coverageobjectwindow { from, to } - the ingested history window; identity ("sirene_dataset_unavailable") when the Sirene stock could not be consulted; insolvency_flag when the insolvency flag could not be determined

Fields of an event

FieldTypeDescription
familystringinsolvency (collective proceedings: safeguard, receivership, liquidation), deregistration (removals from the register) or sale (business sales and transfers)
naturestringOriginal DILA label (e.g. “Jugement de conversion en liquidation judiciaire”); null when the publication does not state one - never invented
effective_onstringJudgment/effect date (YYYY-MM-DD) when distinct from publication; null otherwise
published_onstringBODACC publication date (YYYY-MM-DD)
gazette_issuestringBODACC gazette issue carrying the announcement
notice_numberintNotice number within the gazette issue
courtstringCourt or registry that issued the announcement; null when not stated
textstringShort original announcement text (truncated at ingestion)

The view is capped to the 100 most recent announcements: when a company has more, truncated is true and total keeps the honest count before the cap.

Flags: facts, not a score

The flags block states BODACC facts derived mechanically from the company’s complete timeline - never the filtered view, and never an opinion:

FlagTypeDerivation
has_open_insolvency_announcementbool | nullThree states, read on the most recent insolvency announcement that qualifies the state of the proceeding - see below
is_deregisteredboolA deregistration has been published in the BODACC
latest_eventobject{ family, date } of the announcement with the most recent effect date across all families; date is the effect date when known, else publication; ties on effect date are broken by the most recent publication; null when the timeline is empty

has_open_insolvency_announcement is nullable

ValueMeaning
trueThe most recent qualifying insolvency announcement is an act of a collective proceeding (opening, conversion, extension, plan ordered, modified or resolved)
falseIt is a closure (or an end of proceeding, or a retracted opening judgment) - or the SIREN has no insolvency announcement at all over the window
nullAnnouncements of this family exist, but none of them qualifies the state of the proceeding; coverage.insolvency_flag says so ("undetermined_latest_notice")

true is a fact about the gazette, not a solvency opinion or a risk score - the service does not rate companies. null is not a failure and not a false in disguise: it is the service refusing to answer a question the published labels do not answer. Agents that branch on this flag must treat it as nullable - if (flags.has_open_insolvency_announcement) silently reads null as “nothing to report”, which is exactly the reading this third state exists to prevent.

Two rules make the derivation predictable:

  • The classification is an explicit, closed table of DILA labels - one list of closures, one list of collective acts, and everything else is undetermined. There is no default rule “anything that is not a closure is an opening”. The DILA collective family is a gazette rubric, not a list of proceeding acts: it also carries registry filings, sanctions aimed at the director, and a catch-all label (“Autre jugement prononçant”, 33,748 announcements) that says nothing about the underlying act.
  • A non-qualifying announcement does not extinguish the previous ones. The derivation walks back to the most recent announcement that does qualify; the flag only becomes null when no announcement in the timeline qualifies. A registry filing published after a judicial liquidation therefore leaves the flag at true (see the SAN MARINA example below).

What the service deliberately does not do: reclassify the catch-all by reading the announcement text. Matching prose for “conciliation” or “homologation” would be a home-made classifier over free text served as an official fact - the wrong side of the line this service draws. The announcement is served in full instead, text included, and the agent reads it.

Example - insolvency view (SAN MARINA)

GET /company/events?siren=321875205&family=insolvency, against the 2026-07-26 BODACC snapshot - receivership opened in 2022, converted to judicial liquidation in 2023:

{
  "data": {
    "siren": "321875205",
    "company": {
      "name": "SAN MARINA",
      "natural_person": null,
      "status": "active"
    },
    "events": [
      {
        "family": "insolvency",
        "nature": "Dépôt de l'état des créances",
        "effective_on": "2023-08-25",
        "published_on": "2023-09-05",
        "gazette_issue": "20230170",
        "notice_number": 2594,
        "court": "Greffe du Tribunal de Commerce de Marseille",
        "text": "L'état des créances est déposé au greffe où tout intéressé peut présenter réclamation devant le juge-commissaire dans le délai d'un mois à compter de la présente publication."
      },
      {
        "family": "insolvency",
        "nature": "Jugement de conversion en liquidation judiciaire",
        "effective_on": "2023-02-20",
        "published_on": "2023-02-23",
        "gazette_issue": "20230038",
        "notice_number": 1758,
        "court": "Greffe du Tribunal de Commerce de Marseille",
        "text": "Jugement prononçant la liquidation judiciaire désignant liquidateur SCP J.P Louis & A.Lageat, Mandat Conduit Par Me J.P Louis 30 Rue Cours Lieutaud 13001 Marseille et SAS LES MANDATAIRES mission conduite par Me Vincent DE CARRIERE 50 Rue Sylvabelle 13006 MARSEILLE."
      },
      {
        "family": "insolvency",
        "nature": "Jugement d'ouverture d'une procédure de redressement judiciaire",
        "effective_on": "2022-09-22",
        "published_on": "2022-09-27",
        "gazette_issue": "20220187",
        "notice_number": 3494,
        "court": "Greffe du Tribunal de Commerce de Marseille",
        "text": "Jugement prononçant l'ouverture d'une procédure de redressement judiciaire, date de cessation des paiements le 25 août 2022 désignant administrateur SCP Avazeri-Bonetto, Mission Conduite Par Me F.Avazeri 23/29 Rue Haxo 13001 Marseille et SELARL GILLIBERT & ASSOCIES, mission conduite par Me Vincent GILLIBERT 11 Rue Venture 13001 MARSEILLE avec les pouvoirs : assister le débiteur pour tous les actes de gestion, mandataire judiciaire SCP J.P Louis & A.Lageat, Mandat Conduit Par Me J.P Louis 30 Rue…"
      }
    ],
    "total": 3,
    "truncated": false,
    "flags": {
      "has_open_insolvency_announcement": true,
      "is_deregistered": false,
      "latest_event": { "family": "sale", "date": "2024-02-01" }
    },
    "coverage": {
      "window": { "from": "2008-01-01", "to": "2026-07-26" }
    }
  },
  "provenance": {
    "source": "dila-bodacc",
    "fetched_at": "2026-07-26T18:43:01Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-26T00:00:00Z" }
  }
}

Note how the flags illustrate the derivation rule: they read the complete timeline, not the requested view. total: 3 counts the requested view, but this SIREN has 36 announcements in all - and latest_event points at one of the 33 you cannot see here: a business sale (“Achat d’un établissement secondaire ou complémentaire par une personne morale”, Aix-en-Provence registry), gazetted on 2024-02-01. That announcement carries no published effect date, so date falls back to its publication, exactly as the rule states. Meanwhile the most recently published announcement of the timeline (2024-05-19) carries an effect date of 2017-05-22 - a sale gazetted seven years after it took effect - and is not retained: the BODACC routinely publishes an announcement, sales especially, long after the fact, which is precisely the trap this field avoids. has_open_insolvency_announcement is true, and note why: the most recent insolvency announcement is a registry filing (“Dépôt de l’état des créances”) whose label qualifies nothing, so the derivation walks back to the one below it - the judgment converting the receivership into a judicial liquidation. A non-qualifying announcement does not extinguish the fact published before it.

These values are what the 2026-07-26 snapshot returns. An example served from a snapshot ages with the store: a newly gazetted announcement can move latest_event at any refresh, so read the numbers as an illustration for the as_of shown - only the derivation rule is a contract.

Example - the flag the service refuses to guess (BNP PARIBAS)

GET /company/events?siren=662042449&family=insolvency, against the same 2026-07-26 BODACC snapshot. One insolvency announcement, and its DILA label is the catch-all:

{
  "data": {
    "siren": "662042449",
    "company": { "name": "BNP PARIBAS", "natural_person": null, "status": "active" },
    "events": [
      {
        "family": "insolvency",
        "nature": "Autre jugement prononçant",
        "effective_on": "2026-04-07",
        "published_on": "2026-04-16",
        "gazette_issue": "20260073",
        "notice_number": 2568,
        "court": "Greffe du Tribunal de Commerce de Draguignan",
        "text": "Jugement d'homologation de l'accord intervenu dans la procédure de conciliation. Le jugement est déposé au greffe où tout intéressé peut en prendre connaissance. ."
      }
    ],
    "total": 1,
    "truncated": false,
    "flags": {
      "has_open_insolvency_announcement": null,
      "is_deregistered": false,
      "latest_event": { "family": "insolvency", "date": "2026-04-07" }
    },
    "coverage": {
      "window": { "from": "2008-01-01", "to": "2026-07-26" },
      "insolvency_flag": "undetermined_latest_notice"
    }
  },
  "provenance": {
    "source": "dila-bodacc",
    "fetched_at": "2026-08-01T17:04:52Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-26T00:00:00Z" }
  }
}

The announcement is real, it is French BNP Paribas, and it is filed under the gazette’s insolvency rubric - but “Autre jugement prononçant” is the label DILA uses for everything it does not file elsewhere. Reading it as an opening would have declared a systemically important bank in collective proceedings on what the text shows to be the homologation of a conciliation agreement: a preventive, amicable procedure that opens no collective proceeding at all, and arguably a resolution signal rather than a distress one.

So the flag is null, coverage.insolvency_flag names the reason, and the announcement is still served with its nature, its court, its dates and its text. The refusal is to over-qualify a fact, never to hide it: everything needed to read the situation is in the payload, and the agent decides whether it wants a human to look.

An empty list is a paid answer

The BODACC does not “know” SIRENs - it knows announcements. A SIREN that exists in the Sirene register and carries no announcement is therefore a 200, billed, with events: [] - never a 404. The absence of announcements is the answer the agent asked for, and it is only meaningful over a stated window: coverage.window { from, to } carries the history actually ingested (since 2008) and is served on every response, empty or not.

A real empty answer, captured against the production store on 2026-07-12 (an active sole trader with no BODACC announcement):

{
  "data": {
    "siren": "902120096",
    "company": {
      "name": null,
      "natural_person": { "last_name": "DEMAILLY", "first_name": "LOLITA" },
      "status": "active"
    },
    "events": [],
    "total": 0,
    "truncated": false,
    "flags": {
      "has_open_insolvency_announcement": false,
      "is_deregistered": false,
      "latest_event": null
    },
    "coverage": {
      "window": { "from": "2008-01-01", "to": "2026-07-12" }
    }
  },
  "provenance": {
    "source": "dila-bodacc",
    "fetched_at": "2026-07-12T15:07:29Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-12T00:00:00Z" }
  }
}

The question “does this company have legal announcements?” received its answer: none known over the 2008-01-01 -> 2026-07-12 window. The flags are false and latest_event is null by derivation from the empty timeline, not by default; the Sirene echo (a natural person: name: null, natural_person filled) confirms the SIREN exists in the register.

”Unknown to the register” is not “known, without announcements”

These are two different answers, and only one of them is billed. The empty list above says this company exists and the gazette has nothing on it. A SIREN that was never assigned by INSEE says nothing about any company at all - so it is a 404 NOT_FOUND, not billed, with the very same message as GET /company/resolve:

{ "error": "siren not found in the Sirene register", "code": "NOT_FOUND" }

The existence of an identifier is one single truth across the service: one route cannot answer “unknown” while another bills an answer for the same input. The check runs before the timeline is read, so a nonexistent SIREN never reaches the billing path.

Why this matters more here than it looks: nine digits that pass the Luhn check are not evidence that a company exists, and the failure mode is not exotic. 000000000 passes the checksum (its digits sum to zero) and is the filler value the BODACC export carries on lines whose SIREN field was empty - which makes it the third most frequent SIREN of the store, with hundreds of announcements attached. Billing a 200 for it returned a timeline of announcements that were individually real but belonged to hundreds of unrelated companies across different courts, with affirmative flags on top. The same held for 123456782, the SIREN used as an example in manuals. A misconfigured ERP, a truncated CSV or an empty spreadsheet cell produces exactly these inputs, and the agent had no way to tell the aggregate apart from a genuine company’s history.

Two boundaries keep the rule narrow:

  • A 404 is never invented. The existence check is the identity lookup this route already performed, simply moved ahead of the announcements read - it costs nothing and it adds no new source. If the Sirene stock cannot be consulted at all, the answer is served exactly as before, with coverage.identity = "sirene_dataset_unavailable" stating why the identity echo is missing. Ignorance is never turned into a 404.
  • Existing and clean stays a paid 200. Nothing above narrows the contract of this route: 902120096 (the empty list shown above) still returns a billed 200 with events: [] and its coverage.window. What changed is only the input that names no company.

Sirene identity echo

Each answer carries a minimal company echo from the INSEE Sirene register - legal name (or the natural person’s name for a sole trader) and active/ceased status - so the agent can cross-read the timeline against the company’s identity without a second call. For the full identity card (legal form, NAF activity, workforce, head office), use GET /company/resolve.

On this route the echo is missing in one case only: when the Sirene stock itself cannot be consulted. company is then null, coverage.identity says why ("sirene_dataset_unavailable"), and the BODACC timeline is still served on its own authority - a missing echo is never an error. A SIREN the register does not know is the other case entirely, and it never reaches this point: it is the 404 above.

Freshness

Answers are served from a snapshot of the BODACC dataset, refreshed by delta; freshness.as_of carries the extraction date of the image that backed the answer, and coverage.window.to matches it. If a refresh is missed, the service keeps serving the last good snapshot - honestly dated by as_of - and never turns staleness into a 5xx.

Errors

Only requests the service cannot answer leave the 200 range - none of them are billed. Note the deliberate absence of a 404 for a SIREN the register knows: no announcements is a 200 (see above). The only business 404 is a SIREN the register does not know - there, what is missing is the input, not the answer.

StatuscodeCase
400INVALID_INPUTMalformed SIREN (wrong length, non-digits), or unknown family value (accepted variants listed in the message)
400INVALID_INPUTUnknown query parameter name - the message names it, suggests the nearest expected one when there is a close match, and lists what the route accepts (siren, family). Not charged.
400INVALID_CHECKSUMWell-formed SIREN that fails its Luhn check - a probable typo the agent should fix
400MISSING_PARAMETERNo siren provided
404NOT_FOUNDA well-formed siren= absent from the Sirene register (a legal unit never assigned by INSEE - same message as /company/resolve); also unknown routes. Never for a known SIREN without announcements, and never invented when the Sirene stock cannot be consulted
503DATA_UNAVAILABLEThe BODACC store has not been ingested yet - there is no last good snapshot to serve
500INTERNALInternal error (detail logged, not exposed)
{ "error": "invalid checksum for siren `321875206`", "code": "INVALID_CHECKSUM" }

{ "error": "siren not found in the Sirene register", "code": "NOT_FOUND" }

Attribution

Legal-announcement data is derived from the BODACC (Bulletin officiel des annonces civiles et commerciales), published by DILA under the Licence Ouverte / Open Licence (Etalab). The identity echo comes from the Sirene register published by INSEE, under the same licence.

See also

  • GET /company/resolve - the full official identity card behind the SIREN: legal name, status, legal form, NAF activity, head office.
  • GET /iban/screen - screen a counterparty’s IBAN against the OFAC/EU/UN sanctions lists before paying it.
  • For agents - discovery surfaces, the live /catalog and how settlement works.