From a Project Brief to a Search
Briefs arrive as prose. This guide turns one into a request body, end to end. Someone writes down who they want to talk to: a vendor whose customers matter, a seniority band, a list of job titles, a page of priority accounts. Someone else has to make that a filter set.
The brief below is synthetic, and so is its phrasing; real briefs word these headings a dozen different ways. What is consistent is the set of things they specify, and that is what the translation hangs on.
Enterprise data platform selection
We want to understand how large organizations pick, migrate between, and consolidate data platforms. Ideal conversations are with people who have personally run or sat on one of those evaluations.
Platform in focus — Snowflake Data Cloud
Nearby products count too — anything in the Data Warehousing category
Where they work — enterprise data infrastructure: warehousing, analytics engineering, BI tooling, and the platform teams that own them
How senior — Director, VP, SVP, or C-level
Which teams — data engineering, analytics, platform and infrastructure engineering, enterprise applications, procurement and vendor management
Titles we tend to see (a flavour, not a checklist) — VP of Data, Director of Data Engineering, Head of Analytics, Chief Data Officer, Director of Data Platform, Enterprise Data Architect, Procurement Lead
Accounts to start with — Salesforce, Henry Schein, Patterson Companies, then widen from there
How recent — still in the role, or left it within the past 12 months
Where — United States
Read the brief as four questions#
Every line above answers one of four questions, and each maps to a different part of the request. Sorting the prose into these buckets first is most of the work:
| Question | In the brief | Becomes |
|---|---|---|
| Anchor — what makes a company relevant? | The platform in focus, and the category around it | vendorgraphics.aggkProductId |
| Persona — who at that company? | How senior, which teams, and the titles they expect to see | workExperience.seniority / role / subrole / jobTitle |
| Scope — which of those count? | Geography, how recent the role must be, the sector they sit in | person.country, employmentStatus, endDate, company.industry |
| Priority — who do they want first? | A short account list, plus an instruction to widen past it | A separate tier — not a filter. See step 4. |
Step 1 — resolve the names to IDs#
The brief names a product and a list of companies. The product must become an ID before you can filter on it; the companies do not have to, but usually should. This is the step most integrations skip, and it is where a brief quietly goes wrong — “Snowflake” the company is not Snowflake Data Cloud the product, and a brief naming a vendor’s product family (“Marketing Cloud” vs “Commerce Cloud”) needs the right one of several catalog entries.
curl --request POST \
--url https://api.aggknowledgeai.com/v2/live/vendorgraphics/search/products \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '{ "productName": "Snowflake Data Cloud" }'That returns product 48211, in category 1785 (Data Warehousing) — which is also the answer to the brief’s nearby-products line, since a category filter covers every product in it.
Run the priority company names through Company Match the same way, one call per name, and keep the id of each confident hit. Ambiguous names should be reviewed rather than guessed — see Company Matching. For this brief that yields 44002816, 65731533, and 44510632.
Names or IDs?#
Companies can also be filtered by name directly, with no resolve step — company.companyName is a phrase match against the company’s name, so { "or": ["salesforce"] } matches any company whose name contains that phrase. That is the fast path, and it is genuinely the right choice for exploring a brief before committing to it. For the search you actually deliver, resolving to IDs is usually worth the extra calls:
companyName | aggkCompanyId | |
|---|---|---|
| Setup | None | One Company Match per name, plus review of ambiguous hits |
| Precision | Phrase collisions — “delta” also matches Delta Dental and Delta Faucet | Exactly the companies you picked |
| Alternate spellings | “J.P. Morgan” and “JPMorgan” do not match each other | What Match’s scoring exists to resolve |
| Subsidiaries | Included by accident — “wells fargo” also catches Wells Fargo Advisors | Included on purpose, via company.expansion (v3) |
| Auditability | You cannot see which companies the phrase resolved to | An explicit list you can review and hand back with the results |
| Stability | The population shifts as new companies with colliding names appear | Fixed until you re-resolve |
The deciding factor is usually not precision but reuse: a resolved ID list is an artifact you can store against the brief, re-run next quarter, and show someone. A name filter is re-interpreted on every request.
Step 2 — translate the persona#
Three separate brief lines describe one person, and they do different jobs in the request.
Seniority is a band, and it does not map one-to-one#
“Director, VP, SVP, or C-level” is four stated bands but three vocabulary values — there is no svp. Senior vice presidents normalize to vp:
"seniority": { "or": ["director", "vp", "cxo"] }Teams become roles and subroles#
The brief’s team list is prose covering several distinct groups. Map each to the closest role, and reach for subrole where the brief is more specific than a role can express — “data engineering” and “procurement” are subroles, not roles:
"role": { "or": ["engineering", "analyst", "operations"] },
"subrole": {
"or": ["data_engineering", "data_science", "data_analyst",
"information_technology", "procurement"]
}role and subrole are single-valued per work experience — one person’s job has one of each. Use or to mean “any of these”. Passing two values as and can never match, and returns zero results rather than an error.Title keywords refine — they do not replace#
Briefs hedge their title lists — a flavour, not a checklist — for a reason: the author is describing a shape, not enumerating the population. jobTitle is the one analyzed-text filter in Discover, matched against both the stored title and its English translation, so it is the right tool for those keywords — but it should sit on top of role and seniority, not replace them. A search filtered only by title finds the titles someone happened to think of.
| Operator | Means | Use it for |
|---|---|---|
jobTitle.or | title matches any one of these entries | A list of alternative titles from the brief |
jobTitle.and | every term appears in one title | Narrowing a concept — “data” + “architect” |
Terms within a single value are already ANDed, so “data platform” requires both words in the same title. jobTitle is the only field where multi-value and behaves this way; everywhere else it is a trap.
Step 3 — scope it#
A bare country name is ambiguous in a way worth resolving with whoever wrote the brief. person.country is where the person is; company.country is where their employer is headquartered. For interview recruiting you almost always want the person.
“Still in the role, or left it within the past 12 months” is a union, not one filter: people currently in the role, plus people who left within the year. The two arms disagree on employment status, so they cannot share a filter block — which is what step 4 is about.
Step 4 — priority accounts are a tier, not a filter#
This is the part briefs get mistranslated most often. A named account list followed by then widen from there is a ranking instruction, not a restriction: start with these companies, then keep going. Filtering the search down to the list throws away everything past it — and dropping the list loses the priority.
v2 Advanced takes one filter block, so a tiered brief is several searches you merge yourself: one restricted to the priority company IDs, one for the broader product-customer population, and — because the brief also wants recent leavers — an employment-status variant of each. Deduplicate on the person id, keeping the highest-priority tier each person appeared in.
Switch this page to v3 to see the same brief as a single request with named groups.
{
"company": {
"aggkCompanyId": { "or": [44002816, 65731533, 44510632] }
},
"workExperience": {
"seniority": { "or": ["director", "vp", "cxo"] },
"subrole": {
"or": ["data_engineering", "data_science", "data_analyst",
"information_technology", "procurement"]
},
"personFilter": "CURRENT"
},
"person": {
"personCountry": { "or": ["united states"] }
}
}{
"vendorgraphics": {
"aggkProductId": { "or": [48211] },
"filterByDetectionPeriod": true,
"detectionPeriodBuffer": "SIX_MONTHS"
},
"workExperience": {
"seniority": { "or": ["director", "vp", "cxo"] },
"subrole": {
"or": ["data_engineering", "data_science", "data_analyst",
"information_technology", "procurement"]
},
"personFilter": "CURRENT"
},
"person": {
"personCountry": { "or": ["united states"] }
}
}Repeat both with “personFilter”: “PAST_NOT_CURRENT” and “endDate”: { "gte": "2025-08" } to pick up the recent leavers.
v3 expresses the whole brief as one request: each tier is a named filter group, a person matches if they satisfy any group, and matchedFilterGroups on every result tells you which tiers they landed in. Sort by that and the priority accounts come out first, with the broader market behind them — no merging, no second call.
{
"or": [
{
"name": "priority-current",
"company": {
"aggkCompanyId": { "or": [44002816, 65731533, 44510632] }
},
"workExperience": {
"seniority": { "or": ["director", "vp", "cxo"] },
"subrole": {
"or": ["data_engineering", "data_science", "data_analyst",
"information_technology", "procurement"]
},
"employmentStatus": "CURRENT"
},
"person": { "country": { "or": ["united states"] } }
},
{
"name": "priority-recent-leaver",
"company": {
"aggkCompanyId": { "or": [44002816, 65731533, 44510632] }
},
"workExperience": {
"seniority": { "or": ["director", "vp", "cxo"] },
"employmentStatus": "PAST_NOT_CURRENT",
"endDate": { "gte": "2025-08" }
},
"person": { "country": { "or": ["united states"] } }
},
{
"name": "market-current",
"vendorgraphics": {
"aggkProductId": { "or": ["48211"] },
"detectionWindow": { "enforce": true, "buffer": "SIX_MONTHS" }
},
"workExperience": {
"seniority": { "or": ["director", "vp", "cxo"] },
"subrole": {
"or": ["data_engineering", "data_science", "data_analyst",
"information_technology", "procurement"]
},
"employmentStatus": "CURRENT"
},
"person": { "country": { "or": ["united states"] } }
},
{
"name": "market-adjacent-category",
"vendorgraphics": { "categoryId": { "or": ["1785"] } },
"workExperience": {
"seniority": { "or": ["director", "vp", "cxo"] },
"subrole": { "or": ["data_engineering", "data_science"] },
"employmentStatus": "CURRENT"
},
"person": { "country": { "or": ["united states"] } }
}
],
"grouping": { "maxResultsPerCompany": 5 },
"pagination": { "page": 0, "size": 500 }
}The fourth group is the brief’s nearby-products line — a category filter reaches every product in Data Warehousing, so it does not need the alternates enumerated. Because it is a named group, results that only match it are identifiable as the widest-net tier.
company.aggkCompanyId.or is unioned with the companies the product filter resolves to, so a listed account appears whether or not it uses the product. That is deliberate: naming a company by ID is read as stated intent.companyName behaves the opposite way — it is an ordinary filter, so it intersects with the product’s companies rather than widening past them. So if what you want is “these accounts, and only where they use the product”, there are two routes:
- Name filter, one request — put the account names in
company.companyName.oralongside thevendorgraphicsfilter, and the group returns only the overlap. Cheap, but inherits every imprecision in the names-or-IDs tradeoff above. - Intersect the IDs yourself — run Product customer search for the product, keep the IDs it shares with your matched priority list, and pass only those as
aggkCompanyIdwith no product filter. More calls, exact result, and you can see the overlap before searching.
grouping.maxResultsPerCompany keeps one large employer from consuming the whole page — worth setting on any brief whose priority list mixes very large and very small organizations.Step 5 — size each tier before you crawl it#
Check tiers individually rather than in aggregate: a brief is usually over-broad in exactly one place, and a single total hides which.
Run Count results with each tier’s body. It is free on both versions and answers past the paging ceiling, so it also settles the question briefs love to ask in passing — “roughly how many are there, with no cap?”
One caveat when you quote that number back: the v2 count is over work experiences, so a person holding two matching roles is counted twice and the figure reads high against the people you will actually page through.
The v3 count is over distinct people, so it matches what you will page through. It is exact up to 40,000 and estimated above that — check isApproximate before quoting it as a hard number.
Run Validate request on the document once too. It is also free, and it catches the mistakes a tiered brief invites: an invalid seniority or industry value, a filter placed under the wrong entity, and — on v3 — filters left at the root instead of inside an or group. Warnings name the filter group they came from, which matters once you have four.
A search still returns at most 5,000 people on either version. If a tier is bigger than that, slice it — by region, by employee-count band, or by sub-function — and page each slice. See How Discover Works.
What the brief is asking for that Discover cannot express#
Some lines in a brief are screening criteria, not filters. Discover produces the population; qualification happens afterwards, in a screener or a conversation. Reading these as filters is how a search ends up empty:
| The brief asks for | Discover can | You still need to |
|---|---|---|
| Hands-on, frequent use of the product | Find people at companies where the product was detected, and how recently | Screen for individual usage — there is no seat- or session-level signal |
| Someone who sat on the evaluation | Filter to the functions and seniorities that usually own it | Confirm involvement; role is a proxy for it, not evidence of it |
| Views on a particular theme | Nothing — a theme is not an attribute | Treat it as the screener’s subject, not a filter |
| An account list to start from, then wider coverage | Tier the search so named accounts are identifiable | Decide how deep into the broader tier to go |
The finished loop#
- Resolve product and company names to IDs, and store them against the brief.
- Translate seniority, function, and titles — in that order of load-bearing weight.
- Apply scope: person geography, employment recency, industry.
- Express priority accounts as their own tier, not as a filter.
- Size each tier, slice anything over 5,000, then page.
- Enrich only the people who survive screening — Discover returns summaries, and the person’s
idis what Person Enrich takes aspersonId.