Skip to content

Commit d5501d5

Browse files
committed
fix(hunter): match documented Discover response and coerce numeric employees
1 parent 50c0678 commit d5501d5

5 files changed

Lines changed: 68 additions & 96 deletions

File tree

apps/sim/blocks/blocks/hunter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ Return ONLY the search query text - no explanations.`,
373373
results: {
374374
type: 'array',
375375
description:
376-
'Companies matching the search (name, domain, logo, linkedin_url, company_type, industry, size, location, founded_year, crunchbase_url)',
376+
'Companies matching the search (domain, organization, personal_emails, generic_emails, total_emails)',
377377
},
378378
// Companies Find (flattened)
379379
name: { type: 'string', description: 'Company name (companies-find, discover)' },

apps/sim/tools/hunter/companies_find.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ export const companiesFindTool: ToolConfig<HunterEnrichmentParams, HunterEnrichm
4848
description: c.description ?? '',
4949
industry: c.category?.industry ?? '',
5050
sector: c.category?.sector ?? '',
51-
size: c.metrics?.employees ?? '',
51+
size:
52+
c.metrics?.employeesRange ??
53+
(c.metrics?.employees != null ? String(c.metrics.employees) : ''),
5254
founded_year: c.foundedYear ?? null,
5355
location: c.location ?? '',
5456
country: c.geo?.country ?? '',

apps/sim/tools/hunter/discover.ts

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const discoverTool: ToolConfig<HunterDiscoverParams, HunterDiscoverRespon
7575

7676
if (params.query) body.query = params.query
7777
if (params.domain) body.organization = { domain: [params.domain] }
78-
if (params.headcount) body.headcount = { include: [params.headcount] }
78+
if (params.headcount) body.headcount = [params.headcount]
7979
if (params.company_type) body.company_type = { include: [params.company_type] }
8080
if (params.technology) {
8181
body.technology = {
@@ -89,38 +89,22 @@ export const discoverTool: ToolConfig<HunterDiscoverParams, HunterDiscoverRespon
8989

9090
transformResponse: async (response: Response) => {
9191
const data = await response.json()
92+
const companies: Array<{
93+
domain?: string
94+
organization?: string
95+
emails_count?: { personal?: number; generic?: number; total?: number }
96+
}> = Array.isArray(data?.data) ? data.data : []
9297

9398
return {
9499
success: true,
95100
output: {
96-
results:
97-
data.companies?.map(
98-
(company: {
99-
name?: string
100-
domain?: string
101-
logo?: string | null
102-
linkedin_url?: string | null
103-
company_type?: string | null
104-
meta?: {
105-
crunchbase_url?: string | null
106-
founded_year?: number | null
107-
location?: string | null
108-
industry?: string | null
109-
size?: string | null
110-
}
111-
}) => ({
112-
name: company.name ?? '',
113-
domain: company.domain ?? '',
114-
logo: company.logo ?? null,
115-
linkedin_url: company.linkedin_url ?? null,
116-
company_type: company.company_type ?? null,
117-
industry: company.meta?.industry ?? null,
118-
size: company.meta?.size ?? null,
119-
location: company.meta?.location ?? null,
120-
founded_year: company.meta?.founded_year ?? null,
121-
crunchbase_url: company.meta?.crunchbase_url ?? null,
122-
})
123-
) ?? [],
101+
results: companies.map((c) => ({
102+
domain: c.domain ?? '',
103+
organization: c.organization ?? '',
104+
personal_emails: c.emails_count?.personal ?? 0,
105+
generic_emails: c.emails_count?.generic ?? 0,
106+
total_emails: c.emails_count?.total ?? 0,
107+
})),
124108
},
125109
}
126110
},

apps/sim/tools/hunter/hunter.test.ts

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -137,60 +137,60 @@ describe('hunter email_finder', () => {
137137
describe('hunter discover', () => {
138138
const transform = discoverTool.transformResponse!
139139

140-
it('maps companies array (not data.data)', async () => {
140+
it('maps documented data array shape', async () => {
141141
const result = await transform(
142142
respond({
143-
companies: [
143+
data: [
144144
{
145-
name: 'Stripe',
146-
domain: 'stripe.com',
147-
logo: 'https://logo.png',
148-
linkedin_url: 'https://linkedin.com/company/stripe',
149-
company_type: 'Privately Held',
150-
meta: {
151-
industry: 'Fintech',
152-
size: '1000+',
153-
location: 'San Francisco, CA',
154-
founded_year: 2010,
155-
crunchbase_url: 'https://crunchbase.com/org/stripe',
156-
},
145+
domain: 'hunter.io',
146+
organization: 'Hunter',
147+
emails_count: { personal: 23, generic: 5, total: 28 },
157148
},
158149
],
159150
})
160151
)
161152

162-
expect(result.output.results).toHaveLength(1)
163-
expect(result.output.results[0]).toEqual({
164-
name: 'Stripe',
165-
domain: 'stripe.com',
166-
logo: 'https://logo.png',
167-
linkedin_url: 'https://linkedin.com/company/stripe',
168-
company_type: 'Privately Held',
169-
industry: 'Fintech',
170-
size: '1000+',
171-
location: 'San Francisco, CA',
172-
founded_year: 2010,
173-
crunchbase_url: 'https://crunchbase.com/org/stripe',
174-
})
153+
expect(result.output.results).toEqual([
154+
{
155+
domain: 'hunter.io',
156+
organization: 'Hunter',
157+
personal_emails: 23,
158+
generic_emails: 5,
159+
total_emails: 28,
160+
},
161+
])
175162
})
176163

177-
it('returns empty array when companies is missing', async () => {
164+
it('returns empty array when data is missing', async () => {
178165
const result = await transform(respond({}))
179166
expect(result.output.results).toEqual([])
180167
})
181168

169+
it('falls back to zero counts when emails_count is missing', async () => {
170+
const result = await transform(
171+
respond({ data: [{ domain: 'acme.com', organization: 'Acme' }] })
172+
)
173+
expect(result.output.results[0]).toEqual({
174+
domain: 'acme.com',
175+
organization: 'Acme',
176+
personal_emails: 0,
177+
generic_emails: 0,
178+
total_emails: 0,
179+
})
180+
})
181+
182182
it('throws when no search params provided', () => {
183183
const buildUrl = discoverTool.request.url as (p: Record<string, unknown>) => string
184184
expect(() => buildUrl({ apiKey: 'k' })).toThrow(/At least one search parameter/)
185185
})
186186

187-
it('wraps headcount in include array', () => {
187+
it('builds body per docs (headcount as plain array, technology wrapped)', () => {
188188
const buildBody = discoverTool.request.body as (
189189
p: Record<string, unknown>
190190
) => Record<string, unknown>
191191
const body = buildBody({ apiKey: 'k', headcount: '11-50', technology: 'react' })
192192
expect(body).toEqual({
193-
headcount: { include: ['11-50'] },
193+
headcount: ['11-50'],
194194
technology: { include: ['react'] },
195195
})
196196
})
@@ -243,6 +243,16 @@ describe('hunter companies_find', () => {
243243
})
244244
})
245245

246+
it('prefers employeesRange and coerces numeric employees', async () => {
247+
const rangeResult = await transform(
248+
respond({ data: { metrics: { employees: 5432, employeesRange: '1001-5000' } } })
249+
)
250+
expect(rangeResult.output.size).toBe('1001-5000')
251+
252+
const numericResult = await transform(respond({ data: { metrics: { employees: 5432 } } }))
253+
expect(numericResult.output.size).toBe('5432')
254+
})
255+
246256
it('survives missing nested objects', async () => {
247257
const result = await transform(respond({ data: {} }))
248258
expect(result.output).toMatchObject({

apps/sim/tools/hunter/types.ts

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -153,35 +153,16 @@ export const SENIORITY_OUTPUT: OutputProperty = {
153153
}
154154

155155
/**
156-
* Output definition for discover result company objects
156+
* Output definition for discover result company objects.
157+
* Hunter Discover returns minimal info per company — use Domain Search or
158+
* Company Enrichment for richer data on a specific result.
157159
*/
158160
export const DISCOVER_RESULT_OUTPUT_PROPERTIES = {
159-
name: { type: 'string', description: 'Company name' },
160161
domain: { type: 'string', description: 'Company domain' },
161-
logo: { type: 'string', description: 'URL of the company logo', optional: true },
162-
linkedin_url: {
163-
type: 'string',
164-
description: 'LinkedIn profile URL of the company',
165-
optional: true,
166-
},
167-
company_type: {
168-
type: 'string',
169-
description: 'Company type (e.g., privately held, public company)',
170-
optional: true,
171-
},
172-
industry: { type: 'string', description: 'Industry of the company', optional: true },
173-
size: { type: 'string', description: 'Headcount range of the company', optional: true },
174-
location: { type: 'string', description: 'Headquarters location', optional: true },
175-
founded_year: {
176-
type: 'number',
177-
description: 'Year the company was founded',
178-
optional: true,
179-
},
180-
crunchbase_url: {
181-
type: 'string',
182-
description: 'Crunchbase URL of the company',
183-
optional: true,
184-
},
162+
organization: { type: 'string', description: 'Organization name' },
163+
personal_emails: { type: 'number', description: 'Count of personal emails' },
164+
generic_emails: { type: 'number', description: 'Count of generic (role-based) emails' },
165+
total_emails: { type: 'number', description: 'Total emails found for the company' },
185166
} as const satisfies Record<string, OutputProperty>
186167

187168
/**
@@ -211,16 +192,11 @@ export interface HunterDiscoverParams extends HunterBaseParams {
211192
}
212193

213194
export interface HunterDiscoverResult {
214-
name: string
215195
domain: string
216-
logo: string | null
217-
linkedin_url: string | null
218-
company_type: string | null
219-
industry: string | null
220-
size: string | null
221-
location: string | null
222-
founded_year: number | null
223-
crunchbase_url: string | null
196+
organization: string
197+
personal_emails: number
198+
generic_emails: number
199+
total_emails: number
224200
}
225201

226202
export interface HunterDiscoverResponse extends ToolResponse {

0 commit comments

Comments
 (0)