# Check a French company for insolvency: its BODACC timeline > One paid call returns a French company's BODACC timeline — insolvency judgments, deregistrations, business sales — with factual flags, and a flag it refuses to guess. Published 2026-08-11 · Updated 2026-08-15 · HTML version: https://invoket.com/blog/should-an-agent-extend-credit-to-this-company --- An agent about to invoice a customer on 30-day terms, release a first order or onboard a supplier is making a credit decision. The question it can actually answer from an official source is narrow and useful: **has anything been published in the French legal gazette about this company?** [`GET /company/events`](/docs/api/company-events) answers it by SIREN — a dated timeline of insolvency judgments, deregistrations and business sales drawn from the **BODACC**, plus three flags derived mechanically from that timeline. No account, no key, no network call at request time. Two properties decide whether an integration reads it correctly. **An empty list is a paid answer** — the most common one, in fact — and the insolvency flag is **nullable**, because when the published label does not qualify what happened, the service says so instead of guessing. Everything below was captured against production on 11 August 2026, against the BODACC snapshot dated **2026-08-09**. The volume behind the question is not theoretical: INSEE, republishing Banque de France data, counted **6,139 business failures in May 2026** alone ([Insee, *Défaillances d'entreprises*](https://www.insee.fr/fr/statistiques/2015359), published 21 July 2026). Roughly six thousand counterparties a month stop being counterparties. ## The timeline, captured `GET /company/events?siren=321875205&family=insolvency` — a retail chain whose receivership was converted into judicial liquidation: ```json { "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…" }, { "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…" }, { "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…" } ], "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-08-09" } } }, "provenance": { "source": "dila-bodacc", "fetched_at": "2026-08-11T15:31:00Z", "freshness": { "kind": "snapshot", "as_of": "2026-08-09T00:00:00Z" } } } ``` Every announcement carries its **gazette issue, notice number and court** — the coordinates a human can use to pull the original notice. The endpoint serves the published fact, not a summary of it. Note the relationship between the view and the flags. `family=insolvency` narrowed `events` to three, and `total: 3` honestly counts *that view* — but this SIREN has **36 announcements** in full, and `latest_event` points at one of the 33 not shown: a business sale gazetted in February 2024. **The filter changes what the agent looks at, never the facts the flags are derived from.** An agent that filters and then reads `flags` is reading the complete timeline either way. ## Flags are facts, not a score Three flags, each a mechanical derivation over the complete timeline: | Flag | What it states | |------|----------------| | `has_open_insolvency_announcement` | `true` / `false` / **`null`** — read on the most recent insolvency announcement that *qualifies* the state of the proceeding | | `is_deregistered` | A deregistration has been published in the gazette | | `latest_event` | `{ family, date }` of the announcement with the most recent **effect** date, across all families; `null` when the timeline is empty | `true` is a fact about the gazette, not a solvency opinion. There is no score, no rating, no probability of default — those would be an inference wearing an official register's authority, which is the line this endpoint does not cross. ### The third state exists because the labels do not always answer `GET /company/events?siren=662042449&family=insolvency` returns one announcement, filed under the gazette's collective-proceedings rubric: ```json "events": [ { "family": "insolvency", "nature": "Autre jugement prononçant", "effective_on": "2026-04-07", "published_on": "2026-04-16", "court": "Greffe du Tribunal de Commerce de Draguignan", "text": "Jugement d'homologation de l'accord intervenu dans la procédure de conciliation…" } ], "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-08-09" }, "insolvency_flag": "undetermined_latest_notice" } ``` The SIREN is `662042449` — BNP Paribas. Under a default rule of "anything filed under the insolvency rubric that is not a closure is an opening", a systemically important bank would have been flagged as being in collective proceedings. What the text actually shows is the **homologation of a conciliation agreement**: a preventive, amicable procedure that opens no collective proceeding, and arguably a resolution signal rather than a distress one. So the derivation runs off an **explicit, closed table** of gazette labels — one list of closures, one list of collective acts, everything else *undetermined* — and the flag comes back `null` with `coverage.insolvency_flag` naming the reason. `"Autre jugement prononçant"` is the catch-all DILA files under when nothing else fits. Two consequences for the code that consumes this: - **`if (flags.has_open_insolvency_announcement)` reads `null` as "nothing to report"** — precisely the reading the third state exists to prevent. Branch on the three states explicitly. - **A non-qualifying announcement does not extinguish the ones before it.** In the SAN MARINA capture, the most recent insolvency notice is a registry filing that qualifies nothing; the derivation walks back to the conversion judgment underneath, and the flag stays `true`. The refusal is to over-qualify, never to hide: the announcement is served in full — `nature`, court, dates, `text` — and the agent decides whether a human should look. ## The most common answer is an empty list, and it is billed To measure what an agent actually gets, we queried **1,200 random checksum-valid SIRENs** against the production store on 11 August 2026: | Outcome | Count | |---------|-------| | `404` — SIREN unknown to the Sirene register (**not billed**) | 796 | | `200` — legal unit exists, **no announcement** over the window | 331 | | `200` — legal unit exists, **at least one announcement** | 73 | So of the 404 real legal units, **82 % had a completely clean gazette record**. That is the answer the agent asked for, and it is only meaningful because `coverage.window { from, to }` is served on **every** response, empty or not: "nothing known between 2008-01-01 and 2026-08-09" is a statement; "nothing found" is not. Among the 73 with a record, the families were **61 deregistrations, 33 insolvency announcements and 23 sales**; 59 companies carried `is_deregistered: true` and 3 an open insolvency announcement. Deregistration — the counterparty no longer being on the register at all — is the single most frequent thing the gazette will tell you about a random French company. One of them, `811791888`: ```json { "company": { "name": null, "natural_person": null, "status": "ceased" }, "events": [ { "family": "deregistration", "nature": "Radiation", "effective_on": "2024-02-27", "published_on": "2024-03-05", "court": "Greffe du Tribunal de Commerce de Montpellier", "text": null }, { "family": "deregistration", "nature": "Radiation", "effective_on": "2023-06-21", "published_on": "2023-06-25", "court": "Greffe du Tribunal de Commerce de Montpellier", "text": null }, { "family": "deregistration", "nature": null, "effective_on": null, "published_on": "2018-07-25", "court": "GREFFE DU TRIBUNAL DE COMMERCE DE MONTPELLIER", "text": null } ], "total": 3, "flags": { "has_open_insolvency_announcement": false, "is_deregistered": true, "latest_event": { "family": "deregistration", "date": "2024-02-27" } } } ``` Two things to read here. The Sirene echo says `status: "ceased"`, agreeing with the gazette — a cross-check available in the same payload, without a second call. And the 2018 announcement carries `nature: null` and `effective_on: null`, because **the publication itself states neither**. Across the 117 announcements in the sample, **49 carried no `nature` at all**. A null there is the gazette's silence, faithfully transmitted; the alternative — inferring a label from the surrounding fields — would be a home-made classifier served as an official fact. Large timelines are capped, honestly. `GET /company/events?siren=652014051` (Carrefour) returns `total: 117` with `truncated: true`: the view is limited to the **100 most recent** announcements, and `total` keeps the count from before the cap rather than reporting what fits in the payload. ## "Unknown to the register" is not "known, without announcements" These are different answers, and only one of them is billed. A well-formed SIREN that INSEE never assigned is a **`404 NOT_FOUND`, unbilled** — the existence check runs against Sirene *before* the timeline is read, so a nonexistent key never reaches the billing path: ``` GET /company/events?siren=000000000 → 404 { "code": "NOT_FOUND", "error": "siren not found in the Sirene register" } GET /company/events?siren=123456782 → 404 { "code": "NOT_FOUND", … } GET /company/events?siren=321875206 → 400 { "code": "INVALID_CHECKSUM", "error": "siren failed its checksum (Luhn): likely a typo" } ``` The check matters more than it looks. `000000000` passes the Luhn test — its digits sum to zero — and it is the filler the BODACC export carries on lines whose SIREN field was empty, which makes it one of the most frequent "SIRENs" in the raw gazette, with hundreds of unrelated announcements attached to it. A misconfigured ERP, a truncated CSV or an empty spreadsheet cell produces exactly that input. Billing a timeline for it would return announcements that are individually real and collectively meaningless, with affirmative flags on top. `123456782` — the SIREN used as an example in manuals — is the same story. And the boundary stays narrow in the other direction: if the Sirene stock itself cannot be consulted, the timeline is served anyway with `coverage.identity: "sirene_dataset_unavailable"`. **Ignorance is never turned into a 404.** ## The blind spot to plan for: the gazette publishes late This is the honest limit of the endpoint, and it is measurable. Across the sampled announcements that carry both dates, the gap between the **effect date** and the **publication date** had a **median of 16 days**, a 90th percentile of **79 days**, and a maximum of **777 days**. An announcement enters the store only once DILA has published it. So "no insolvency announcement today" means *nothing has been gazetted yet* — a judgment handed down two weeks ago will typically not be there. For a credit decision, that is a bounded, declared blind spot rather than a hidden one, and it is the reason `effective_on` and `published_on` are two separate fields instead of one convenient "date". The same lag is why `latest_event` ranks by **effect** date and not by publication: in the SAN MARINA timeline, the most recently *published* announcement (May 2024) carries an effect date of **May 2017** — a sale gazetted seven years after the fact. Ranking by publication would have made a 2017 event look like the company's latest news. Three more scope limits worth stating plainly: - **No score, no prediction.** The flags are derived facts. Nothing here rates a company or forecasts a default. - **BODACC only, by SIREN.** No search by name, no SIRET input — announcements are published per legal unit. The history ingested starts in **2008**. - **A snapshot, honestly dated.** `freshness.kind` is `snapshot` and `as_of` carries the extraction date. A missed refresh means the last good snapshot keeps being served under its true date — **staleness is never turned into a 5xx**. ## Wiring it up 1. **Branch on three states, not two.** `true`, `false` and `null` for `has_open_insolvency_announcement`. Treat `null` as "a human reads the `text`", never as "clear". 2. **Read `is_deregistered` first for onboarding.** It is the most frequent finding, and a deregistered counterparty is not one you invoice. 3. **Store `provenance.freshness.as_of` with the verdict.** "On what basis did the agent extend credit on 11 August 2026" is answerable only if the snapshot date sits beside the decision. 4. **Keep the two dates apart.** Age your decision on `effective_on`, plan for the publication lag on `published_on`. 5. **Distinguish 400 from 404.** A checksum failure means *fix the input*; a `NOT_FOUND` means *this key names no company* — stop, do not retry. 6. **Discover, then pay.** The `402` → pay → replay cycle is walked with runnable snippets in the [Quickstart](/docs/quickstart), and the machine discovery surfaces are described in [For agents](/docs/for-agents). ## Where it sits in a counterparty check The gazette answers *what has legally happened*. It does not answer *who this is* — that is [`/company/resolve`](/docs/api/company-resolve), whose Sirene identity echo is already embedded in every events response — nor *can I invoice them across borders* ([`/company/vat`](/docs/api/company-vat)), *can they receive the e-invoice* ([`/company/peppol`](/docs/api/company-peppol)), or *who controls them* ([`/company/ownership`](/docs/api/company-ownership)). If the agent wants all of it in a single settlement, [`GET /company/report`](/docs/api/company-report) bundles the blocks by SIREN with provenance per block, and [`/preflight/supplier`](/docs/api/preflight-supplier) turns that bundle into a one-call onboarding verdict. Legal health is also not sanctions screening: that is [`/screen/entity`](/docs/api/screen-entity) for a name and [`/iban/screen`](/docs/api/iban-screen) for the account you are about to pay. A SIREN read off an inbound invoice comes from [`/invoice/read`](/docs/api/invoice-read). Every amount and every accepted settlement rail lives in the gateway's [`/catalog`](https://api.invoket.com/catalog), which is the single source of truth — **no price appears in this article**. Announcement data is derived from the **BODACC**, published by DILA under the **Licence Ouverte / Open Licence (Etalab 2.0)**; the identity echo comes from the **INSEE Sirene** register under the same licence. The captures and the sample above are **snapshot-dated illustrations**, not a contract: a new gazette issue moves `latest_event` at any refresh. Only the rules are guaranteed — one SIREN per call, an empty timeline served as a paid answer over a stated window, a flag left `null` rather than guessed, and a key the register has never seen never billed. The full parameter and field contract is on the [`GET /company/events` documentation](/docs/api/company-events).