the merge safety sprint

Your match rules, tested by someone outside your team.

The audit pack: twelve files, a receipt per decision, and a report that opens offline.

A wrong merge in a company master is expensive and quiet: two suppliers become one account before a payment run, two counterparties before an audit. Send us a file. We tell you which of your rows are the same company, which of your existing matches are wrong, and what we could not decide — and we write nothing to your systems. Every decision arrives with a citation you can open and a receipt you can re-fetch, so an auditor or a new steward can check it without taking our word for anything.
Self-serviceCreate your workspace online and verify your email to start matching company records. Free starts with 300 credits and no card. Paid monthly plans are available in workspace Billing; VAT is added where applicable. Usage is tracked without automatic overage charges. Trust and security
Writes back
Nothing, to any system of yours
The bundle
Twelve files, one flat directory
Every decision
One receipt, re-fetchable
The price
Fixed, read off your own file
If we find nothing dangerousThat is a result
How it starts
One file — a CRM export, a supplier master, a post-merger overlap. We read it with the preview first, then quote from what is actually in it.
Start free →
what it got wrong
0
wrong merges, across every shape of input we tested

On 317 live GB companies recorded 2026-08-29, across seven shapes of input from a company number down to a name cut off by a field limit. Where the evidence would not carry a match, the row came back unresolved and named the column that would have settled it.

Zero wrong bindings were observed across the historical six-shape recording and the separate domain fixture. Observed correctness was 1.0000 on accepted outcomes; this does not guarantee an error-free new workload. 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.

Every figure, its population and what it does not show →

Files in the bundle, or fewer, with every absent ledger named and reasoned
12
Totals checked before a pack may ship; the two pair ledgers are checked against seven more
10
Rows written back to your CRM, ERP, MDM or warehouse
0
Re-fetchable receipt per decision the pack cites, refusals included
1

Properties of the artefact, checkable by opening it: the file list, the identities in manifest.json, the receipts re-fetched against the live API. These four are not accuracy figures. Every published rate, with its denominator, interval and fixture, is on the accuracy page, and every fixture behind those rates is one we built, not an external customer holdout.

On this page
the sequence

Three moments, and what you hold after each one

A Sprint is a scoped piece of work at a fixed price. It starts with one file of yours and ends with a directory of files you keep.

  1. 01

    Before anyone quotes — the composition table

    We read your file with the preview and send back what is in it: how many rows carry a registration number, how many a domain, how many a postcode, how many nothing but a name, and which of your populated columns we could not use. That conversation costs nothing and commits nothing.
  2. 02

    The run — your copy, through the public API

    We agree the depth and the window, and your file goes through the same public batch endpoint your own integration would call. Every row is decided once, and nothing is written to anything of yours at any point.
  3. 03

    The pack — a directory you keep

    Twelve files, assembled from the logs the finished run already wrote, so nothing in the pack is re-decided. If the totals do not reconcile it is not delivered, and you get a failed run and an explanation instead of a bundle you cannot check.

The composition table is the first thing back, before anybody quotes. It costs nothing, and it is the whole of the conversation until you decide whether to run a Sprint.

Start free
the bundle

Twelve files, and what each one is for

Three are one row per row of your file. Two are one row per pair of your rows the cascade compared. One is one row per company those rows landed on. Row grain and pair grain add up separately, each against its own totals, because a pair is two rows compared, not a row decided.

file · grain · what it holds
accepted_matches.csvrowRows the cascade bound to a company that already existed.

The decision column is always match; a first mint can never appear here, because a match and a mint are different claims about your row. Every accepted confidence in a complete pack sits at or above the 0.90 floor, and the pack re-checks that; it does not simply assert it. 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.

new_entities.csvrowRows where nothing matched, so a new company record was created.

Nothing in the catalogue or the registers supported a match. A mint is not a match: these are not calibrated candidate matches and are not subject to the confidence floor, which is why they get a file of their own.

unresolved.csvrowEverything the run did not decide, and the column that would have settled each row.

Including the rows the loader refused to submit at all, absorbed from the skipped log as disposition=not_submitted. Every row carries a typed reason_code, a prescription from a closed vocabulary of eight, and prescription_fields — the cheapest of your own columns that would have made the row resolvable. It names a field, never a predicted outcome.

golden_records.csventityWhich of your rows landed on which company, and the values that survived.

A membership projection, and it deliberately carries no per-field __licence, __permits_resale, __source or __source_as_of columns: a subset of citations is exactly the partial attribution an open register’s licence forbids. Those columns live in the separate export, golden.csv, handed over beside the pack. Do not treat the pack’s golden records as licence-bearing or redistributable.

withheld_matches.csvpairPairs of your own rows the cascade compared and would not call.

No verdict either way. Read settled_by_cluster before you read the count: false is the pair that went to the review queue and stayed on two separate companies; true is the pair the direct comparison could not call and the fold then merged through other edges, so both rows appear on one company in golden_records.csv. Only the first kind is a merge we declined to make, and the two files are what a data buyer cross-checks first.

do_not_merge.csvpairMerges the cascade reached and the guard refused, naming the field of yours that disagreed.

The mapping is total and fixed. A decisive identifier conflict — an LEI, a DUNS or a registration number disagreeing — is cannot_link. Corroborated weak evidence is advisory and never promotes, because two individually rebuttable signals contradicting at once is not a proven incompatibility. The conflict attributes are translated into your CSV’s field names, not our internal fact keys, so which column disagreed and which column would help are answered in one vocabulary.

reversal_map.csvactionWhich rows were grouped onto which company, so you can undo a grouping in your own system.

It is deliberately not a rollback manifest. The v1 contract requires every action to carry a before value, a precondition and a complete inverse operation, and a pack receives no prior state of your systems, so it cannot promise an inverse for an action it never saw. It is a map, not a guarantee.

decision_receipts.ndjsondecisionOne receipt per decision the pack cites, re-fetchable at a URL.

The verbatim decision body, re-fetchable at GET /v1/decisions/{id}. A blocked pair gets a durable receipt too, so a refusal redeems at a URL the same way a merge does. The receipts are not RFC 8785 canonical and carry no receipt_sha256: re-verification is by re-fetching and comparing, not by signature.

ATTRIBUTION.txtbundleThe attribution notice the open registers require.

Companies House is licensed under OGL v3.0 and INSEE SIRENE under Licence Ouverte 2.0, and both require attribution. The notice is written after the CSV and derived from that file’s own __source columns, so it names only the sources the delivered file actually contains, not every register we hold.

report.htmlbundleThe written report: self-contained, offline, and printable to PDF.

A white-label slot can carry your company’s name, set when the pack is assembled; left empty, it renders an unbranded page. The ledger coverage notes sit beside the table, not in a footnote; the cover prints the decision depth that actually applied; and the divergences from the v1 contract are a visible block, not an appendix. A report is never the system of record.

manifest.jsonbundleEvery total in the pack, and the list of what this version does not do.

not_audit_pack_v1: true with an enumerated divergence list, the absent and partial ledgers with their reasons, the four pair counters, the requested and the permitted run depth, the prescription counts, the reconciliation identities and any anomalies. Every number quoted anywhere else in the pack is read out of this file.

manifest.sha256bundleWritten last. Its presence is what says the pack finished.

The pack is assembled in a staging directory and promoted with this file moved last, so a promotion interrupted half-way leaves a directory with no completion marker, never one claiming to be finished. Stale files from a previous run are cleared first, so last run’s do_not_merge.csv cannot survive beside a manifest declaring that ledger absent. The checksums bind the parts of one pack and detect corruption; they do not prove publisher identity.

Why a ledger can be missing, and when a zero is allowed to stand

An absent ledger is absent, not empty

A ledger this version cannot produce is left out and named in the manifest with a reason. It is never shipped as a header with no rows. The incumbent-contradictions ledger is always absent: no incumbent proposal can be supplied yet. The two pair ledgers go the same way when a run had no pairs log. A header-only file reads as we found no dangerous merges, which is a different claim from we did not run the check.

A zero needs a denominator behind it

A pair ledger with no rows is written only when a pairs log exists, because the log carries the denominator. With it, zero means we compared pairs inside your file and refused none — a finding. Without it there is nothing behind the zero, so the file is not written, and the report says beside the table which of the two you are looking at.

Four things worth knowing before you open one of these in Excel
pack_format
A format marker naming the pack version, v0, is column 1 of every row of every CSV, not just the manifest. Customers open one file in Excel and forward it, and a self-identifying row is the only version marker that survives that.
formula escaping
Cells that begin with a formula character are escaped with a leading apostrophe in exactly five columns — legal name, trading names, entity legal name, phone and registered address — and in no others. The common case is not an attack, it is a UK phone number rendering as #NAME? in the column sold as the surviving golden value. Excel does not display the apostrophe. It is deliberately not applied to your source reference, which is your own key back into your CRM and must round-trip byte for byte.
run_depth
The manifest carries both the depth requested and the depth the plan permitted, and the report cover prints the shallower of the two. They can differ silently: a plan clamps a request downwards, and the free plan caps at the probabilistic stage. A Sprint mandated at adjudication that executed entirely at probabilistic would otherwise show up only as a pack full of raise-the-depth prescriptions that could not name the cap.
prescriptions
The closed vocabulary is eight values, and three of them name no column of yours on purpose: raise the depth, retry when the escalation stage is available, and none supportable. A cause of ours leads and does not silence the column. A retrieval column your file already carries and normalisation rejected is never prescribed — absent and unusable are different findings, and that row is told to repair the value, not to buy the column.

Every file above is one you keep, and we will tell you which of these ledgers your data will actually populate before a Sprint is scoped.

Start free
Educational download

Inspect an offline audit-pack tutorial

Download an audit pack generated by the product's real import, export and pack builders over eight fictional records in a disposable local test service. It shows how the files fit together before you run your own company file.

This is an educational fixture, not a customer delivery or a production run. Registry sources and research were disabled, the adjudicator was a stub and the requested depth was probabilistic. Supplied identities were not externally verified. Receipts belong to the discarded local service and cannot be re-fetched from the public API. The pack's manifest names its v0 limits; its checksums detect file changes and are not signatures.

  1. TUT-1 creates a local Tutorial Acme identity. TUT-2 and TUT-3 are accepted into that identity using their agreeing supplied registration number. Follow their source references across new_entities.csv, accepted_matches.csv and golden_records.csv.
  2. TUT-4 creates the second local identity, Tutorial Northbank. TUT-5 matches it. A newly created local identity is not a calibrated candidate match, which is why the two outcomes have separate ledgers.
  3. TUT-6 remains unresolved. TUT-7 and TUT-8 have no identifying business data and were not submitted by the loader. All three appear in unresolved.csv, so the eight-row input still reconciles.
Download the offline tutorial ZIP
the price

A fixed price, read off your own file

This is how a Sprint gets scoped. We run the preview, read the table with you, and price from what is in the file rather than from a row count. If your file is mostly bare names with no town, no postcode and no domain, the table says so before anybody has spent anything — and that is a conversation about which column to add, not a quote.

Underneath the Sprint is the metered product, and its whole price rule is five lines. Three of them are zero.

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.

Paid monthly subscriptions are purchased through Stripe in workspace Billing. Free needs no card. Usage is recorded separately and does not create automatic usage or overage charges.

The four plans, their allowances and the licence terms →

Send the file you want priced. You get a Sprint scope and a fixed number back, or a straight answer that your file does not need one.

Talk to us
who this is for

Built for a master you have to defend

If you already run a licensed company-data feed or a master data management platform, run Entity Bind alongside it. It is the independent decision and evidence layer: it tests, repairs and governs the matches those systems propose, and it will contradict them with a citation when they are wrong.

built for

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.

wrong for

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.

We publish what we do not claim — no SOC 2, no external validation of the accuracy figures, no customer holdout — beside the retention, erasure and tenancy detail a security review asks for. Read the trust page →

the gate

A pack whose totals do not reconcile is never delivered

Ten totals are checked at the end of a run and recorded in the manifest, and the two pair ledgers are checked against seven more of their own. Any failure sets the run status to failed, quarantines the action files, suppresses the completion marker and exits non-zero. The artefact never comes into existence.

The load-bearing one is the partition. Every input row lands in exactly one ledger — accepted matches plus new entities plus unresolved equals rows in — and the count of distinct source references equals the number of rows read. That is the first thing a data buyer checks.

Row grain — the ten keys the manifest publishes
rows_in_partition
Every input row lands in exactly one ledger. Accepted matches plus new entities plus unresolved equals rows in. This is the load-bearing one.
distinct_source_refs
The number of distinct source references equals the number of rows read. A file whose row count cannot be reconciled is the first thing a data buyer checks.
rows_in_covers_source
Every row of the source file is accounted for, not merely every row the run happened to see.
not_submitted_matches_skipped_log
The not-submitted rows in unresolved.csv are exactly the rows the import's skipped log recorded. A row cannot quietly disappear between the two.
golden_records_cover_targets
Every entity any accepted match or mint points at has a golden record in the projection.
golden_members_sum
The membership counts across golden records add up to the rows that reached an entity.
reversal_actions_match_clusters
The reversal map describes the same groupings the golden records do, action for cluster.
receipts_cover_decision_ids
Every decision id cited anywhere in the pack has its receipt in the bundle. Null on an offline pack, because the check did not run.
state_rows_done_matches
The resume file agrees with the ledgers about how much of the file was processed. Null when no state file was supplied.
every_unresolved_row_has_a_prescription
No unresolved row ships with an empty prescription cell. `none_supportable` is a first-class answer; a blank is not.
Pair grain — seven more, checked separately
pair_partition
The pair ledgers add up against the pairs log, and never against the row count. Folding one into the other would double-count your file.
withheld_pair_ids_distinct
No pair appears twice in the withheld ledger, including after a resumed import re-appended it.
do_not_merge_pair_ids_distinct
The same, for the blocked ledger.
pair_refs_resolve
Both sides of every pair row resolve to rows that exist in your file.
no_cannot_link_inside_one_entity
This one fails the pack outright. A pack that says we prevented this merge while its own golden records show the two rows collapsed onto one company is self-contradictory and must never ship.
pair_receipts_cover_decision_ids
Every pair decision cited has its receipt. Null on an offline pack.
pairs_complete
The list was not truncated and pair capture was reported. False makes the run partial and puts a banner on the report, because a claim about every dangerous merge cannot be made over a list that was cut.
checks that did not run

A check that did not run is recorded as null, never as true

The state identity is null when no resume file was supplied, and the receipt identities are null on an offline pack — one built without receipts, with no key and no call. Such a pack can therefore never be complete: it reports confidence_verified: false and a status of partial, because a receipt is the re-verifiable artefact and our own refs log is not.

The gate is tested from the failing side too: a deliberately corrupted state file was rejected with exit 1, the action files quarantined, the marker suppressed.

what we will not promise

We do not promise a number of dangerous merges prevented

We do not promise a count of dangerous merges prevented, and you should distrust anyone who does before they have seen your file. Zero is a normal, honest result: two rows carrying contradicting company numbers are refused outright and never reach the guard that would have counted them.

Why zero happens

A blocked pair needs the cascade to have proposed a merge first. Two rows carrying contradicting company numbers are answered as a non-match outright and never reach the guard that would have counted them, so the blocked ledger can be empty on a file that was full of conflicts.

The half of the guard the ledger cannot see

It carries the guard’s edge pass only. The clustering step runs the same guard again, per member against the cluster as it stands, and a refusal there detaches the member without writing a pair row. The manifest names that gap rather than leaving you a zero you cannot interpret.

A reported pair limit of zero

It means unreported, never no pairs found, and it fails closed: the completeness identity goes false, the run becomes partial, and the report carries a banner. A claim about every dangerous merge cannot be made over a list that was cut.

If a clean file is the outcome, that is what the report will say — and if the preview sees it first, you get that answer instead of a quote.

Start free
the version

This is v0, and its manifest lists everything v1 asks for that it does not do

Audit Pack v1 is a frozen contract — version 1.0.0, dated 20 August 2026, and marked in its own header as not implemented. It fixes a boundary; it does not claim a runner, a browser intake, a report renderer or a writeback connector exists. What ships today is v0: a post-processor over artefacts a finished import already wrote.

The divergences are enumerated in the specification, asserted in the manifest as a not-a-v1 flag with a list, and rendered as a visible block in your report. A cover note implying v1 compliance would invite you to hold us to v1’s receipt and rollback sections, and we would lose, correctly.

The eight divergences, named
Visible in the pack
  • A flat directory, where v1 mandates nested rows, projections, receipts, control and diagnostics folders.
  • A nineteen-column row header that deliberately does not reuse v1’s eighteen-column shape, and no v1 field name reused carrying a v0 meaning.
  • The withheld ledger is pair grain, not row grain, because there is no incumbent proposal input for it to be about.
  • Pairs are identified by a hash of the run and the sorted pair, not by lexicographic ordering, so the pack is not a function of your file’s row order — and a resumed import’s re-appended pairs de-duplicate.
Required by v1, absent here
  • Canonicalised, hashed receipts sufficient for offline decision replay. v0 receipts are re-fetched and compared instead.
  • A fifteen-column rollback manifest with before values, preconditions and inverse operations. v0 ships a reversal map, which is a different and smaller thing.
  • Counterfactual sensitivity and merge blast radius. Two of the differentiating analyses on the roadmap are simply absent.
  • Incumbent contradictions, and the authoritative-evidence and reviewer-confirmed bases for a cannot-link. Neither is wired, and the manifest says so.
what writes nothing covers

Nothing is written to your systems. It is not a shadow run.

The no-write property is scoped to your systems, and that scope is exact: nothing is written to your CRM, your ERP, your MDM or your warehouse. The pack proposes actions; it never applies them.

It is not the v1 no-write attestation. The logs the pack post-processes came from a real import through the public batch endpoint, which minted entities, recorded decisions and consumed credits in our own catalogue.

correction

Undoing a merge, and exactly where it stops

A merge is a decision with a receipt, and a decision with a receipt can be reversed. Today that reversal is a single-record detach: one record comes out of an over-merged company and becomes a fresh one containing itself. It is the safe correction for the common case — this CRM row belongs to a different company — and it is deliberately not a general split or a rollback of merge history.

single-record detach
POST /v1/entities/ent_01K2WQ4F7N0B3J5XGRV8YH6PDA/detach
Authorization: Bearer <review credential>
Content-Type: application/json

{
  "record_id": "rec_01K3XJ7PZ8H4A2E9T6KMSD1VRC",
  "reviewer_id": "analyst@example.com",
  "reason": "Registry evidence confirms a separate legal company"
}

409 Conflict
{ "error": { "code": "entity_detach_unsafe", ... } }   # before any write
The tenant comes from the verified credential and can never be supplied in the body. An ordinary ingestion key cannot detach a record: a separately issued review credential is mandatory, and its immutable stored name is the reviewer principal. The operation is free, and rate-limited with the review endpoints.

Six preconditions are tested first, and any one of them failing returns a typed conflict — entity_detach_unsafe — before any write happens.

The six preconditions, all of which fail closed
  1. 01

    The entity and the record are yours

    The verified key supplies the tenant; it cannot be named in the body. Another tenant's entity is indistinguishable from a missing one and returns 404, not 403, so the route cannot be used to confirm that a competitor is a customer.
  2. 02

    At least one other member remains

    Detaching the sole member is not a split, it is a rename with extra steps.
  3. 03

    Every member record is still present

    The tenant's record store has to hold all of them. A cluster with a deleted member cannot be re-partitioned safely, because the missing record's facts cannot be attributed.
  4. 04

    Every customer fact has an owner, and the selected record owns at least one

    Migration 0012 gave each fact the immutable id of the record that contributed it. Rows written before it are readable but were not backfilled — the contributing record cannot be reconstructed reliably, and assigning origins by value is not a safe backfill. An entity holding a legacy, unattributed customer fact therefore refuses detach outright and needs an explicit offline rebuild first.
  5. 05

    No remaining member has byte-identical resolution content

    Fingerprint bindings are keyed by content, not by record id, so sending two identical members to different entities would make replay ambiguous.
  6. 06

    No other entity already claims the record

    Membership is still stored as JSON, so the transactional adapter scans the tenant and refuses a duplicate. It will not mint a third membership.
What moves, what stays, what commits, and what it will not do
what moves
Facts contributed by the selected record move to the fresh entity. Identifier crosswalk and reviewer-derived alias facts are recomputed on both sides from the facts each side retains.
what stays
Unattributed registry, filing and research facts stay on the source entity. They are never discarded and never guessed onto the detached one: such evidence may well have been fetched while resolving the record being detached, but the schema proves its source, not which merge event introduced it.
what commits
One database transaction carries both entity memberships and fact sets, the blocking keys for both entities, the detached record's content-fingerprint binding recorded as human review at confidence 1.0, every stored client-reference link, invalidation of cached responses quoting the source, and an immutable non-match decision naming the reviewer, the reason, the acting credential and the detach itself as a feature.
what it will not do
Exactly one member per request. No arbitrary set split, no bridge analysis, no rollback to a historical cluster shape, no automatic reattach command, and the route is not HTTP-idempotent — after an uncertain network result, read the membership before retrying. The non-match receipt is a high-value negative label, but the matcher does not consume negative constraints yet: an exact-byte replay follows the corrected binding, while materially changed content runs the cascade again and could merge again.

These limits keep the workflow reversible without pretending the current data model can reconstruct merge history it never stored.

before anybody quotes

Preview reads your file first, and refuses to forecast

It is the first step of a Sprint, and we run it on the file you send. It sorts every row by the strongest retrieval signal that survives normalisation, counts what each column can and cannot contribute, and prices a ceiling on running the file. It reports what your file contains, never a predicted resolution rate: what identifiers a file holds is a fact about the file, and what the cascade will do with them is not. A test in the product fails the build if a predicted-coverage field ever appears in that output. It calls no API and spends no credits.

The full output: six tiers, the signals table, the two column blocks, two totals
preview, on an illustrative file
# identifier composition — 4812 rows
register_key             1004
catalogue_key             318
name_qualified           2447
name_only                 961
no_retrieval_signal        61
not_submittable            21

## signals (a row may carry several)
signal                    usable  in file  unusable
registration_number          988     1142       154  [register]
lei                           16       16         0  [register]
domain                      2231     2398       167  [catalogue]
postal_code                 1942     1942         0  [qualifier]

## populated columns we could not use
These rows were tiered as if the column were empty. The column is NOT
missing from the file, so asking for it again is the wrong ask.
registration_number       154 of 1142 values did not survive normalization  [register]
                          redacted shape of one: n/a
domain                    167 of 2398 values did not survive normalization  [catalogue]
                          redacted shape of one: <host>/<path>

## columns carrying nothing
mapped, and empty on every row: trading_names
not mapped, or empty and indistinguishable from absent: country, jurisdiction

rows with a usable name: 4791 (a name column was populated on 4812 rows; 21 of those normalized to nothing)
rows the API will accept: 4791
The layout is the preview's own, including the two blocks about columns. The counts are illustrative, not a customer's.
What the composition table is telling you
six tiers
Register key, catalogue key, name qualified, name only, no retrieval signal, and not submittable. The tier is computed over the normalised record, because normalisation is what retrieval sees: a domain column holding N/A is an identifying field to the admission check and nothing at all to the blocking index, and a preview that counted it would have quoted a file it had mis-read.
three kinds of empty
Unmapped — send us the column. Mapped and empty on every row — the column is there and nobody fills it in. Populated with values that did not survive normalisation — you already have the data, in a shape we cannot read. The customer action differs completely between the three, so the report never collapses them into one number.
the spend line
The estimate is a conservative ceiling on a partly unmeasured basis, and it is printed under a provenance sentence that says so. That sentence travels with the figure into any proposal. A model identifier the rate card has never heard of is priced at the most expensive published rate and named in the unpriced list.
what it costs you
Nothing at all. Knowing what is in your file before anybody talks price is a reasonable thing to want, and it is why this step runs separately from the one that spends.
your own upload
Your Entity Bind workspace has an upload of its own, and it is a run rather than this preview. It takes a UTF-8 CSV of at most 1,000,000 bytes and, by default, 500 US or GB company rows, shows you five of them so you can map your columns — name is required; domain, country, registration_number, jurisdiction, lei, ticker, exchange, address, city, postcode and client_ref are optional — and works on the Free plan in Matching mode, which makes no registry or model calls; Matching and registry checks needs an active paid plan purchased in workspace Billing. Starting it stores the rows in your workspace, and a finished run downloads as CSV or JSON with its decision receipts.

We will run the preview against your file and send the table back before anyone talks about price.

Start free
Ask about the packanswers from this page and the contract behind it
Ask about the pack, the Sprint, the gate or the correction path. I answer from what is published, including the parts that are not built yet.

Prefer to put it to a person? Talk to us instead →

how we run it

Five steps, each one consuming what the last one wrote

These are the steps we run on your file during a Sprint, in order. Wherever a step calls the service, it goes through the public HTTP API rather than the database, and nothing reaches a private path — so a successful import is evidence that your own integration will work. Three of the five can run without calling the API at all: the preview, the duplicate report and a pack assembled without receipts.

  1. 01

    Preview

    Qualify the file before anybody quotes it
    01 · preview
    reads    leads.csv, with your columns mapped
    calls    nothing
    returns  the composition table and a spend ceiling
    What it costs, what comes back, and what to watch

    Calls nothing and spends no credits, so it costs you nothing.

    The composition table shown in full further up this page, and then a SPEND BASIS: line and the spend ceiling as JSON — or the whole thing as one JSON report.

    It is the only step that runs before there is a commercial relationship at all, which is why it is separate from the one that spends. We run it on the file you send, and the table is what comes back.

  2. 02

    Import

    Resolve the file through the public batch endpoint
    02 · import
    reads    leads.csv, keyed on your crm_id, Notes mapped to context
    calls    POST /v1/batch, at the depth we agreed with you
    writes   leads.csv.refs.csv, .skipped.csv, .pairs.csv, .state.json
    What it costs, what comes back, and what to watch

    The only step that spends. Abstentions and byte-identical replays are free.

    Four artefacts beside your file — leads.csv.refs.csv (your key to our record and entity ids, with the decision, the stage, the decision id and the review id of every abstained row), leads.csv.skipped.csv, leads.csv.pairs.csv and leads.csv.state.json — then a JSON run report: rows in, rows skipped, entities out, duplicate clusters, largest cluster, automation rate, abstained, pairs withheld, pairs blocked, replayed and credits charged.

    Your own key column travels through as client_ref and comes back on every row, so the output joins to your data without matching on our ids. The automation rate’s denominator is rows the API answered, not rows in the file: a row the loader refused to submit was never offered to the cascade. The run is resumable, and it refuses to resume at a different depth, because two policies in one refs log would make a prescription mean one thing in the first half of a pack and another in the second.

  3. 03

    Duplicate report

    Answer the question the buyer came with
    03 · duplicate report
    reads    leads.csv.refs.csv
    calls    nothing
    writes   clusters.csv, your rows grouped by the company they landed on
    What it costs, what comes back, and what to watch

    Reads the refs log the import already wrote, so it calls nothing and spends nothing.

    A cluster summary as JSON, and a CSV grouping your resolved rows by the company they landed on, with members labelled by your key values, not ours. A cluster is two or more rows by default. Run over the whole tenant catalogue instead of one file’s refs log, the members are record ids.

    This is not a re-resolution. It reports what the cascade already decided: it submits nothing, changes nothing and costs nothing. “These six of your rows are one company” is the sentence a RevOps or MDM buyer opened the conversation with, and it is answered in their own identifiers.

  4. 04

    Export

    Take the golden records out, with their citations
    04 · export
    calls    GET /v1/entities/export
    writes   golden.csv, with provenance and licence columns
    writes   ATTRIBUTION.txt
    What it costs, what comes back, and what to watch

    Streams an endpoint you can call yourself. No resolution, no adjudication, no spend.

    A fixed, documented column set that does not vary with your data, so whatever loads this file will keep loading it. Per-field __source, __url, __source_as_of and __last_checked_at columns, per-field __licence and __permits_resale, and an ATTRIBUTION.txt.

    Provenance and licence are on by default, and licence handling fails closed: an unreadable licence reads as not-resellable, never as permitted. The governance columns are off by default, because they export names of natural persons. Personal identifiers — date of birth, nationality, country of residence, address — are dropped at ingestion and are never in the file whatever the setting says.

  5. 05

    Audit pack

    Assemble the bundle, reconcile it, and render the report
    05 · audit pack
    reads    leads.csv.refs.csv, leads.csv, leads.csv.state.json, golden.csv
    calls    GET /v1/decisions/{id}, once per decision the pack cites
    writes   pack/ — the bundle, with manifest.sha256 written last
    What it costs, what comes back, and what to watch

    Re-decides nothing. A pack rebuilds from a finished run without re-spending anything.

    The bundle, and a JSON summary read straight back out of its own manifest: pack format, the not-a-v1 flag, run id, run status, totals, reconciliation, receipts, rights, absent ledgers, pair totals, prescriptions, anomalies, whether the report was written, and the file list.

    A pack is built from the import’s refs log into an output directory of its own, and its run id is given explicitly or read from the state file or the source file. A named input file that does not exist is a hard error, never a silent skip: naming a file is a claim that it is there, and a typo in the state file’s path would otherwise publish a null identity as a check that could not be run. A pack can also be built entirely offline, without receipts, with no key and no call.

What the fifth step prints, on the published verification fixture
the fifth step, on the verification fixture
{
  "not_audit_pack_v1": true,
  "run_status": "complete",
  "out_dir": "pack/",
  "totals": {
    "rows_in": 8,
    "accepted_matches": 3,
    "new_entities": 4,
    "unresolved": 1,
    "entities_out": 4,
    "duplicate_clusters": 2
  },
  "reconciliation": {
    "rows_in_partition": true,
    "distinct_source_refs": true,
    "golden_records_cover_targets": true,
    "receipts_cover_decision_ids": true,
    "every_unresolved_row_has_a_prescription": true
  },
  "absent_ledgers": {
    "incumbent_contradictions.csv": "no_incumbent_proposal_input"
  },
  "report_written": true
}
Abridged; the manifest carries every total and every identity, and this is the summary the step prints back out of it. The counts are the published end-to-end verification run: an eight-row repository fixture with three Monzo spellings sharing a company number, two Revolut, one Starling, one name-only consultancy and one empty row, which resolved to four companies with two duplicate clusters and partitioned 3 + 4 + 1 = 8. An internal fixture, not a customer delivery.
Two things to plan for: the window, and your notes column
planning the run

The import is not a queue, and your notes column is worth mapping

Batches go through the batch endpoint one at a time, and a 300,000-row file is an overnight run — plan the window, not the throughput. Within one batch the order of rows no longer changes the answer; across batches it still does, because batch two has to see the companies batch one created.

If your file has a Notes or Description column, map it into context: it reaches the adjudicator as caller-supplied evidence, not as an identifying field, and a row carrying nothing but a name is what the cascade abstains on most. A row carrying only context is still skipped, because free text cannot say which company a row is.

merge safety sprint

Start with your own company records.

A Sprint runs over the public API on a copy of your file and writes nothing to your CRM, ERP, MDM or warehouse. You keep every file it produces.

300 credits. No card.