NeuralKite: acquisition intelligence that can explain every address it gives you
A live acquisition-intelligence product for fragmented UK private markets. At its centre is an email discovery and verification engine that grades every address and keeps its reasons. Relevant to AI engineering, private-equity and finance roles.

The landing page above is the part of NeuralKite anyone can see. Most of the engineering sits behind it: a pipeline that finds the people who own and run a small private company, works out how to reach them, and says plainly how far each answer can be trusted.
The problem
A fragmented sector is made of many small private companies, and small private companies publish very little about themselves. The live site names three such sectors: UK managed service providers, accountancy firms and IT services. Screening one of them for acquisitions means finding the companies, working out who owns them, judging each against a thesis, writing the case up, and then reaching an owner who rarely publishes a direct address.
NeuralKite is a product for that sequence. It maps private companies, scores targets against an acquisition thesis and produces decision-ready dossiers, through five workflows: ownership discovery, screening, thesis-fit scoring, shortlisting and export. The hardest part to do honestly is the last step before outreach: an address that is a guess has to be labelled as a guess.
StatusLive product; customer and usage figures are not published. Nothing on this page should be read as a claim about customers, revenue or launch stage.
What I did
I built the product end to end. Everything described below is in the codebase; I have left out anything the code does not do.
- Wrote the email discovery and verification engine from the socket up, on Node’s own net and dns modules: people extraction, email mining, format inference, a catch-all control, an SMTP recipient check, protocol probes, risk tiers and a rationale trail, streamed to the browser as it runs.
- Integrated the Companies House API, with a rules-based sector classifier, a website finder and a governance-signal generator on top of it.
- Stood up a self-hosted PostgreSQL warehouse, and loaded and analysed a 2.8-million-company Crunchbase dataset in it.
- Built the Claude agents, on the Anthropic SDK, that score companies against a thesis, write the dossier and draft a short outreach sequence.
- Built sending through the user’s own SMTP accounts and through a Postal mail server I host, and the application around all of it in Next.js App Router and TypeScript, with Prisma, NextAuth and Stripe, deployed at neuralkite.com.
How it is layered
| Layer | Component | What it does |
|---|---|---|
| Interface | Next.js App Router, TypeScriptNextAuth, Stripe | Sign-in, billing, target lists, dossiers and sequences. The discovery and verification views read a stream of events and draw each step as it lands. |
| Streaming | Server-sent events from /api/email/discover-stream and verify-streamverify-bulk for a whole dossier | One event per step: the MX lookup, the catch-all test, each format tried, each SMTP reply code. Proxy buffering is switched off in the response headers so events arrive as they happen. |
| Agents | Claude on the Anthropic SDKtargeting, research, discovery, composer | Score companies against the thesis on five axes, read the scraped pages into a dossier, catch the people that structured extraction missed, and draft a three-step sequence. Claude Haiku does extraction; Claude Sonnet does scoring, dossiers and drafting. |
| Discovery and verification | discovery.ts, email-protocol-probes.tsNode’s net and dns modules, no third-party verifier | People extraction, email mining, format inference, MX and provider classification, the catch-all control, the SMTP recipient check, the protocol probes, the risk tier and the rationale trail. |
| Registry intelligence | Companies House REST APIclassifier, domain discovery, governance signals | Officers and persons with significant control, paginated. A rules-based sector classifier, a name-to-website finder, and deal-trigger signals computed from tenure, ownership and board changes. |
| Scraping | Playwright, Cheerio | Reads the homepage, about, team and careers pages and recent posts, keeping both the HTML and a cleaned text version, and reports each page as a stage. |
| Data | Prisma on PostgreSQLa separate pg pool to the Crunchbase warehouse | Application data in one database; the 2.8-million-company Crunchbase load in another, queried in SQL by the targeting agent. Companies House candidates are staged, then promoted to companies under an id that cannot collide with a Crunchbase one. |
| Sending | Nodemailer over the user’s own SMTP accounta self-hosted Postal server | A connection test when an account is added, presets for the common providers, a daily limit per account, paced batches, and delivery, open, click and bounce events back from Postal by webhook. |
| Secrets | AES-256-GCM, Node crypto | Stored SMTP passwords are encrypted with a fresh random IV per value and an authentication tag, and decrypted only at the moment of sending. |
Finding the person, and grading the address
A dossier is only useful if it ends in someone to talk to. The discovery module takes the pages the scraper has read and returns a list of people, each with a role, an address where one can be found or inferred, a risk tier and the chain of evidence behind it.
From a team page to a graded address
Three passes, in this order in the code. The company’s pages yield people and a house format for addresses. The domain is questioned once. Then each person’s address is built, checked and graded, and the reasons are kept.
People, from the most structured source to the least
Four strategies run over each page and their results are merged by name: the JSON-LD a site declares about its own people, then schema.org microdata, then the DOM (team-member elements and LinkedIn profile anchors with a name beside them), and last a Claude pass over the cleaned text that catches layouts the heuristics miss and is told to leave out testimonials and customers. A name has to survive a validator before it is kept, and each person records which method found them and on which URL.
Titles are classified into owner, director, executive, partner, senior or employee. Principals are sorted to the top, because in an acquisition the principal is the person the dossier is for.
Addresses the company has already published
Every scraped page is mined for addresses: mailto links, plain text, and the obfuscated forms people write to dodge scrapers. An address is kept only if it sits on the company’s own domain, so a web agency’s address in a footer never enters the record. Each mined address is matched to a person by comparing its local part with their cleaned name, and keeps the URL it came from.
Most companies build addresses one way. Every matched pair of person and published address is tested against the common formats, from first.last to an initial and surname, and each match is a vote. The winning format becomes the domain’s fingerprint, stored with the samples behind it. Shared role mailboxes such as info or sales are excluded from the vote, and honorifics and post-nominal letters are stripped from names before any address is built from them.
What the SMTP check does, precisely
This is the part that deserves plain language. The check opens a connection to the domain’s mail server and speaks the opening of an ordinary delivery: greeting, EHLO, a sender, then the address in question with RCPT TO. The server’s reply to that last command is the whole result, and the client then closes the connection. It never sends the DATA command: no message is composed, none is transmitted, and nothing arrives in anyone’s inbox. An acceptance means the server recognised the recipient; a refusal means it did not; anything else is recorded as inconclusive. It is still a request to someone else’s server, so candidates are tried one at a time, the loop stops at the first acceptance, and on a catch-all domain no candidate is probed at all.
The control that makes an acceptance mean something
Some mail servers accept every recipient and sort it out later. On those, an acceptance proves nothing. So before any real address is checked, the module runs the same recipient check once per domain on a control address: a random local part that cannot belong to anyone. If the server accepts it, the domain is catch-all. If it refuses, the server is selective, and only then is a later acceptance on a real address allowed to count as verification. If the control gets no clean answer, that is recorded too, and a later acceptance is described as supportive, not as proof.
The mail servers are also classified by provider, and each class carries a note that goes into the trail: catch-all is rare on the big hosted suites, so the control is reliable there; filtering gateways often refuse or throttle probes from unfamiliar addresses, so a refusal from one is weak evidence. The code says so in the record instead of pretending otherwise.
Protocol probes: bounded, parallel and quiet when they fail
Alongside the control, a set of probes runs in parallel against the domain. The most useful is DKIM selector fingerprinting: a DKIM key lives at a selector chosen by whoever sends the mail, vendors pick recognisable ones, and each well-known selector that resolves to a real key names a vendor the company sends through. The SPF probe maps its include chain to vendors the same way; the rest read the domain’s mail policy records. Every probe is raced against a short timer, returns an empty result on any error and never throws, so one slow name server cannot hold up or break a run.
Risk tiers, and what each one means
All of that is reduced to one tier per address. The order below is the order the code tests them in.
| Tier | Assigned when | What it means for the person using it |
|---|---|---|
| Verified, two sources | the address is published on the company’s site, and a selective server accepted it | the strongest case: the company printed it and its own mail server recognises it |
| Verified by SMTP | the address was built from a format, and a selective server accepted it | the mailbox exists; nobody has published it, so the person match rests on the name |
| Site only | the address is published on the site, but SMTP did not confirm it | trustworthy as a source; the server was catch-all, blocked the probe or gave no clean answer |
| Inferred format | the domain is catch-all, and the format was voted by real addresses found on the site | an informed guess: right format for this company, mailbox unconfirmed |
| Catch-all | the domain is catch-all, and there was no format evidence | a guess, labelled as one; first.last is used because nothing better is known |
| Format guess | built from a format, and either no candidate was accepted or one was while the control was inconclusive | the weakest usable tier: plausible, and not confirmed by anything that counts |
Two notes ride alongside the tier: an address that turns out to be a role mailbox is flagged as reaching a team, not a person, and an address on a consumer mail service is flagged as probably personal. The numeric confidence beside each tier is a hand-set heuristic that orders results sensibly. It is not a measured accuracy and the product does not present it as one.
The rationale trail
Every signal is appended to the person’s record as a typed, dated step with its source URL or sample addresses and the SMTP reply where there is one. The wording changes with the evidence: the same acceptance produces three different sentences depending on whether the control was refused, accepted or inconclusive. The site, the server, the control, the provider class and the probes are separate witnesses, and the verdict records which of them agreed.
Showing the work live
Discovery and verification stream to the browser over server-sent events: the mail-server lookup, the catch-all test, each format as it is tried with its reply, and for a single address the SMTP conversation itself, step by step. The dossier run streams the same way, which is what the demonstration below shows.
The acquisition-intelligence layer
- Mapprivate companies in fragmented UK sectors: MSPs, accountancy firms, IT services
- Ownership discovery
- Screening
- Thesis-fit scoringtargets scored against an acquisition thesis
- Shortlisting
- Dossier and exportdecision-ready dossiers, written by LLM agents, then exported
Companies House, and a classifier that shows its sums
The registry client calls the Companies House API for a company’s profile, its officers and its persons with significant control. Bulk registry rows are scored by a rules-based classifier that uses only registry fields: SIC codes in tiers, positive and negative name patterns, the accounts category as a proxy for size, company type and age. Every point gained or lost is written to a list of reasons, so a score can be read back as the SIC code, the name match and the accounts category that produced it.
Registry data has no website field. The domain finder strips the legal suffixes from a company name, generates a handful of likely UK domains, fetches them in parallel and checks whether the page title matches the company. All the candidates it tried are returned with the answer, so a wrong match can be seen and corrected.
Governance signals
Officer and ownership records are turned into the things an acquirer looks for: director tenure, ownership concentration, a founder-led firm, a small board, a corporate owner, and dated signals such as a new director appointed beside a long-serving one as a possible handover. Each signal carries a score, a confidence and the registry facts behind it, and the dossier prompt passes them to the model labelled as official records.
The warehouse, the agents and the dossier
The targeting agent turns a set of criteria into SQL against the Crunchbase warehouse, then has Claude score each batch of companies on thesis fit, ownership clarity, scale potential, financial signals and risk profile, with its reasoning. The research agent scrapes the company’s site, runs people discovery, and asks Claude for the dossier, with the governance block included when there is one. A company with no website gets a dossier marked failed with the reason, not an invented one.
Sending, and the credentials that go with it
Outreach goes out from the user’s own mailbox, under a daily limit, or through a Postal mail server I host, which returns delivery, open, click and bounce events by webhook. The composer drafts an initial message, a follow-up and a closing one from the dossier. SMTP passwords are encrypted before they reach the database and decrypted in memory only when a send needs them.
Decisions that mattered
- Ask the domain before asking about the person
- The catch-all control runs once per domain, first. If the server accepts a mailbox that cannot exist, every later acceptance is worthless, so the code skips per-candidate probing entirely and goes straight to the inferred format, labelled as such.
- Verified only means verified on a selective server
- The verified flag is set when the server accepted the address and the control was cleanly refused. An acceptance with the control inconclusive is kept, at lower confidence, and described in the trail as supportive.
- A refusal is not proof of absence
- Many providers block recipient checks from addresses they do not know. When every candidate is refused the module does not mark the person unreachable. It falls back to the inferred format and writes down that nothing confirmed it.
- Structured data first, the model last
- What a site declares about itself in JSON-LD is more reliable than anything inferred, so it is read first and wins on conflict. The model runs last and only adds people nobody else found.
Evidence that it worked
The evidence here is the software. There are no customer, revenue or usage figures on this page, because none are published, and no accuracy figures, because I have not measured any that I would stand behind.
| Measure | Value | Context |
|---|---|---|
| Deployment | live at neuralkite.com | publicly accessible with sign-in and demo booking; verified 15 September 2026 |
| Verification engine | six risk tiers, each with a written rationale | what the code computes for every address; a description of the output, not of how often it is right |
| Data | 2.8 million companies | Crunchbase dataset loaded into the self-hosted PostgreSQL warehouse and analysed; a dataset size, not a usage figure |
| Demonstration | recorded run, 82 seconds | the company-research stage run on a fictional example company; not real data |
| Usage | not published | launch stage, customer numbers and revenue are not stated anywhere on this site |
A research run, stage by stage

Limitations and status
NeuralKite is a live product whose commercial figures are not published, so this page shows what the software does and nothing about who uses it. No real person, address or company from the application appears here.
The verification engine has limits that no engineering removes. A mail server can accept a recipient and discard the mail later, and the large providers often refuse to answer at all, so many addresses end in the lower tiers, correctly. I have not published an accuracy figure because I do not have a labelled set that would make one honest. The registry classifier is tuned for managed service providers and IT services; the other sectors rely on the thesis scoring and the dossier.
What I learned
The valuable output was never the address. It was the sentence explaining how far to trust it.
An unverifiable guess presented as a fact costs the user a bounced message and some of their sender reputation. The same guess with its tier and its trail is a decision they can make themselves. I also learned how much of network programming is deciding what silence means: a timeout, a deferral and a refusal are three different facts, and most of this module is the care taken not to confuse them.
The live product
neuralkite.com (opens in a new tab) is the live product, with sign-in and demo booking.