TAM building advanced
Build your total addressable market (TAM) from several sources as one account layer in Cargo: one brief goes to Sales Navigator, AI Ark lookalikes, Exa and Apollo, Cargo merges every company into one account, each is classified into your own categories, flagged in or out of scope with the reason and marked net new or already in your CRM, and the report estimates what no source found.
Set up the tam-building-advanced cookbook in this project.
1. If the Cargo CLI is not installed yet, follow every step in https://api.getcargo.io/INSTALL.md
2. From the Cargo project, run: cargo-ai project add cookbook/tam-building-advanced
3. Then follow .claude/skills/tam-building-advanced/SKILL.md. Stop for my approval before any paid call, and stop at "cargo-ai project plan" before deploying anything.$cargo-ai project add cookbook/tam-building-advancedSay this to your agent
Build our TAM from Sales Navigator, AI Ark, Apollo and Exa for B2B software with 51 to 1,000 people in North America and Western Europe, and tell me how much is net new.
Illustrative output #
Fictional records, to show the shape of what comes back.
| TAM | Companies |
|---|---|
| In scope | 18,420 |
| Already in your CRM | 4,960 |
| Net new | 13,460 |
| Flagged out of scope (with reason) | 21,305 |
| Needs review (thin page) | 1,140 |
| Source | Found | Found by no other source | Added in this order |
|---|---|---|---|
| Sales Navigator | 16,210 | 9,380 | 16,210 |
| AI Ark | 5,640 | 910 | +1,250 |
| Apollo | 7,980 | 640 | +730 |
| Exa | 1,520 | 120 | +230 |
Coverage: Sales Navigator × Apollo puts the market at 22,100 or more, so the 18,420 in scope are
about 83% of it at most. Top account: northwind-analytics.example, B2B SaaS, 340 people, found by
all four sources, net new.
What you will be asked #
| Input | Why | |
|---|---|---|
| icp | Looked up | Four source filters and the classifier answer to it. If they answer to four different readings, the TAM is four markets |
| market_minimum | Looked up | A source missing one bills for the whole dimension, and the sources stop searching the same market |
| categories | Looked up | They are what every company is classified into. Your CRM’s values make the TAM and the CRM speak one language |
| sales_navigator_searches | Looked up | One search serves at most 1,000 accounts; past that the rest of the market is silently not there |
| lookalike_seeds | Looked up | AI Ark’s lookalikes are only as good as the seeds; a guessed customer is not evidence |
| exa_queries | Looked up | Exa reaches companies filed under labels nobody filters on, but only for what the queries describe |
| crm | Looked up | It decides what “net new” is measured against |
| limits | Looked up | Every paid source bills per returned record: the limits are the invoice |
Done when #
- the ICP came from the context or was researched and approved, and the three criteria are in all four sources and
settings.ts - the categories came from the CRM’s industry field or
icp.md, and every row oftam_companiescarries one of them - every Sales Navigator URL is under 1,000 accounts and carries
INDUSTRY,COMPANY_HEADCOUNTandREGION - the sources were synced and profiled one at a time, in order, each piloted on five accounts first
- every account a TAM source found has one profile with a LinkedIn company id and a status, no LinkedIn company id sits on two accounts, and every account holding two LinkedIn ids was read
- the report gives the TAM, net new and already in the CRM, the per-source table, the coverage estimate (or why it is too thin), and actual credits against the estimate
- nothing was written to the CRM
node --import tsx evals/contract.mjspasses against the adapted resources
What it costs #
Read this before pointing the skill at a market-sized brief.
Counting is free on AI Ark and a fixed fee per search on Sales Navigator. Count every candidate before anything syncs.
Sourcing bills per returned record on Sales Navigator, AI Ark and Apollo, and per returned result on
Exa. Each source’s limit (or the number of queries times exaResultsPerQuery) is its invoice.
Enrichment bills once per company, not per source. Cargo merges the sources before the play runs, so a company four sources found is one account and one profile: one LinkedIn enrich and one classification. Estimate them on the expected number of distinct companies, which is well under the sum of the sources. The merge itself is free.
Fetch every live price immediately before the approval with cargo-ai connection integration get for
salesNavigator, aiArk, apolloio, exa and linkedin, and record the lookup time. No price is
written in this skill on purpose: they change.
The sources carry no schedule. See refresh above for what a re-sync really costs.
To build a total addressable market (TAM) that is as complete as you can make it, give the same brief to four sources, merge everything they find into one account per company, classify every company into your own categories, and measure how much of the market you still miss. This pipeline does that in Cargo: Sales Navigator, AI Ark, Exa and Apollo land in their own models, Cargo’s account unification merges them (with your CRM’s companies) into Unify Accounts, one play reads each account once from LinkedIn and classifies it, and the report says what is net new and estimates the companies no source found.
cargo-ai project add cookbook/tam-building-advanced
tam-building is the single-source version: one AI Ark search, one model. Start
there when one source and a list are enough. Come here when the question is
“how much of my market do I actually have?”
Why four sources #
No data provider is your market. Each one answers the brief its own way, and each misses companies the others find:
| Source | Its job |
|---|---|
| Sales Navigator | The census: every company LinkedIn files under your filters |
| AI Ark | Lookalikes of the customers you name |
| Apollo | A second census, in a different database |
| Exa | Search by meaning: companies filed under labels nobody filters on |
In the build this pipeline is written from, the biggest source held most of the market, and the companies it missed included a share of the accounts the team most wanted to win. Combining is how you find them; the overlap is how you measure what is still missing.
How it works #
flowchart TD
icp["context/icp.md + settings.ts<br/>brief, your categories, Exa queries"]
sn["tam_source_sales_navigator<br/>LinkedIn id"]
ark["tam_source_ai_ark<br/>domain + LinkedIn page"]
exa["tam_source_exa<br/>domain"]
apollo["tam_source_apollo<br/>domain + LinkedIn page"]
crm["your CRM companies"]
accounts["Unify Accounts<br/>Cargo merges: one account per company"]
play["tam_profile_companies<br/>LinkedIn enrich + classify, once per account"]
profile["tam_companies<br/>LinkedIn id + page + domain, category, status"]
report["Report: net new vs CRM · per source · coverage estimate"]
icp --> sn & ark & exa & apollo
sn & ark & exa & apollo & crm -->|"declared unification"| accounts
accounts --> play --> profile
profile -->|"unifies back: joins the sources"| accounts
accounts --> report
- One brief, four translations. Industries, company size and countries are written into every source, so they search the same market.
- Cargo merges, not a workflow. Every source model declares how it identifies a company, and Cargo’s account unification merges them into Unify Accounts, normalising LinkedIn URLs and domains itself. Your CRM’s companies are in the same accounts, so “already in the CRM” is free.
- One profile per company. One play, a build step run as a batch after
each source sync, reads each account a TAM source found: a LinkedIn company enrich (the LinkedIn page any source gave, else
the domain) and a classification, written to
tam_companies. A company four sources found is enriched once. - The profile joins the sources. Sales Navigator knows a company by its LinkedIn id, AI Ark and Apollo by its domain and LinkedIn page name, Exa by its website. The profile row holds the LinkedIn id, the page, LinkedIn’s domain and the account’s own domain, and unifies back into the same account, so they become one.
- Your categories, not theirs. Each company is classified from its own
description into the categories in
settings.ts, ideally your CRM’s industry values. A provider’s industry label is never trusted. - Flag, never delete.
statusandexclusion_reasonare computed ontam_companiesfromsettings.ts: services firms, subsidiaries, companies out of size or country stay, with the reason. - Measure what is missing. Every account lists which sources found it. The report estimates the market from the Sales Navigator × Apollo overlap (capture-recapture) and gives your coverage.
| File | Resource | Role |
|---|---|---|
infra/connectors/*.ts | defineConnector | the four sources and LinkedIn, on managed connections |
infra/models/tam-source-*.ts | defineModel | the four sources: filter, budget, no schedule, declared unification |
infra/models/tam-companies.ts | defineModel | one profile per company; status and exclusion_reason computed |
infra/models/accounts.ts | defineModel | Unify Accounts, adopted, nothing else declared on it |
infra/plays/profile-company.ts | definePlay | the only play: LinkedIn enrich + classify, once per account |
infra/scripts/company-domain.ts | defineScript | the domain: LinkedIn, the account, or the short link’s landing |
infra/settings.ts | (constants) | size band, countries, your categories, the Exa queries |
Why these choices #
- Cargo’s unification is the merge. It already normalises LinkedIn URLs and domains and follows a chain of keys across rows. A workflow doing the same is slower, costs runs, and builds a second account layer next to the one every other pipeline reads.
- The LinkedIn enrich is the identity. It turns whatever a source gave (an id, a page name, a website) into the same three keys and the same columns, so a company Exa found reads exactly like one Sales Navigator found.
- The domain is the best evidence available, never a short link. LinkedIn’s when it is a real domain; else the account’s; else where LinkedIn’s short link actually lands. A short link written as a domain is a key unrelated companies share, and Cargo would merge them.
- Sources run one after another. Profile one source’s accounts before the next syncs, and the next source mostly merges onto accounts that already have a profile, for free.
- The overlap is kept. Every account lists which sources found it. The number is a fit signal, and the Sales Navigator × Apollo overlap is the coverage estimate.
- Built once, refreshed at most quarterly. A source sync re-bills every
record, including the companies already in the TAM, and a TAM does not
need to be fresher than three to four months. By default nothing is
scheduled: re-source when you decide to, or take the
refreshvariation’s quarterly cron. - Nothing is written outside Cargo. Pushing the net new into the CRM is a separate, approved write.
Placeholders (edit before deploy) #
- The ICP and categories: copy
context/icp.mdinto the project’scontext/, or keep the one there. infra/settings.ts: size band, countries, your categories, the Exa queries.- Each source’s filter and
limit:infra/models/tam-source-*.ts. Sales Navigator URLs come fromextract-sales-navigator-searchwhen the market is over 1,000 accounts. - The CRM: nothing to declare. Its companies model must unify into
Unify Accounts (HubSpot and Salesforce companies do by default); the report
reads its key in each account’s
ids.
Verification #
node --import tsx evals/contract.mjs
cargo-ai project types && cargo-ai project check && cargo-ai project plan
evals/acceptance.md is the line-by-line acceptance test.