# Pre-flight a supplier before an agent onboards it > Identity, insolvency, live VAT, Peppol, control chain and sanctions on the company and on its ultimate parent — one call, one verdict, and the reason behind it. Published 2026-08-10 · Updated 2026-08-16 · HTML version: https://invoket.com/blog/pre-flight-a-supplier-before-an-agent-onboards-it --- An agent about to onboard, pay or invoice a counterparty has to answer seven questions at once — does it exist, is it still active, has anything been gazetted against it, is its VAT number valid right now, can it receive an e-invoice, who controls it, and is either it or its parent designated on a sanctions list. [`GET /preflight/supplier`](/docs/api/preflight-supplier) answers all seven in one call and returns **one verdict** — `ok`, `review`, `stop` or `insufficient_coverage` — with every reason attached to the block and the source it came from. Its governing rule is the one that makes the verdict worth reading: **`ok` is impossible while any block is unavailable.** A check that did not happen never passes. ## The problem: the answer is not the hard part, the aggregation is Every one of those seven questions already has a public answer. INSEE Sirene says whether the legal unit is active. BODACC publishes the insolvency judgments. VIES answers on the VAT number. GLEIF publishes the declared control chain. OFAC, the EU and the UN publish their designation lists. The registers are open; nothing here is proprietary intelligence. What an autonomous agent lacks is not the data, it is a **decision it can defend** — one call whose result is a verdict, not seven responses in seven shapes that someone still has to reconcile. And the reconciliation is where counterparty checks actually fail: the FBI's IC3 counted **$3.04 billion** in verified business-email-compromise losses across 24,768 complaints in 2025, on top of a **$20.9 billion** total, with the great majority of BEC funds moving through ordinary wire and ACH workflows ([2025 IC3 Annual Report](https://www.ic3.gov/AnnualReport/Reports/2025_IC3Report.pdf)). Those payments do not fail a technical filter. They fail a supplier check that was partial, or skipped because it was seven calls long. ## The rule: a gap is not a pass, and a gap is not a signal The composite keeps two distinctions that a naive aggregator collapses, and the whole value of the verdict rests on them. **`not_applicable` is an answer. `unavailable` is a gap.** "GLEIF publishes that there is no parent above this entity" is an answer, and an answer never blocks an `ok`. "We could not establish the control chain" is a gap — and only the gap forbids `ok`. Each block therefore carries one of four coverage states: | `coverage` | Meaning | Effect on the verdict | |------------------|-------------------------------------------------------------|-----------------------| | `complete` | The question got its answer | none | | `unavailable` | It did not — missing dataset, register unreachable | **forbids `ok`** | | `not_applicable` | There was nothing to check | none | | `not_requested` | You waived the check explicitly | none | **A gap is also not a signal.** A VAT number VIES could not check is a gap; a VAT number a member state firmly answers as invalid is a signal, and it weighs on the verdict. Conflating the two produces the two failure modes that make automated screening useless — a green light because a register was down, or a red flag because a register was down. A block that got no answer carries **neither `data` nor `provenance`**, only its coverage and a short stable reason. The gap is stated, never filled. And the verdict is always *exactly* the maximum severity among the listed reasons, each carrying its block and a stable code — so an agent can **recompute the verdict** from the blocks instead of taking it on trust. ## The check no sequence of unit calls gives you: the parent The `/company/*` endpoints and [`/screen/entity`](/docs/api/screen-entity) will each answer their own question if you would rather orchestrate them yourself. One thing an endpoint-by-endpoint review structurally misses: screening the **ultimate parent**, because you have to resolve the control chain first in order to know whose name to screen. That is the case the composite exists for, and it looks like this in production (abridged from [the doc page's captured example](/docs/api/preflight-supplier#review--the-parent-is-the-reason-this-endpoint-exists)): ```json { "subject": { "name": "IBM FRANCE FINANCEMENT", "siren": "316037753", "lei": "0XKTQGPTA3P542CII630" }, "verdict": "review", "verdict_reasons": [ { "block": "parent_screening", "code": "parent_weak_sanctions_hit", "detail": "partial_tokens" } ], "blocks": { "identity": { "coverage": "complete", "data": { "status": "active", "…": "…" } }, "events": { "coverage": "complete", "data": { "total": 0, "flags": { "has_open_insolvency_announcement": false } } }, "vat": { "coverage": "complete", "data": { "status": "valid", "…": "…" } }, "group": { "coverage": "complete", "data": { "ultimate_parent": { "status": "declared", "link": { "entity": { "legal_name": "INTERNATIONAL BUSINESS MACHINES CORPORATION", "legal_country": "US" } } } } }, "name_screening": { "coverage": "complete", "data": { "verdict": "no_hit", "hits": [] } }, "parent_screening": { "coverage": "complete", "data": { "verdict": "weak_hit", "hits": [ { "list_source": "OFAC-SDN", "match_type": "partial_tokens" } ] } } } } ``` The company itself is spotless — active, VAT valid, nothing gazetted, `no_hit` on the lists. The `review` comes **entirely from one level up**. Note also that a `weak_hit` on the parent is a `review` and never a `stop`: a partial token overlap is a lead to check, not a designation. ### Control chain is not beneficial ownership, and the difference is legal State this one exactly, because an agent that treats a `group` block as a UBO check is wrong in a way that matters. GLEIF Level 2 records the entity's **direct and ultimate accounting consolidating parent** — the highest legal entity preparing consolidated financial statements — **self-declared by the entity itself, with no ownership percentage** ([GLEIF, Level 2 Data: Who Owns Whom](https://www.gleif.org/en/lei-data/access-and-use-lei-data/level-2-data-who-owns-whom)). It is an accounting definition of control, not an AML one, and the absence of a declared parent is not evidence of an absence of control. There is no open-register substitute either. On 22 November 2022 the Court of Justice of the EU invalidated the provision of the anti-money-laundering directive that opened beneficial-ownership registers to the general public (joined cases C-37/20 and C-601/20), holding that it was a serious interference with the rights to private life and data protection; access now requires a demonstrated legitimate interest ([eucrim summary](https://eucrim.eu/news/cjeu-no-unrestricted-access-to-data-of-beneficial-owners/)). So a public, unauthenticated pre-flight can tell you the declared consolidation parent and screen it. It cannot tell you the beneficial owner, and the endpoint says so in `data.limits` on **every** response rather than in a footnote. ## `insufficient_coverage` is the honest answer, not a failure Most French companies have no LEI. No LEI means no publishable control chain, which means the parent cannot be screened either — so a perfectly clean supplier comes back like this: ```json { "subject": { "name": "DECATHLON FRANCE", "siren": "500569405", "lei": null }, "verdict": "insufficient_coverage", "verdict_reasons": [ { "block": "group", "code": "coverage_unavailable", "detail": "no_lei_declared_for_siren" }, { "block": "parent_screening", "code": "coverage_unavailable", "detail": "group_unavailable" } ] } ``` Active, VAT valid, nothing in the gazette, nothing on the lists — and still not `ok`. The gap **cascades and is named**, rather than being reported as "no parent". This is a structural limit of the LEI reference, stated as such: not an outage, and not a silence. For an agent, it is the useful answer — it says precisely which question is still open, so the workflow can escalate that one question instead of re-running everything. ## When a register throttles, a dated verdict beats a lost `ok` VIES routes each query to the member state that owns the data, and each national database has its own maintenance windows and outages; "member state service unavailable" is a normal, frequent outcome of the system rather than an exception ([Your Europe / VIES](https://europa.eu/youreurope/business/taxation/vat/check-vat-number-vies/index_en.htm)). Quotas are per member state, not per caller: a pre-flight can be throttled because the rest of Europe is querying that register at that second. Losing an `ok` over that would blame the supplier for someone else's traffic. So when the live check fails and a **firm** verdict on that exact number is already held from the last 24 hours, the `vat` block serves it — `coverage: "complete"`, `served_from: "cache"`, with `cached_age_secs` and a `freshness.kind` of `cached` — and `ok` stays reachable. Two things this does not change: the verdict is never invented (only a firm `valid` / `invalid` really obtained from VIES ever enters the cache), and a number we have **never** had a verdict on stays the gap it always was. Read the age before acting on it; a stricter agent can apply its own threshold and re-check the number itself through [`GET /company/vat`](/docs/api/company-vat). ## The onboarding loop 1. **Anchor** the counterparty on one key — `siren` or `lei`, exactly one. Passing both, or neither, is a `400`. Entering by `lei` on an entity whose GLEIF record declares a real SIREN reopens all the French blocks; a foreign entity with no French registration gets them as `not_applicable`, never as a gap. 2. **Add the bank leg when you are about to pay**, by passing `iban`. That attaches a whole [`/preflight/payment`](/docs/api/preflight-payment) answer as the `bank` block — and it uses the supplier's **register name** as the beneficiary name, so the name check is genuinely screened rather than skipped. 3. **Branch on the verdict, not on a score.** `stop` and `review` are decisions; `insufficient_coverage` is a named open question. There is no numeric risk score to threshold, by design. 4. **Audit against the blocks.** Every reason points at a block; every block carries its own `provenance` with the snapshot date it was answered from. The root `provenance` reflects only the identity foundation — the registers are dated differently and the VAT check is live, so the dates that matter are on each block. 5. **Persist the response, not a boolean.** Six months later, "why did the agent onboard this supplier" is answerable from the stored blocks and their dates. The payment side of that loop is the usual discover → `402` → pay → replay cycle; the [Quickstart](/docs/quickstart) walks it with runnable snippets and [For agents](/docs/for-agents) covers the discovery surfaces. ## Billing: an unusable answer is never billed The x402 golden rule is that the agent pays for the *answer to its question*, so the mapping is strict in both directions. A counterparty in liquidation is a **useful answer** → **200** with a motivated `stop`, and it is billed. A well-formed identifier that no register knows is a **404**, and it is **unbilled** — no empty pre-flight is ever paid for. `stop`, `review` and `insufficient_coverage` are all billed, because all three are answers. This endpoint is not priced at the catalog's flat per-call rate, and that is deliberate: one call runs seven checks, eight with an IBAN, in one round-trip and **one settlement**. **No price appears in this article** — the gateway's [`/catalog`](https://api.invoket.com/catalog) is the single source of truth for every amount and every accepted rail, the unit endpoints included, so compare them there. ## What it will not do - **No search by name.** The entry key is a SIREN or an LEI. Resolving a name to an identifier is a different problem and is not solved here. - **No credit opinion, no risk score, no financial statements, no directors.** The verdict is a deterministic decision table over sourced, dated facts. - **No reconciliation between registers.** If Sirene reports a legal unit as active while BODACC shows a conversion to liquidation, the composite reports both and lets the most severe fact decide — it does not adjudicate which register is right. - **Peppol reachability is a directory fact, not a signal.** A supplier absent from the Peppol directory weighs nothing on the verdict; it just cannot receive an e-invoice at that address. - **`ok` means what it says and nothing more**: every requested check was answered, and none of them raised a signal, on the data served. That last sentence travels verbatim in `data.limits` with every response, which is the point — the caller and the auditor read the same scope statement. For the parameter contract, the full verdict and reason-code tables, the block shapes and the error codes, see the [`GET /preflight/supplier` documentation](/docs/api/preflight-supplier).