Research · Audit

Audit Firm Client Book — Which Public Companies Does This Auditor Sign, and Who Just Left?

Builds an audit firm's public-company client book from cover-page facts, verifies each member against its own filing, files the departures as a cited dossier.

When a registered public accounting firm is sanctioned, barred, merged, or simply starts shedding clients, every company it signs is affected in the same few weeks — and the list of who those companies are is not published anywhere. This audit builds it from the SEC record, checks each member against its own cover page, and files a cited dossier.

Why this works with edgar.tools MCP

Every annual report carries the audit firm's name, its PCAOB registration number, and its city as structured cover-page facts. edgar.tools extracts them from every 10-K, 20-F, and 40-F, resolves the filer-typed firm ids against a canonical table of 582 firms, and keeps a register of 39,699 filings back to October 2021. That makes "every issuer this firm signs" one indexed query instead of a crawl — and, because the register is historical while the screen is current, it makes "who left" a subtraction rather than a guess.

How to use

Give the model a firm — a name ("BF Borgers", "Marcum", "KPMG") or a PCAOB id if you have one — and let it run the procedure end to end. It finishes by filing a dossier you can share.

Audit the public-company client book of [FIRM].
The universe pass (screen_companies) is an Analyst capability. On Free or Pro the model will tell you so and fall back to the per-company path: company_brief returns any single company's auditor, PCAOB id, city, and stated prior auditor on every plan, including free accounts. You lose the client book, not the facts.
Prompt — paste this into Claude, ChatGPT, or Gemini

The audit (v0)

You are auditing one registered public accounting firm: which public companies does it currently sign, how much of its book has already moved, and is the book you just pulled even real? Follow the steps in order. Step labels are fixed (R resolve · G gather · A analyze · W widen · D deliver). The deliverable is a create_report dossier, not a chat summary.

Non-negotiables

  • Every claim quotes a filing or states a verifiable structural fact. Conclusions are the questions the record raises.
  • Never characterize the firm or the people at it. The record supports "these filings name a different firm than they did last year." It does not support "the firm is losing clients," "was dropped," or any claim about quality, conduct, or competence. A sanction is reportable only by citing the sanctioning document, never inferred from the client book moving.
  • Untagged is never "no auditor." A company with no tagged firm has not filed a tagged annual report in the window. Report it beside the buckets, never inside one.

Procedure

R1 — Resolve the firm to PCAOB ids. Call screen_companies({ plan: { clauses: [{ route: "axes", text: "audited by <firm>", axes: { auditor: "<firm>" } }] }, limit: 50 }). Read plan.auditors[0] before anything else: matched tells you whether the name hit exactly or by prefix, and firms[] names every registrant the screen counted, with via_alias: true on ids pulled in as typo-aliases of a matched id. One brand routinely spans many registrants — seven PCAOB ids file as "Ernst & Young LLP," and "KPMG" matches 34 registered firms — so decide here which registrants the question actually means, and say so in the dossier. If you already know the id, use axes: { auditor_firm_id: "<id>" } instead and skip the ambiguity.

G1 — Pull the client book. The same call returns it: rows[] are the companies currently tagged to those ids, total is the count, and audit_concentration.firms[] gives the per-registrant split with untagged beside it. Page with offset if total exceeds your limit. Record both counts per firm: plan.auditors[].firms[].register_clients and audit_concentration.firms[].companies.

A1 — Read the two counts against each other. They are different populations and the gap is the finding, not an error. register_clients counts a firm's filings across the whole register since 2021; companies counts what is tagged to it right now. A firm with hundreds in the register and a handful today has a client base that has moved on — that subtraction is the headline of this audit. State both numbers and what each covers; never present one as the other.

G2 — Verify each member against its own cover page. For every company in the book (or a stated sample, if the book is large), call company_brief({ company: <cik> }) and read governance: auditor (the name the filer typed), auditor_firm_id, auditor_location, auditor_as_of, and source_accession. Do not skip this step on the assumption that the id is right. The screen groups on the id, and the id is filer-supplied.

A2 — Coherence: name against id, same record. Flag every member whose governance.auditor name is not the canonical name for the id it carries. That member is evidence of a mis-tag, not evidence that the firm signs it — move it out of the client book and into its own finding, quoting the filer's own name and accession. This is the step that decides whether the book you just built is real.

W1 — Stated departures (Analyst plan; per company). company_brief reports a prior auditor only when the filer itself stated a change on its cover page. Walk the members flagged in A2 plus any company you already know left, and capture the stated prior firm and the fiscal year. Stop when you have covered the flagged set — there is no reverse index from a firm to everyone who left it, so this lane is bounded by the companies you can name, and the dossier must say that rather than implying a complete departure list.

D1 — Write the dossier. Sections, unnumbered, in this order: Assessment (one bolded central claim, two or three pillars pointing into the evidence, a posture verdict) · Which registrants this is (the R1 echo as a table: id, canonical name, register_clients, current companies, via_alias) · The client book (the member table + the untagged count, with the A2 exclusions removed and counted) · Register versus today (the A1 subtraction, stated as two populations) · Mis-tagged members (each A2 finding, one verbatim callout per filer-typed name) · Stated departures (W1, with the no-reverse-index limit stated) · What would make this benign (mandatory — always state that a cover-page id is filer-supplied and uncorrected, that a rename or merger moves names without moving auditors, and that an untagged filer is untagged, not unaudited) · Open questions · Sources, plus a citationNote carrying the method chain.

D2 — File. Call create_report({ kind: "dossier", title, blocks }) now, in this turn — not as an offer. Every block carries sources: [] at minimum, headings and the citationNote included; citationNote.method is an array of strings; do not number headings (the renderer letters sections); one callout per verbatim quote, never a paraphrase. External sources are https://www.sec.gov/…-index.htm URLs.

Priors — what each observation is worth

P1 — The gap between register and current counts is the story, and it is not an error. Measured on BF Borgers (PCAOB 5041) on 2026-09-14: 488 clients in the register, one company currently tagged. A firm whose register count dwarfs its current count has a book that has moved; a firm where the two track has a stable one.

P2 — After a firm stops signing, its PCAOB id keeps collecting rows, and they are contamination. Every row tagged to firm 5041 for a fiscal period after May 2024 names a different firm on the same cover page: four name "Bush and Associates CPA LLC," one "Bush & Associates CPA LLC," one "MICHAEL GILLESPIE & ASSOCIATES, PLLC." Zero name BF Borgers. Ex-clients carry the old id forward onto their new auditor. So the single company a naive screen returns for that firm today is not its client — it is a filer that typed a stale id. Always run G2. Note that this pattern defeats a frequency heuristic: at four rows it is too common to look like a typo.

P3 — One name is not one registrant. The PCAOB registers each national member firm separately under the same brand. A company moving from KPMG's Israeli member firm to KPMG's US firm is a genuine change of registrant and is not the company leaving KPMG. Compare ids; treat names as labels.

P4 — A rename or a merger is not an auditor change. When Marcum's practice moved to CBIZ, thousands of filers began naming a different firm with no change in who audits them. edgar.tools suppresses known firm-network pairs, but a pair it does not yet know will read as a switch. Before reporting a departure, check whether the two firms are the same practice under two names.

P5 — A missing fact is not a negative fact. untagged counts companies with no tagged auditor in the window — filers outside the annual cycle, filers whose latest annual report predates cover-page tagging, filers whose block did not parse. It is not a count of unaudited companies, and it never belongs inside a firm's bucket.

Stopping conditions

  • `TIER_INSUFFICIENT` on `screen_companies` — call account_status({ triggered_by: "tier_denied", denied_tool: "screen_companies" }), tell the user the universe pass needs Analyst and give the link, then run the per-company path with company_brief on whichever companies they can name. Never go quiet, and never substitute a smaller answer without saying you did.
  • `matched: "none"` in the R1 echo — the name resolved to no registrant, so the screen correctly returns zero companies rather than the universe. Re-run with a shorter or differently-spelled name (try the firm's legal name) before reporting that it audits nobody.
  • A book larger than you will verify — G2 is one call per member. Above roughly 25 members, verify a stated sample, say which members you checked, and do not describe the unchecked remainder as confirmed.
Then askRun this on an audit firm in the news.

What the audit finds that a screen cannot

A screen answers "who does this firm sign." This audit answers the two questions that actually move: how much of this firm's book has already left, and is the book I just pulled even real. The second question is not rhetorical — on the fixture, the naive answer was one client and the true answer was zero, and only the per-member cover-page check separated them.

QuestionRatio screen / base LLMThis audit
Who does this firm sign todayNo public list exists; an LLM recalls headlinesOne indexed call over cover-page facts, with the per-registrant split
How much of the book has leftInvisible — one snapshot has no pastThe register count beside the current count, as two named populations
Is a listed client really a clientTaken on trust from the firm idEach member checked against the name on its own cover page
Which registrant is meant"KPMG" treated as one firmEvery id the name resolved to, echoed and counted separately
DeliverableA chat answerA cited dossier object the server validated, shareable as a frozen link

Worked run (2026-09-14): BF Borgers CPA PC, PCAOB 5041 — barred by the SEC in May 2024. The register holds 488 filings for the id; exactly one company is tagged to it today. That one company's own cover page names Bush & Associates CPA LLC, a separate registrant (PCAOB 6797, 80 filings of its own), so the true current book is zero and the screen's single row is a filer carrying a stale id onto its new auditor. Every row tagged to 5041 for a period after the bar names a different firm — four "Bush and Associates CPA LLC," one "Bush & Associates CPA LLC," one "MICHAEL GILLESPIE & ASSOCIATES, PLLC," none BF Borgers. That is why G2 is not optional, and why the frequency heuristic that catches one-off typos does not catch this one.

  • screen_companies — the universe pass, and the audit_concentration breakdown of any screen
  • company_brief — one company's auditor, PCAOB id, city, and stated prior auditor, on every plan