Your row goes in. The company comes back with the working attached.
Seven stages, stopping at the cheapest one that can decide.
Three things can happen to a row, and you can defend all three
You send a row — a name, a company number, an LEI, a domain, half an address. Each outcome leaves something you can show: an evidence trail, a recorded refusal, or the name of the column that would have settled it.
Your rows are one company.
You get the company with every field cited, and a decision id that reopens the whole evidence trail at a URL. One credit where an identifier or the scorer settled it, five where it took a model or live registry research.
The cascade proposed a merge and did not get to make it.
Two candidates within 0.02 of each other, or two carrying the same decisive identifier, and the merge is withheld even above the upper threshold. The record escalates carrying indistinguishable_candidates in its features, so the refusal is a fact on the row and never a silence.
Nothing clears the floor, so nothing is bound.
Back comes the ranked shortlist, a typed reason, the one column of yours that would have settled it, and a review id a person can work. Zero credits, at every stage, including the ones that spent the most getting there.
- The confidence floor. Below it a match is unrepresentable, not merely discouraged. It is an acceptance contract, not calibration evidence: it says what we refuse to return, not how often an accepted match is right. Read it with the coverage it costs.
- 0.90
- Credits for an abstention, at every stage, including the expensive ones
- 0
- Accepted matches at or above 0.90. Every accepted match on the representative fixture cleared the confidence floor. Nothing below it is returned as a match at all.
- 1,666 / 1,666
- False binds across the six ways a file arrives, on the 317-company fixture. Historical results on an internal UK fixture with a preloaded catalogue. The September 5 replay has no full-prompt-verified calls and cannot establish current calibration. No independent customer holdout or global coverage claim.
- 0
Internal fixtures only. The historical 2026-08-29 per-tier recording covers 317 GB companies with identifier-rich arrivals seeding the catalogue first; the context tier includes country and registered-town prose. Its legacy replay has no full-prompt-verified calls and cannot certify current calibration. The September 5 CI reports cover 1,176 labelled pairs and 705 records over 250 companies using offline stubs. These are not independent customer holdouts or live production validation; the 99% minimum-input product target remains unmet. Scope is company and organisation identity only — not people, vessels, bank-account ownership, sanctions or emissions. Results may not be used for eligibility decisions about individuals, including employment, credit and tenancy.
Figures here come from fixtures we built. There is no external customer holdout. The method is published in full, and where assurance stands is under Trust and security.
On this page
The cascade, and where it stops
Most of your file never reaches a model. The cheap deterministic stages carry the bulk of it and model spend goes to the ambiguous tail, so an identifier-rich file costs almost nothing to run and a file of bare names costs what it costs. Running a frontier model on every record would cost roughly a hundred to a thousand times what bulk traffic can carry, and it would not give the same answer twice.
- 01
Normalise
no decision, no chargeTidy the row until two spellings of one company can meet.
Legal forms, punctuation, transliteration and register-specific number shapes. A Companies House number is eight characters behind an optional letter prefix, and knowing that is what stops a stripped six-digit number colliding with a charity reference.
- 02
Retrieve candidates
no decision, no chargeFetch the handful of companies worth comparing, leaning on the rarest words in the name.
Rarity-weighted blocking pulls a shortlist out of the catalogue and the registers. Measured recall at ten is 1.0000 with zero misses on the representative fixture, which is the property the later stages depend on.
- 03
Deterministic identifier rules
1 creditIf an identifier agrees, decide here and run nothing else.
An agreeing company number or LEI, checked for register shape rather than for string equality, decides on its own. No model call, no egress, one credit.
- 04
Probabilistic scoring
1 creditWeigh what agrees against what does not, then accept, reject or escalate.
A Fellegi–Sunter three-region policy: accept, reject, or the borderline band that escalates. Weights are fitted, and the out-of-fold analysis of that fit is published rather than assumed.
- 05
LLM adjudication
5 creditsPut the register’s own evidence in front of a model, for the rows scoring could not separate.
The borderline band only, with the register's own evidence rendered into the prompt — every rival candidate's town, status, previous names and legal form. Verdicts are calibrated per model and prompt version, so a prompt change fails closed to abstention rather than quietly recalibrating the product.
- 06
Agentic research
5 creditsGo and look, against the live sources, and cite what was read.
The ambiguous tail: registry and web research against the live sources, cited. Reached by a small minority of records, and the reason a depth cap exists.
- 07
Abstain
0 creditsSay so, and hand back the shortlist and the column that would have settled it.
Nothing clears the floor, so nothing is bound. The record returns a ranked shortlist, a typed reason, the column that would have decided it, and a review id. Free, at every stage, including the ones that spent the most getting there.
All seven stages are one endpoint from a caller’s side. What a request accepts, what each response carries, which of your columns map where, and what happens when two company identities conflict are documented on the company entity resolution API reference.
What a decision costs, and how to cap it
The whole rule is five lines, and three of them are zero. Plus the depth cap a caller sets per record.
What a decision costs, and how to cap it
The whole rule is five lines, and three of them are zero. Plus the depth cap a caller sets per record.
The module that quotes the price in the response also writes the ledger row and serves the public pricing endpoint, so the number here and the number you would be charged cannot drift apart. Paid subscriptions are purchased through Stripe in workspace Billing; usage is recorded separately without automatic usage or overage charges.
- 1
- A decision reached by the deterministic or probabilistic stage. CPU only, no egress.
- 5
- A decision that needed LLM adjudication or agentic research.
- 0
- An abstention, at any stage, including the ones that spent the most getting there.
- 0
- A byte-identical replay. Per tenant, per exact content, and with no expiry.
- 0
- An input that normalised to no identifying signal. There was nothing to answer.
A record that normalises to nothing comparable returns unresolved with no_identifying_signal before any candidate is retrieved: no entity minted, no fingerprint bound, no review item queued.
A depth cap the caller sets, per record
depth caps how far the cascade may go for one record, and it is a per-record argument even inside a batch. depth=probabilistic forbids egress entirely — no registry candidate pass, no evidence lookup, no adjudication — enforced at both egress points, and it is what the Free plan is. A capped record still goes to the review queue.
The published method behind stage 04
- Fellegi and Sunter (1969), “A Theory for Record Linkage”, Journal of the American Statistical Association 64(328), 1183–1210
The paper behind stage 04's three regions, accept, reject and the borderline band that escalates. Its optimality result holds under the model's assumptions, conditional independence between fields among them, which real company data usually breaks.
Which stage your rows stop at is a property of your columns rather than of this table. Create your Free workspace and inspect a company CSV; contact us for an assisted composition preview before a larger run.
Start freeThe floor underneath all three
This is the part that stops a wrong merge reaching your file. Stage 04 turns the scored pair into a probability and reads it against two thresholds: above the upper one, and clearly ahead of the runner-up, the pair merges on its own; at or below the lower one it is a non-match. Everything between escalates, and so does anything the margin guard will not call unique. Underneath all three sits a number none of them can go below.
A match below 0.90 confidence is unrepresentable. The record comes back as an abstention with a ranked shortlist instead.
It is not a setting anyone can turn off. The refusal is built into the object that carries a resolution outcome, and checked again on every path that can produce one. Enforced as a domain invariant on the resolution outcome and again by a predicate called on the online, batch and replay paths, so a sub-floor accepted match cannot be constructed on any of them. A cached replay whose inherited confidence has fallen below the floor is withheld rather than re-served.
MIN_ACCEPTED_MATCH_CONFIDENCE is one immutable constant with one predicate beside it, so the online, batch and replay paths cannot invent subtly different comparisons of the same number.
An acceptance contract, not calibration evidence
It is an acceptance contract, not calibration evidence: it says what we refuse to return, not how often an accepted match is right. Read it with the coverage it costs.
The records it refuses are records you do not get an answer for, and /accuracy reports that coverage cost beside every safety figure.
Two carve-outs:
- A merge a human reviewer approves is recorded against the reviewer, excluded from the automated population every accuracy figure is measured over, and withheld on replay.
- A new entity minted because nothing matched is not a match and is not subject to the floor. Its reject-side confidence can fall as low as 0.6752, on 27 observations.
How the three thresholds were set, and where they are weakest
Each one swept, with the run that set it. The reject side is the weakest claim this product makes.
How the three thresholds were set, and where they are weakest
Each one swept, with the run that set it. The reject side is the weakest claim this product makes.
- upper 0.97
- Swept, not chosen — and the sweep that set it is dated. On the superseded 2026-07-29 pairwise snapshot (1,173 labelled pairs, stub adjudicator, run at lower 0.35 and an adjudicator floor of 0.98 that has since been corrected to 0.50) 0.97 was the lowest threshold whose auto-merge precision lower bound cleared 0.99: 1.0000 over 406 merges, 95% CI [0.9906, 1.0000], at 0.6726 automation over the 1,173 pairs. That table is retained to explain how the threshold got here; it is not a current configuration report and not shipped performance. Neither current instrument proves 0.99. The 2026-09-05 continuous-integration gate bounds at 98.94% on 357/357 error-free automated merges at 63.10% automation, which is 24 consecutive clean merges short of a 99% lower bound, and out of fold at this threshold the probabilistic band carries one error in 231 merges, bounding at 0.9759. Automation is non-increasing in the upper threshold, so a higher threshold buys a tighter bound by deciding less. And the knee is one row deep: everything from 0.70 to 0.96 is blocked by a single handwritten holding-company and operating-company pair sharing one group website, which scores 0.9614.
- lower 0.35
- Not provable on either instrument. No swept value reaches a 0.99 negative predictive value lower bound, so it stays where it is: the four rows that separate the candidates are not enough to move it on. A wrong non-match is a duplicate entity, and nothing gates it. The reject side is the weakest claim this product makes.
- adjudicator floor 0.50
- Derived by five-fold out-of-fold selection over 370 recorded live verdicts, which chose 0.50 unanimously with zero wrong merges in every held-out half. It had been 0.98, which was not a threshold but a wall: a live run measured the model’s confidence range at 0.20 to 0.93, so a floor at 0.98 sat above the model’s entire output range and discarded every verdict the product had paid for.
- the margin guard
- Even above the upper threshold, an auto-merge is withheld when the two best candidates sit within 0.02 of each other, or when more than one candidate carries the same decisive identifier. The record escalates and every such row carries indistinguishable_candidates in its features. On the 116-case resolution gold set this cut baseline false binds from 0.0309 to 0.0208 and raised accuracy from 0.9381 to 0.9479, for 0.86 points of resolution rate.
- how high it can go
- Confidence limits depend on the population behind the decision. The highest shipped population bound is 0.9968. The nominal 95% Wilson lower bound of 1,190 historical prompt-v8 uncorroborated MATCH verdicts with zero recorded errors. The 2026-08-29 population pools six input shapes with repeated company families and calibration/evaluation overlap. Its independence and transfer to new customer inputs are unproven. Legacy replay cannot certify current calibration. Confidence bounds depend on the recorded evidence population; there is no universal score for every match. Read match_confidence_basis and the evidence. A 0.99 filter can return some v8 adjudicated matches while excluding valid identifier matches, so validate its coverage cost on your own inputs. A stricter threshold needs an evaluation on your own labelled records, with the coverage it costs reported beside precision.
Already have a match policy? Ours is above — floor, thresholds and margin guard. Create your Free workspace and inspect your own rows against it. Contact us if you want an assisted comparison of the two policies.
Start freeWhat a decision id redeems
Every decision keeps a receipt: the features it was taken on, the thresholds in force when it was taken, the evidence it cited, the model and prompt version where a model was involved, the pipeline version, and a re-run of the score from the stored features. Refusals are receipted on the same terms, and reading one back is a free call.
It is written for the person who has to defend the merge, and the reproduction is what stops it being an assertion. Two fields on it are easy to confuse. The pair score is checkable: the contributions are the shipped log-odds weights times the feature values, and replay.recomputed_probability re-derives it. But match_confidence is the one to gate on — the probability that this record is bound to the right real-world company, and always the smaller of the per-record estimate and what its population supports.
One gap, unfixed: the receipt names the features and evidence that decided a pair, and returns no fact value for any attribute. It will not tell you what the register said each field was.
A receipt in full, field by field
The exact payload from an internal fixture record, and what each field is for.
A receipt in full, field by field
The exact payload from an internal fixture record, and what each field is for.
The buyer’s criterion is “a merge I cannot explain”, not “a merge I cannot reproduce”.
GET /v1/decisions/dec_01K3XJ7Q2M5R8V0C4NDYB6WPTF
Authorization: Bearer eb_live_…
200 OK
{
"decision": {
"decision_id": "dec_01K3XJ7Q2M5R8V0C4NDYB6WPTF",
"left_record_id": "rec_01K3XJ7PZ8H4A2E9T6KMSD1VRC",
"right_ref": "ent_01K2WQ4F7N0B3J5XGRV8YH6PDA",
"decision": "match",
"stage": "probabilistic",
"probability": 0.9919,
"match_confidence": 0.9755,
"match_confidence_basis": "calibrated_probability",
"identifier_rule": null,
"top_features": [
{ "feature": "name_token_sort", "value": 1.00, "contribution": 4.2337 },
{ "feature": "postal_match", "value": 1.00, "contribution": 2.9338 },
{ "feature": "name_jaro_winkler", "value": 1.00, "contribution": 1.6650 },
{ "feature": "address_similarity","value": 0.91, "contribution": 0.9942 },
{ "feature": "address_present", "value": 1.00, "contribution": -0.8300 },
{ "feature": "postal_present", "value": 1.00, "contribution": -0.8139 }
],
"thresholds": {
"upper": 0.97,
"lower": 0.35,
"llm_confidence_floor": 0.50,
"min_accepted_match_confidence": 0.90
},
"verdict": null,
"evidence": [
{
"source_id": "companies_house",
"source_kind": "registry",
"licence": "OPEN",
"url": "https://api.company-information.service.gov.uk/search/companies?q=…",
"summary": "3 companies match this query: \"ARDEN LOGISTICS\" (unfiltered search).",
"retrieved_at": "2026-08-29T09:41:11.402Z"
}
],
"pipeline_version": "2026.08.29+fs7",
"reviewer_id": null,
"acting_key_id": null,
"acting_surface": null,
"decided_at": "2026-08-29T09:41:12.884Z",
"replay": {
"reproduced": true,
"recomputed_probability": 0.9919,
"pipeline_version_matches": true
}
},
"correlation_id": "cor_01K3XJ7Q2M5R8V0C4NDYB6WPTF"
}The shape is exact — field names, enum values and the null-versus-absent distinction are the API’s own. The values are from an internal fixture record, not a customer’s.
- probability 0.9919
- The pair score. The contributions above are the shipped log-odds weights times the feature values; add the model intercept of −4.2558 to the six shown plus the remaining feature terms and the logistic of the sum is the probability printed on the receipt. That is what replay.recomputed_probability re-derives.
- match_confidence 0.9755
- A different quantity from the pair score, and the one you should threshold on: the probability that this record is bound to the right real-world company. It is the smaller of the per-record estimate and the population bound — here the 95% Wilson lower bound of 292 clean probabilistic binds out of 294 observed, which is 0.9755. The pair scored higher; the population does not support more.
- identifier_rule
- Null here because the scorer decided. Where a deterministic rule decided it names the rule — lei_agreement, regno_agreement — and those merges carry no probability, so replay.reproduced is null on them by construction. Without this field the receipts for the most certain merges in the system would read as the emptiest.
- thresholds
- The thresholds this decision was taken under, not today's. A receipt read after a policy change still has to explain the call that was actually made.
- pipeline_version_matches
- False after a pipeline release, because the score is then reproduced under different weights than the ones that decided it. That is the expected reading after a release, which is why it is a separate field from reproduced and never folded into it.
- evidence[].summary
- A register search count is cited as evidence, phrased as “N companies match this query” and never as “N companies share this name”, with the query stated verbatim and whether the search was filtered. A registry search is a full-text index, not a uniqueness constraint. One match for a coined name is close to decisive; one match for “Global Services Ltd” is a fact about a narrow query, and the adjudicator prompt is forbidden from letting a count decide on its own.
- as a population
- GET /v1/decisions returns receipts newest-first, filtered by pipeline_version, threshold_upper and a half-open decided_at window, plus acting_key_id where a credential applied a human verdict. That is what makes “show me every decision taken under a threshold we have since raised” a question with an answer. Every row already stored all of it; what did not exist before was a way to ask for more than one id at a time, so the remediation question required an export and a script.
A receipt per decision is what the audit pack is made of. Upload your own CSV in your Free workspace to inspect its results and receipts. Contact us separately to scope an assisted twelve-file audit pack.
Start freeYou pay once per distinct record, ever
Before the cascade runs, the record's canonical JSON is hashed with SHA-256, excluding the server-minted record id. That fingerprint is the replay key: send the same bytes again and the same binding comes back at zero credits, with no expiry.
A cache time-to-live is a promise about our infrastructure that expires on our schedule. A fingerprint is a property of your data.
The key is the composite of fingerprint and tenant, so identical content from two customers is two paid resolutions producing two entities. There is no cross-customer discount for a company we have seen before, and a filter would not have given us one: the insert itself collided.
One changed character is a fresh, chargeable resolution: a trailing space, a corrected postcode, a re-cased legal form. Erasure drops the binding, so a record you asked us to forget is one you pay to resolve again.
A replay returns the binding the original resolution earned, at the stage and confidence it earned. A reviewer’s verdict replays as human review at confidence 0.0, never as an automated match. The golden record you get back is the entity as it stands now, so it may carry facts it did not carry last month.
Re-running the same file next quarter costs nothing, with the two caveats above. Create your Free workspace to run this quarter’s CSV, then choose a paid plan in Billing when your workload needs more.
Start freeFive things that decide what lands in your file
Which value wins, what may be stored, what a parent means, what a batch boundary does, and who may overrule any of it. Open what you need; all of it prints.
Golden records and survivorship
Once records are clustered, one value per attribute has to win. The order it wins in, and what happens to the ones that lose.
Golden records and survivorship
Once records are clustered, one value per attribute has to win. The order it wins in, and what happens to the ones that lose.
Survivorship ranks facts that are valid now ahead of future or superseded episodes, then by source precedence, then by recency taken from when a source last confirmed the value, then by confidence, then by a case-shape and value tiebreak. Every fact is kept, not just the winner: the losers stay readable with their own provenance.
A register is not a well-run database, it is the legal definition of the company's identity. A named human reviewer sits at the same rank, and carries the same obligation to cite.
A document the company itself submitted to a register or a regulator.
What you sent us. Above our own research, below the register that defines the company.
Our own cited research against live sources. Open-licensed and therefore storable, which is part of why it outranks the tier below it.
A paid enrichment vendor. Below our own research, on evidence: a live probe on 2026-08-12 had a paid vendor return “HSBC Group” in Londonderry, NH, US where Companies House returned “HSBC HOLDINGS PLC”, 8 Canada Square, London E14 5HQ.
Derived by us from other facts. Never outranks its own inputs.
The ordering is total. One set of facts has exactly one ordering regardless of the order they arrived in, so an unchanged record re-assembles byte-identically and does not fire a downstream change-data-capture or screening event for a record that did not change. A name-like attribute tie is broken on case shape, so a mixed-case spelling outranks an ALL-CAPS or all-lowercase one and a tie between spellings does not export shouting into your CRM. Identifiers, codes, domains and emails are not case-ranked, because for those the case is not a spelling choice.
When this attribute, from this source, at this value, was first observed.
When a source last re-confirmed the same value. Repeat observations collapse into the held fact; none of them is discarded.
How many confirmations there have been. Counts observations from one source, not independent corroborations.
Continued confirmation outranks novelty, which is why the survivorship tiebreak reads the confirmation date rather than the retrieval date. But freshness breaks ties only within a source tier and never across one, so a stale registry value still beats a fresh web observation. The response surfaces the same fields: per-attribute provenance carries first_seen_at, last_checked_at, checks and a derived age_days, and the entity carries last_checked_at and stalest_field_age_days.
Reporting the freshest field would let one recently-confirmed value disguise a stale record.
Nothing re-checks a fact on a schedule
There is no background revalidation job. Freshness advances only when a resolution happens to re-observe a value, so a record nobody has resolved for six months has six-month-old confirmation dates and the response will tell you so. stalest_field_age_days is published so you can decide what is too old.
For the same reason, checks is not a confidence multiplier. Three confirmations from one source is one source three times, and directory and aggregator listings copy each other and each other’s mistakes.
Licence tiers, enforced at the write path
Every fact carries a licence tier, and the repository refuses to persist a pass-through fact before any write happens.
Licence tiers, enforced at the write path
Every fact carries a licence tier, and the repository refuses to persist a pass-through fact before any write happens.
This is not a policy somebody has to remember at review time; it is a refusal in the code path between the resolver and the database.
Storable and, on the plan that carries resale rights, resellable.
SEC EDGAR, Companies House under the Open Government Licence, GLEIF under CC0, state open data, and our own cited web research.
Storable during the term of the contract that supplies it, and not resellable.
Licensed-during-term enrichment vendors.
May be returned to you in an API response. May never be warehoused here. The repository raises before the write happens.
Vendors whose terms prohibit building a database from their data.
- per-field resale
- Provenance distinguishes two things a single flag would conflate: licence_permits_resale is a fact about the licence, and resellable is that conjoined with whether your plan carries resale rights. Collapsing them would report a CC0 value as non-redistributable to everyone who has not bought the term.
- derived facts
- A derivation cannot launder a tier. The licence on a derived value is the most restrictive of all its inputs and of the method's own reference data, its confidence is at most the minimum input confidence, and its freshness is the stalest input's confirmation date. A derivation cannot manufacture certainty either.
- CUSIP and SEDOL
- Absent by design rather than not yet built. CGS terms prohibit using the data to create or maintain a master file or database of CUSIP descriptions or numbers, which is textbook pass-through, and the persistence layer would raise on one. ISIN is specified and blocked pending legal advice. DUNS is declared absent: nobody was asked, and pooling customer-submitted DUNS across tenants is prohibited.
Enforced by construction, and unexercised
Every source wired today is open-licensed, so nothing in production has ever tripped the pass-through refusal. It is proven by its tests and its type signatures rather than by an incident it caught.
Which registers are wired, under which licence, and what each one does not cover →
The identifier crosswalk
LEI, company number, CIK, ticker and domain, each citing the fact that supplied it — and what a parent field does not mean.
The identifier crosswalk
LEI, company number, CIK, ticker and domain, each citing the fact that supplied it — and what a parent field does not mean.
Every path that writes an entity stamps a derived crosswalk attribute onto it, including one minted by a reviewer’s verdict. It holds one entry per scheme and value, each carrying the fact that supplied it, so the citation is per identifier rather than per record.
- what is qualified
- A company number is qualified by its jurisdiction and a ticker by its exchange, and a ticker is qualified only from an exchanges fact carrying the same source id. An LEI must pass the ISO 17442 MOD 97-10 checksum. The crosswalk cites the source URL of the most authoritative contributing fact and takes its confidence as the minimum over contributors.
- the negatives
- Absence is reported as a finding rather than as a silence. NOT_FOUND means a wired source supplies that scheme and had no value for this company; NOT_CONFIGURED means no wired source supplies it at all. Collapsing the two would make “this company has no LEI” and “GLEIF is not wired here” the same answer, and only the first is about the company.
- ownership
- Sourced, not inferred: GLEIF Level 2 relationship records — direct parent, ultimate parent, branch association, direct children — and the UK register of persons with significant control. Every edge states its basis (accounting consolidation, voting rights, share ownership, registry declared, self declared, unknown) and its certainty (registry asserted, derived by traversal, inferred).
- max_hops
- Traversal is a projection over facts already held, never a resolution. max_hops defaults to 1 and may be raised to 10 per call. Edges are followed only through exact LEIs and authority-scoped registration numbers, within your tenant, never by name and never across tenants, with cycle detection and hard caps on identifier attempts, nodes, edges and repository reads. not_traversed is populated on every response, parent found or not, so depth can never be inferred from an empty field.
- percentages
- Control under the UK register is published as bands. Bands are never multiplied and never converted to a midpoint. An indirect percentage is emitted only when every hop carries an exact registry-published percentage; otherwise the response carries percentage_withheld_reason: band_arithmetic_refused as part of the schema.
“We could compute a number and chose not to, here is why” is a stronger statement to a know-your-business buyer than a number with invisible error bars.
A parent is not one thing, and a subsidiary list is not complete
GLEIF’s definition of a parent is accounting consolidation. A commercial data provider’s can be majority legal ownership. They are different questions with different answers, and rendering both as one “parent” field is wrong in a way nobody downstream can see, which is why the basis travels on the edge.
Subsidiary completeness carries its own bound: a direct-children traversal over GLEIF is complete only with respect to entities that have an LEI, and there are roughly 3.4 million LEIs against roughly 500 million D-U-N-S records. An empty child list is an absence of coverage, not an absence of owners. Where the register itself says why it reports no parent — no LEI, not consolidating, natural persons, non-public, legal obstacles — we return that exception, which turns a 404 into a citation.
Two gaps. The identifier vocabulary currently carries about five of roughly twenty identifier-ish attributes; VAT, ISIN and successor LEIs are held but not crosswalked. And scope here is company and organisation identity: officer and control names appear where a register publishes them under an open licence, but person-disambiguating identifiers are excluded by design, so the ultimate-owner field names people it cannot resolve.
Batch determinism, and the chunking it does not fix
The same file in a different row order gives the same partition. Split into different batches, it does not.
Batch determinism, and the chunking it does not fix
The same file in a different row order gives the same partition. Split into different batches, it does not.
A batch resolves in two phases. Phase one proposes every record through the unchanged cascade against the entity store as it stood when the batch began, with writes staged so no record can see another record’s proposal. Phase two collects the intra-batch match edges, computes connected components with union-find, and folds each component into one entity.
An entity-resolution system whose output depends on the order of its input cannot be reconciled against, cannot be re-run to the same answer, and cannot be parallelised.
The defect this replaced was measured, not theorised: the old sequential loop produced nine different partitions across the twenty-four orderings of the same four rows, and a persona run saw 3 of 60 rows flip between merged and unresolved on ordering alone. The same sixty records in four orders now produce an identical partition. Ordering inside consolidation runs on the record’s content fingerprint — the same key the replay cache uses — with the record id kept only as a tiebreak, because the batch endpoint mints record ids from the idempotency key and the row index, so the id literally is the row’s position in your file. An id that names a place in a queue is exactly the thing that must not order a partition.
A record joins only if it clears the merge guard and the cascade's own admission bar against the entity as it now stands, not merely because an edge exists.
Entity-to-entity merge is refused, because there is no general unmerge path to reverse it with.
A record the cascade would not decide does not get folded into a component by way of somebody else's edge.
What chunking still changes
Order independence holds within one batch. It does not make the answer independent of how you split your file into batches, and that is not a rounding difference. Twenty-four rows in eight near-duplicate triples — a full row with a domain and a company number, a name-only row, and a name-variant row sharing the domain — put through the real batch route on 2026-08-12:
| Chunking | Entities | Review items |
|---|---|---|
| 1 batch of 24 | 15 | 7 |
| 2 batches of 12 | 15 | 7 |
| 4 batches of 6 | 15 | 7 |
| 3 batches of 8, triples straddling a boundary | 13 | 5 |
| 3 batches of 8, one row of each triple per batch | 8 | 0 |
| 24 batches of 1, rows in a,b,c order | 8 | 0 |
| 24 batches of 1, rows in a,c,b order | 8 | 8 |
The mechanism is the first fold rule working as designed. Members are admitted one at a time against the entity as it now stands, so a member admitted early changes the bar for a later one. Measured on one triple with the upper threshold at 0.97: the name-only row scores 0.9923 against an entity holding only the full row, and 0.9632 — refused — against the same entity once the name-variant row has joined it. Whether the variant is in the same batch therefore decides whether the name-only row joins the company or leaves as a singleton.
Safe direction, not strictly better output
The batch path is strictly more conservative than the one-at-a-time path. It refuses rather than over-merges, which is the direction we want, and the cost is duplicate entities where a sequential caller would have got one. So do not read the guarantee as “the same input always gives the same answer” without the within-a-batch qualifier, and do expect a re-chunked file to partition differently.
Every candidate fix — judging membership against the seed rather than the growing entity, re-running the fold to a fixed point, merging pre-existing entities when a batch bridges them — changes the merge path, and the merge path is what the accuracy gate measures.
The review queue
An abstention lands in a queue with the shortlist, a typed reason, the missing column and a review id. Who may settle one, and why those verdicts are held apart.
The review queue
An abstention lands in a queue with the shortlist, a typed reason, the missing column and a review id. Who may settle one, and why those verdicts are held apart.
The caller who abstained can poll the queue. The shortlist is capped at five candidates, a verdict is recorded as match or no-match, and the verdict becomes a labelled training example.
- who may decide
- A review write requires a distinct review credential. A resolve key can poll the queue but cannot approve anything. A verdict is refused unless it names an accountable reviewer, and the credential and surface that applied it are recorded server-side rather than accepted from the caller — a verdict that cannot evidence who approved a merge is a workflow step, not a four-eyes control.
- what a verdict may do
- Rejecting candidate A does not approve candidate B. An alternative binding has to clear the confidence floor and the shared margin guard independently, and then it persists its own earned stage and confidence. Weak or near-tied alternatives leave the verdict unapplied, so a human no-match cannot launder an unreviewed match into certainty.
- cite it or leave it
- A verdict with no cited source is a guess with a person's name on it. Searching hard and finding nothing is a real result, and the queue is designed to accept it: a shared address, a directory listing, a social profile or a bare search-result count are not evidence, and the review procedure says so in those words.
- at scale
- The queue is pollable, sortable by confidence, filterable by a minimum, and settleable in bulk up to two hundred items in one request. It is a human-paced surface and it is rate-limited as one.
Every reviewer verdict receipt is stamped as human review and is excluded from the automated-merge population that every accuracy figure on this site is measured over. A binding a reviewer approved also replays as human review rather than as an automated match. Excluding them is what keeps the published precision figures a measurement of the engine: a population that quietly absorbed the merges a human rescued would report the engine and the analyst as one number, and the number would go up for the wrong reason.
What the queue cannot do yet
Review items raised for pairs found inside a batch cannot yet be applied. A reviewer answering “yes, these are the same company” cannot move a record between two existing entities, because entity-to-entity merge is refused until a general unmerge path exists to reverse it with. The item is real and the answer is recorded; the merge is not performed.
And the flywheel is collecting rather than turning. Verdicts become labels and the port for a fine-tuned model exists; no fine-tuned model does.
Prefer to put it to a person? Talk to us instead →
Teams with a company master they have to defend — an MDM or stewardship queue, a CRM after a migration or a merger, a supplier file before a payment run, or counterparty onboarding that has to survive an audit. If you already run a licensed data feed or an MDM platform, this runs alongside it and tells you which of its matches we would not have made.
Buying breadth as the product: we hold a fraction of an incumbent’s records and are not building a global company graph. Entity Bind resolves companies and organisations only. It does not resolve people, vessels, bank-account ownership, sanctions exposure, emissions or geolocation, and its results may not be used for eligibility decisions about individuals, including employment, credit and tenancy.
The mechanism on this page is proven by fixtures we built. The retention, erasure and tenancy detail a security review asks for is on the trust page, with where assurance stands: no SOC 2, no external validation of the accuracy figures, no customer holdout. Trust and security →
Start with your own company records.
Create your workspace, verify your email and run your first company file. The Free plan needs no card. Create API keys in your workspace when you need an integration.
300 credits. No card.
For a guided review of your file, explore the Merge Safety Sprint →