# From an IBAN to the legal entity behind it > An agent holding a bank account, a security or a SIREN can name the company behind it in two calls — GLEIF identity by LEI, BIC, ISIN, SIREN or exact name plus country. Published 2026-08-11 · HTML version: https://invoket.com/blog/from-an-iban-to-the-legal-entity-behind-it --- An agent that is about to pay an invoice holds an IBAN. An agent reconciling a portfolio holds an ISIN. An agent onboarding a French supplier holds a SIREN. None of those is a company — they are handles that *point* at one. [`GET /company/lei`](/docs/api/company-lei) turns any of them into an official legal identity: legal name, jurisdiction, legal form, entity and registration status, addresses, national registration number, and the market identifiers mapped to the entity. It answers on a German, Irish, Colombian or Japanese counterparty as readily as a French one, served offline from the **GLEIF LEI** reference — the public, CC0 global identity register. The chain worth wiring is two calls long: **IBAN → BIC → legal entity**. Below it is walked with responses captured from production on 11 August 2026, against the GLEIF snapshot dated 2026-08-10 — including the case where the chain *doesn't* resolve, which is the part most integrations get wrong. ## The two-call chain, captured **Call 1 — the bank behind the account.** [`GET /iban/resolve`](/docs/api/iban-resolve) validates the IBAN and returns the institution: ```json { "data": { "iban": "FR7630004000031234567890143", "valid": true, "country": "FR", "bank_code": "30004", "bank": { "name": "BNP PARIBAS", "bic": "BNPAFRPP" }, "reachable": true, "sepa_reachability": { "sct": true, "sct_inst": true, "sdd_core": true, "sdd_b2b": true, "match": "exact" } }, "provenance": { "source": "national-bank-registries+epc-sepa-registers", "freshness": { "kind": "snapshot", "as_of": "2026-06-11T00:00:00Z" } } } ``` **Call 2 — the legal entity behind the bank.** `GET /company/lei?bic=BNPAFRPP`, abridged: ```json { "data": { "query": { "key": "bic", "value": "BNPAFRPP" }, "entity": { "lei": "R0MUWSFPU8MPRO8K5P83", "legal_name": "BNP PARIBAS", "other_names": [{ "name": "HELLO BANK!", "type": "TRADING_OR_OPERATING_NAME" }], "legal_form": "K65D", "legal_jurisdiction": "FR", "entity_status": "ACTIVE", "entity_category": "GENERAL", "registration": { "status": "ISSUED", "initial_registration_date": "2012-12-31", "last_update_date": "2026-01-26", "next_renewal_date": "2027-02-07", "managing_lou": "969500Q2MA9VBQ8BG884" }, "legal_address": { "country": "FR", "city": "PARIS", "postal_code": "75009", "line": "16 BOULEVARD DES ITALIENS" }, "registration_authority": { "ra_code": "RA000189", "registration_number": "662042449", "siren": "662042449", "verified_against_sirene": true }, "identifiers": { "bics": ["BNPAAEADXXX", "…"], "bics_total": 76, "isins": ["CH0419041642", "…"], "isins_total": 41631 } }, "matches": [], "total_matches": 1 }, "provenance": { "source": "gleif", "fetched_at": "2026-08-11T06:17:27Z", "freshness": { "kind": "snapshot", "as_of": "2026-08-10T00:00:00Z" } } } ``` Three details in that response are worth reading closely. - **The BIC-8 resolved.** GLEIF only publishes **BIC-11**; an agent that took `BNPAFRPP` off an IBAN never has the branch suffix. The lookup queries both forms, so `BNPAFRPP` finds the entity through the mapped `BNPAFRPPXXX`. - **`other_names` carries the trading name.** An invoice signed "Hello bank!" and a register entry reading "BNP PARIBAS" are the same counterparty, and the response says so instead of leaving the agent to guess. - **The lists are capped, the totals are not.** `bics` and `isins` are capped at 20 entries, but `bics_total: 76` and `isins_total: 41631` are exact. The agent always knows whether the cap hid something — a truncated list without its count is a silent lie. From here the third hop is [`GET /company/ownership`](/docs/api/company-ownership): who consolidates this entity. Bank account → institution → group, in three settlements, with no key and no account anywhere in the chain. ## Five keys, exactly one per call | Key | What an agent typically holds it from | Validation | |-----|----------------------------------------|------------| | `lei` | A counterparty record, a regulatory filing | ISO 17442 check digits | | `bic` | An IBAN (via `/iban/resolve`), a payment message | Form only — ISO 9362 has no check digit | | `isin` | A security, a portfolio line | Luhn check digit | | `siren` | A French invoice, a supplier form | Luhn check digit | | `name` + `country` | A contract header, an email footer | Exact match after normalization | Zero keys or two keys is a `400 INVALID_INPUT` — the service does not rank keys or pick one. A well-formed key that **fails its check digit** gets a distinct `400 INVALID_CHECKSUM` rather than a `404`, because those two answers demand opposite reactions: fix your input, versus conclude the entity is not in the register. ``` GET /company/lei?lei=R0MUWSFPU8MPRO8K5P84 → 400 { "code": "INVALID_CHECKSUM", "error": "lei failed its ISO 17442 check digits: likely a typo" } ``` The `siren` bridge is worth one line of explanation, because it is the one key GLEIF does not publish directly. GLEIF carries a national registration authority and a self-declared, messy registration number per entity; the bridge is **derived at ingest** under three closed rules — only the two French authorities whose number *is* a SIREN are bridged, separators are stripped, and a 14-digit SIRET yields its 9-digit prefix. A number that merely *looks* like a SIREN under another authority is not bridged: a Luhn coincidence is not a declaration. Where the derived SIREN could be cross-checked against the served Sirene register, `verified_against_sirene` says so — and it has **three** states, `true`, `false` and `null`, because "GLEIF declares a SIREN that does not exist" and "we had no register to check against" are different facts. ## When the chain breaks: the BIC map is self-declared This is the failure an agent must handle, and it is not exotic. Société Générale's head-office BIC returns nothing: ``` GET /company/lei?bic=SOGEFRPP → 404 NOT_FOUND, not billed ``` The entity is plainly in the register. Query it by name instead: ``` GET /company/lei?name=Société%20Générale&country=FR → 200 { "entity": { "lei": "O2RNE8IBXP4R0TD8PU41", "legal_name": "SOCIETE GENERALE", "legal_jurisdiction": "FR", "entity_status": "ACTIVE", "registration_authority": { "siren": "552120222", "verified_against_sirene": true }, "identifiers": { "bics": ["NEWGFRP1XXX"], "bics_total": 1 } } } ``` One declared BIC, and it is not the one on the IBAN. **The GLEIF BIC mapping reflects what each entity chose to declare, not what SWIFT assigns.** So the IBAN → BIC → LEI chain is a high-value shortcut, not a guarantee: BNP Paribas, Crédit Agricole, BRED, Crédit Mutuel and Qonto all resolve from their BIC-8 in the same snapshot; Société Générale and Caisse d'Épargne do not. Wire the fallback — `bic` first, then `name` + `country`, or the `siren` you already hold from the invoice. That a `404` here is **not billed** matters for exactly this reason: an agent that retries through a second key has paid once, for the call that answered. ## An ambiguous key returns the ambiguity, never a pick Several entities can share a normalized name inside one country. The service then serves `entity: null`, the candidates, and the exact count: ```json { "query": { "key": "name", "value": "victoire", "country": "FR" }, "entity": null, "matches": [ { "lei": "9695003FOBCTRX2HMC63", "legal_name": "VICTOIRE", "entity_status": "ACTIVE", "registration_status": "ISSUED" }, { "lei": "969500M534WKMOFRE728", "legal_name": "VICTOIRE", "entity_status": "ACTIVE", "registration_status": "LAPSED" }, "… 4 more …" ], "total_matches": 6 } ``` Six `VICTOIRE` in France. `matches` is capped at 10 and `total_matches` is exact. An agent handed one arbitrarily chosen entity has been given a fact it cannot audit; an agent handed six knows its key is too weak and can escalate to a SIREN or a LEI. Note also `query.value: "victoire"` — the echoed **normalized** key, which is why `Société Générale` matched `societe generale` above. The matching is exact after normalization and **never fuzzy**: "close enough" would be a guess wearing an official register's authority. ## `LAPSED` is a fact, and it is a common one `entity_status` and `registration.status` are served exactly as published, from closed sets, with no editorial comment: | Field | Closed set | |-------|------------| | `entity_status` | `ACTIVE` · `INACTIVE` · the literal `NULL` GLEIF publishes on some records | | `registration.status` | `ISSUED` · `LAPSED` · `RETIRED` · `DUPLICATE` · `ANNULLED` · `PENDING_TRANSFER` · `PENDING_ARCHIVAL` | Two of those deserve care. A `LAPSED` registration means the entity did not renew its LEI on the anniversary — **it does not mean the company is dead**. It is common: GLEIF reported the overall renewal rate stable at **56.6%** in Q1 2026, on an active population that passed 3.02 million entities ([GLEIF, *The LEI in Numbers*, 28 April 2026](https://www.gleif.org/en/newsroom/blog/the-lei-in-numbers-active-lei-population-surpasses-3-million-in-q1-2026)). In the `VICTOIRE` list above, three of six ACTIVE entities carry a LAPSED registration. An entity found with a lapsed registration is therefore a **`200`, and it is billed** — that status *is* the diligence signal the agent came for, delivered raw for the agent's own policy to interpret. There is no risk score in this response, and there will not be one. ## Why a payment agent is meeting the LEI more often The LEI stopped being a securities-only identifier. CPMI's harmonised ISO 20022 data requirements for cross-border payments — updated report published 26 February 2026, with adoption flexibility maintained until the end of 2027 ([BIS/CPMI](https://www.bis.org/cpmi/publ/d230.htm)) — propose the LEI and/or the BIC to identify financial institutions in payment messages ([GLEIF press release](https://www.gleif.org/en/newsroom/press-releases/gleif-welcomes-support-for-the-lei-within-the-cpmi-iso-20022-data-requirements-for-enhancing-cross-border-payments-and-wolfsberg-group-payment-transparency-standards)). An agent that can move between a BIC and an LEI is moving between the two identifiers the payments stack is converging on. ## What it will not do - **No beneficial ownership.** The LEI reference publishes an accounting consolidation parent, never the ultimate beneficial owner under AML rules. That distinction is spelled out on [`GET /company/ownership`](/docs/api/company-ownership). - **No fuzzy search, no search without a country, no search by address.** - **No financials, no scoring, no invented labels.** `legal_form` is served as the raw ELF code (`K65D`, `SGST`) because GLEIF publishes no label for it. - **No sanctions verdict.** Naming an entity is not screening it — that is [`GET /screen/entity`](/docs/api/screen-entity). - **Register coverage, not world coverage.** An entity has a LEI only because it obtained one, typically to trade on markets or to report under a regulation. **A small French company has no reason to appear here**, and its absence says nothing about it: [`GET /company/resolve`](/docs/api/company-resolve) is the French identity card for that case. ## Billing and failure modes | Case | Result | |------|--------| | Entity found — including `LAPSED`, `INACTIVE`, `RETIRED` | `200`, billed | | Key ambiguous — `entity: null` plus candidates and exact count | `200`, billed | | Zero or two keys, `name` without `country`, malformed key | `400 INVALID_INPUT`, **not billed** | | Well-formed key failing its check digits | `400 INVALID_CHECKSUM`, **not billed** | | Well-formed key unknown to the reference | `404 NOT_FOUND`, **not billed** | | GLEIF dataset not ingested, or `siren=` on an image predating the bridge | `503 DATA_UNAVAILABLE`, not settled | That last `503` is deliberate rather than a `404`: an image with no bridge table cannot assert "this SIREN has no LEI", so it declares an unavailability and the four other keys keep working. And a missed refresh keeps serving the last good snapshot under its honest `as_of` — **staleness is never turned into a 5xx**. Unlike [`GET /company/peppol`](/docs/api/company-peppol), there is no `200 not_registered` equivalent here. Peppol publishes a directory, so "absent from it" is a fact worth paying for. GLEIF publishes a register of entities that applied; the absence of a LEI is not a published fact, it is the absence of a record — hence an unbilled `404`. ## Wiring it up 1. **Pick one key, and rank your fallbacks.** `lei` > `siren`/`isin` > `bic` > `name` + `country`. Expect the `bic` hop to miss on some institutions and fall through, unbilled. 2. **Branch on `entity` vs `matches`, not on the array length.** `entity: null` with `total_matches > 1` means "narrow your key", which is a different instruction from "not found". 3. **Store `freshness.as_of` with the answer.** The response is a snapshot of the GLEIF Golden Copy, and six months from now "on what basis did the agent pay this counterparty" is answerable only if the date was kept. 4. **Read status as input, never as verdict.** `LAPSED` and `INACTIVE` feed your policy; the service passes no judgement. 5. **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). 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**. If you would rather buy the whole French counterparty picture in one settlement instead of orchestrating the family yourself, [`GET /company/report`](/docs/api/company-report) bundles identity, insolvency announcements, VAT, Peppol reachability, LEI and ownership by SIREN. The captures above are **snapshot-dated illustrations**, not a contract: a GLEIF refresh moves a renewal date, an entity declares a new BIC, a name becomes ambiguous. Only the rules are guaranteed — one key per call, an ambiguity served as an ambiguity, status served as published, totals exact beside capped lists, and an unanswerable request never billed. The full parameter and field contract is on the [`GET /company/lei` documentation](/docs/api/company-lei).