Newest first. Every entry names the endpoints it touches and says whether it is additive or breaking.
2026-11-02 — watch a saved segment: the companies that enter it, and those that leave
Additive for the operations, and one change to the feed's items to read before you
deploy. Three new operations, two new MCP tools, a new field on every item of the feed, a
new kind of webhook body and header, and a fourth meaning for 402 quota_exceeded.
PUT /v1/{country}/watchlists/{id}/segments/{segment_id}: a list follows a saved segment (201;200when sent again, which changesinclude_leavers). After each daily update of the segment index, the companies that entered it, and withinclude_leavers: truethose that left it, are delivered through the list's webhooks, digest and feed. Nothing already in the segment is delivered. Not counted as a call. See follow a segment.GET /v1/{country}/watchlists/{id}/segmentsandDELETE /v1/{country}/watchlists/{id}/segments/{segment_id}: what a list follows, with the allowance, and stopping (204, or404if the list does not follow it). Not counted as calls. Each followed segment carriessegment.watch(paused_at,failures,last_failed_at): a segment whose daily update fails three times in a row is paused, and delivers nothing until it is followed again or resumed in the console, from a new baseline.GET /v1/{country}/events: every item now carriestype,eventon the items you already read, andsegment.enteredorsegment.lefton the new ones, which holdsegment_changeinstead ofevent. A reader that assumeseventon every item breaks on the first segment item: branch ontype. Nothing changes for a list that follows no segment. See read watched events.- Webhooks: segment bodies have
typesegment.enteredorsegment.leftand the headerFirmus-Segment-Change-Idinstead ofFirmus-Event-Id. A partially disclosed company, a sole trader of a segment saved without the acknowledgement, or a company no longer in the registry is never sent; its delivery is settledwithheldin the console's delivery log. A company that could not be read in full is not withheld: the delivery stays pending and is retried, and the feed ends its page before it withhas_more: true. - Segment items are kept 90 days; older ones leave the feed.
402 quota_exceedednow also answers a follow beyond the plan's watched segments (1 on Free, 10 on Pro, 100 on Scale), withoutRetry-After.- MCP:
watch_segmentandunwatch_segment;watched_eventsreturns the segment items too. See Use Firmus from Claude.
2026-11-02 — segments: count, sample and export companies by explicit filters
Additive. Five new operations, two new MCP tools, three new fields on a job, a new
error code, and two more meanings for 402 quota_exceeded. No existing answer changes.
POST /v1/{country}/segments: the exact count of the active companies matching explicit filters, four facets and a sample of at most ten, with the filters as interpreted.save: truestores the segment (201). One call. See run a segment.GET /v1/{country}/segments,GET|DELETE /v1/{country}/segments/{id}: the saved segments and the allowance (not a call), a saved segment run again (one call), deleted (not a call).POST /v1/{country}/segments/{id}/export: every company the segment may list, or the firstlimitin SIREN order, as a file job ofoperationsegment_export(202). Partially disclosed units are never listed, and sole traders only with the acknowledgementsole-traders-2026-11-01.GET /v1/{country}/jobs/{id}gainscounts.segment_export,exported_rowsandsegment, allnullon a file job.402 quota_exceedednow also answers a save beyond the plan's saved segments (3 on Free, 100 on Pro, 1 000 on Scale), withoutRetry-After, and an export beyond the month's exported rows (1 000, 25 000, 250 000), with it.503 segment_index_unavailable: a server that cannot count segments yet.- MCP:
segmentandsegment_export. See Use Firmus from Claude.
2026-11-02 — watch named companies: watchlists, webhooks, digests and a feed
Additive. Nine new operations, three new MCP tools, and a third meaning for 402 quota_exceeded. No existing answer changes.
GET|POST /v1/{country}/watchlistsandGET|PATCH|DELETE /v1/{country}/watchlists/{id}: lists of SIRENs and SIRETs to follow, each with its filters (severity_floor,include_kinds,exclude_kinds,include_establishments,include_bulk) and an email digest, daily or weekly. Not counted as calls. See watch companies.GET|POST|DELETE /v1/{country}/watchlists/{id}/watches: the numbers of a list, added up to 1 000 at a time fromnumbersor from a finished file job'sjob_id, each answeredadded,already_watched,not_foundorinvalid. Not counted as calls.GET /v1/{country}/events: the watched events of every list, oldest first, read from a cursor. One call per page.- Webhooks, set up in the console: each watched event
POSTed to your endpoints, signed inFirmus-Signature(t=…,v1=…, HMAC-SHA256), retried for 24 hours, then kept and replayable from the console. A legal notice first recorded without its values is delivered again with the same event id andtrigger: "retyped"once it is readable. 402 quota_exceedednow also answers an add that would take the organization past its watched companies: 5 on Free, 1 000 on Pro, 10 000 on Scale. It carries noRetry-After, unlike the two monthly allowances. See errors.- MCP:
watch,unwatchandwatched_events. See Use Firmus from Claude.
2026-11-01 — the source of each field group, in the API and in the file
Additive, with one changed answer. A new key in sources, two new columns at the end
of a file job that enriches, and partially_disclosed now also says true for a masked head
office.
POST /v1/{country}/enrich: every enriched row'ssourcesgainslegal,{ "registry": "bodacc", "published_on": "2026-06-02" }, when at least onelegalfield was selected, andnullotherwise.published_onis the bulletin date of the latest notice behind the selected legal fields,nullwhen there is none.registry: "insee_sirene"is the source of every value but the events and the legal fields.POST /v1/{country}/enrich: a row'spartially_disclosedis nowtruewhen the company, or its head office when one was read (a head-office field oreventsselected), is partially disclosed, so it says whether something on that row is masked. A disclosed company whose head office is masked used to answerfalseand now answerstrue; no other row's value changes, and thelegalfields still follow the company's status alone. The full card'spartially_disclosedis unchanged and describes the company. See enrich.- File jobs that enrich gain two columns after
firmus_partial, at the end of the line:firmus_partially_disclosed, the row'spartially_disclosedastrueorfalse, andfirmus_sources,insee_sireneorinsee_sirene; bodacc 2026-06-02when a legal field was selected (insee_sirene; bodaccwith no notice behind it). Both are empty on a row that was not enriched. Every earlier column keeps its position, sofirmus_partialis no longer last on these jobs: a program that finds it by counting from the end must count two further. A job submitted before this change keeps the columns it was created with, without the new two. See submit a job. - MCP:
enrichnames the BODACC beside INSEE Sirene in each row's sources.
2026-11-01 — inconclusive job rows, and the timeline of a removed company
Additive, with one changed answer. Two new fields and a new last column; a removed
company's timeline is now served instead of a 404.
- File jobs search a row whose search ran out of time partway once more, with a fresh
budget, before writing it. The result gains a last column,
firmus_partial, always present and after every otherfirmus_column, thelegalones included:trueon a row still not conclusive after that, whosenot_foundorambiguousis worth submitting again.GET /v1/{country}/jobs/{id}gainsprogress.partial_rows, the count of those rows. A program that reads the result by column position from the end must account for the new column. A job submitted before this change keeps the columns it was created with, without the new one. See submit a job. GET /v1/{country}/legal-entities/{registry_number}/eventsgainsremoved,truewhen the legal entity is no longer in the register and its removal is recorded. Its timeline used to answer404 not_foundonce the entity was removed; it now answers200withremoved: true, every event's values withheld and a BODACC event'ssource_refnull. A registry number with no record that the log knows through other events is served the same way, withremoved: false.404now means neither the register nor the log knows the number, and is still answered before the filters are read. See list events.- MCP:
job_statusreports the inconclusive rows, andtimelinesays when a company left the registry.
2026-11-01 — legal notices from the BODACC
Additive. New event kinds, a new source and three new enrich fields; no existing field moves. A client that ignores kinds it does not recognise, as list events asks, needs no change.
GET /v1/{country}/legal-entities/{registry_number}/eventsnow also lists the notices the BODACC published about a company in the trade register, withsource: "bodacc"and the notice's id assource_ref: insolvency proceedings opened, converted, under a plan, closed or reopened, conciliation, personal recovery, a business sold or acquired, removal from the trade register, registration, and every other notice aslegal_entity.legal_notice_published. Each kind has a.correctedand a.cancelledvariant, whosepayload.corrects.notice_idnames the notice it corrects; a court retracting its opening is alegal_entity.insolvency.opened.cancelledthat names none. The list is the raw log: a cancelled event stays beside its cancellation. Every payload carriesbulletin,published_onandurl, except on a company that was partially disclosed or not yet in the register when the notice was read, whose payload is{}, orcorrectsalone.legal_entity.accounts_filedis off by default: the list leaves it out unlesskindnames it. The gazette's history since January 2008 isbulk, so it is listed only withinclude_bulk=true.POST /v1/{country}/enrichhas alegaltheme:collective_proceeding,last_accounts_filedandlast_business_transfer, read from those notices with corrections applied, each with itssourceandnotice_url. The defaultevent_kindsis every legal-entity kind but filed accounts. See enrich.POST /v1/{country}/jobswrites thelegalfields as eight newfirmus_columns when they are selected.- MCP:
timelineandenrichdescribe the new kinds and fields to an assistant.
2026-11-01 — file jobs
Additive. Three new operations and a new error code; nothing existing moves.
POST /v1/{country}/jobstakes a whole CSV — a multipartfile, a JSONcsvstring or a JSONrowsarray — and resolves it, enriches it, or both, in the background. It answers202with a job. The columns are detected from the headers unlessmappingnames them. A job holds up to 1 000 rows and 2 MB on Free, 25 000 rows and 20 MB on Pro, 100 000 rows and 50 MB on Scale, and is checked against the month's agent rows for the whole file before it is admitted. See submit a job.GET /v1/{country}/jobs/{id}reads its status, progress and counts per step, and once it has finished givesresult_urland adownload_urlthat needs no key.GET /v1/{country}/jobs/{id}/resultdownloads the result: your file, with its delimiter and byte-order mark, and thefirmus_columns appended. Neither read is counted as a call. A job and its files are kept seven days.409 job_not_finishedis new, answered by the result while the job isqueuedorrunning. Afailedjob serves the rows it answered before it stopped.invalid_request,quota_exceeded,public_key_forbiddenandnot_foundnow also answer/jobs.- MCP:
submit_jobandjob_statusgive an assistant the same jobs, andresolveandenrichnow send it to them for a whole file.
2026-10-29 — a timeline of each legal entity
Additive. A new operation; nothing existing moves.
GET /v1/{country}/legal-entities/{registry_number}/eventslists what the register recorded about a legal entity and its establishments — ceased, renamed, head office moved, an establishment opened or closed — newest first, paged with a cursor, filterable by kind, severity and date. See list events.invalid_requestnow also answers an/eventsfilter or cursor that cannot be read.
2026-08-13 — a discovery root, and documentation_url on errors
Additive. Neither change removes or renames anything, and no existing field moves.
GET /v1answers a discovery document: what this API is, where the contract and this documentation live, how to authenticate, what the free tier allows, and every endpoint with its summary and its parameters. It needs no key, for the reasonGET /v1/openapidoes not. Every field of it is derived from the contract, so the two cannot disagree.documentation_urljoins the error object for every code this page explains, pointing at the section that explains it. It is absent from any other refusal, so treat it as optional — and keep branching oncode, which is the field that carries the meaning.
v1
The initial contract. Four operations, all GET:
GET /v1/{country}/legal-entities/{registry_number}GET /v1/{country}/legal-entities/{registry_number}/establishmentsGET /v1/{country}/establishments/{establishment_number}GET /v1/{country}/suggest
plus GET /v1/openapi, which serves the contract itself and needs no key. GET /v1
arrived later; see the entry above.
France (fr) is the country served. See sources & coverage.
How changes are made
Additive changes happen inside v1, without notice. A new field on a response, a new
optional query parameter, a new country code, a new value in a vocabulary like kind.
Write clients that ignore fields they do not recognise, and none of these will reach you
as a problem.
Breaking changes get a new version in the path. Removing or renaming a field, changing
the type or meaning of one, removing an endpoint, or making an optional parameter
required. v1 does not change under you: it becomes v2 beside it.
Two things sit in between, and both are announced here rather than assumed:
- Data changes. A register changing what it publishes is not an API change, but it can be indistinguishable from one — a nomenclature moving to a new revision is the clearest example. Those get an entry naming the field and the effective date.
- New status codes. A new refusal reason arrives as a new
codein the same error object. Handle an unrecognisedcodeby its HTTP status, and this stays additive.
The contract at GET /v1/openapi is the machine-readable version of
all of this.