mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(hunter): align tools, block, and outputs with Hunter.io v2 API spec (#4511)
* fix(hunter): align tools, block, and outputs with Hunter.io v2 API spec * fix(hunter): match documented Discover response and coerce numeric employees
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Hunter io
|
||||
title: Hunter.io
|
||||
description: Find and verify professional email addresses
|
||||
---
|
||||
|
||||
@@ -53,11 +53,16 @@ Returns companies matching a set of criteria using Hunter.io AI-powered search.
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | List of companies matching the search criteria |
|
||||
| ↳ `name` | string | Company name |
|
||||
| ↳ `domain` | string | Company domain |
|
||||
| ↳ `name` | string | Company/organization name |
|
||||
| ↳ `headcount` | number | Company size/headcount |
|
||||
| ↳ `technologies` | array | Technologies used by the company |
|
||||
| ↳ `email_count` | number | Total number of email addresses found |
|
||||
| ↳ `logo` | string | URL of the company logo |
|
||||
| ↳ `linkedin_url` | string | LinkedIn profile URL of the company |
|
||||
| ↳ `company_type` | string | Company type \(e.g., privately held, public company\) |
|
||||
| ↳ `industry` | string | Industry of the company |
|
||||
| ↳ `size` | string | Headcount range of the company |
|
||||
| ↳ `location` | string | Headquarters location |
|
||||
| ↳ `founded_year` | number | Year the company was founded |
|
||||
| ↳ `crunchbase_url` | string | Crunchbase URL of the company |
|
||||
|
||||
### `hunter_domain_search`
|
||||
|
||||
@@ -86,8 +91,9 @@ Returns all the email addresses found using one given domain name, with sources.
|
||||
| ↳ `first_name` | string | Person's first name |
|
||||
| ↳ `last_name` | string | Person's last name |
|
||||
| ↳ `position` | string | Job title/position |
|
||||
| ↳ `position_raw` | string | Raw job title as found |
|
||||
| ↳ `seniority` | string | Seniority level \(junior, senior, executive\) |
|
||||
| ↳ `department` | string | Department \(executive, it, finance, management, sales, legal, support, hr, marketing, communication\) |
|
||||
| ↳ `department` | string | Department \(executive, it, finance, management, sales, legal, support, hr, marketing, communication, education, design, health, operations\) |
|
||||
| ↳ `linkedin` | string | LinkedIn profile URL |
|
||||
| ↳ `twitter` | string | Twitter handle |
|
||||
| ↳ `phone_number` | string | Phone number |
|
||||
@@ -106,19 +112,7 @@ Returns all the email addresses found using one given domain name, with sources.
|
||||
| `accept_all` | boolean | Whether the server accepts all email addresses \(may cause false positives\) |
|
||||
| `pattern` | string | The email pattern used by the organization \(e.g., \{first\}, \{first\}.\{last\}\) |
|
||||
| `organization` | string | The organization/company name |
|
||||
| `description` | string | Description of the organization |
|
||||
| `industry` | string | Industry classification of the organization |
|
||||
| `twitter` | string | Twitter handle of the organization |
|
||||
| `facebook` | string | Facebook page URL of the organization |
|
||||
| `linkedin` | string | LinkedIn company page URL |
|
||||
| `instagram` | string | Instagram profile of the organization |
|
||||
| `youtube` | string | YouTube channel of the organization |
|
||||
| `technologies` | array | Technologies used by the organization |
|
||||
| `country` | string | Country where the organization is headquartered |
|
||||
| `state` | string | State/province where the organization is located |
|
||||
| `city` | string | City where the organization is located |
|
||||
| `postal_code` | string | Postal code of the organization |
|
||||
| `street` | string | Street address of the organization |
|
||||
| `linked_domains` | array | Other domains linked to the organization |
|
||||
|
||||
### `hunter_email_finder`
|
||||
|
||||
@@ -147,8 +141,17 @@ Finds the most likely email address for a person given their name and company do
|
||||
| `verification` | object | Email verification information |
|
||||
| ↳ `date` | string | Date when the email was verified \(YYYY-MM-DD\) |
|
||||
| ↳ `status` | string | Verification status \(valid, invalid, accept_all, webmail, disposable, unknown\) |
|
||||
| `first_name` | string | Person's first name |
|
||||
| `last_name` | string | Person's last name |
|
||||
| `email` | string | The found email address |
|
||||
| `score` | number | Confidence score \(0-100\) for the found email address |
|
||||
| `domain` | string | Domain that was searched |
|
||||
| `accept_all` | boolean | Whether the server accepts all email addresses \(may cause false positives\) |
|
||||
| `position` | string | Job title/position |
|
||||
| `twitter` | string | Twitter handle |
|
||||
| `linkedin_url` | string | LinkedIn profile URL |
|
||||
| `phone_number` | string | Phone number |
|
||||
| `company` | string | Company name |
|
||||
|
||||
### `hunter_email_verifier`
|
||||
|
||||
@@ -200,15 +203,24 @@ Enriches company data using domain name.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `company` | object | Company information |
|
||||
| ↳ `name` | string | Company name |
|
||||
| ↳ `domain` | string | Company domain |
|
||||
| ↳ `industry` | string | Industry classification |
|
||||
| ↳ `size` | string | Company size/headcount range |
|
||||
| ↳ `country` | string | Country where the company is located |
|
||||
| ↳ `linkedin` | string | LinkedIn company page URL |
|
||||
| ↳ `twitter` | string | Twitter handle |
|
||||
| `person` | object | Person information \(undefined for companies_find tool\) |
|
||||
| `name` | string | Company name |
|
||||
| `domain` | string | Company domain |
|
||||
| `description` | string | Company description |
|
||||
| `industry` | string | Industry classification |
|
||||
| `sector` | string | Business sector |
|
||||
| `size` | string | Employee headcount range \(e.g., "11-50"\) |
|
||||
| `founded_year` | number | Year founded |
|
||||
| `location` | string | Headquarters location \(formatted\) |
|
||||
| `country` | string | Country \(full name\) |
|
||||
| `country_code` | string | ISO 3166-1 alpha-2 country code |
|
||||
| `state` | string | State/province |
|
||||
| `city` | string | City |
|
||||
| `linkedin` | string | LinkedIn handle \(e.g., company/hunterio\) |
|
||||
| `twitter` | string | Twitter handle |
|
||||
| `facebook` | string | Facebook handle |
|
||||
| `logo` | string | Company logo URL |
|
||||
| `phone` | string | Company phone number |
|
||||
| `tech` | array | Technologies used by the company |
|
||||
|
||||
### `hunter_email_count`
|
||||
|
||||
|
||||
@@ -6526,7 +6526,7 @@
|
||||
{
|
||||
"type": "hunter",
|
||||
"slug": "hunter-io",
|
||||
"name": "Hunter io",
|
||||
"name": "Hunter.io",
|
||||
"description": "Find and verify professional email addresses",
|
||||
"longDescription": "Integrate Hunter into the workflow. Can search domains, find email addresses, verify email addresses, discover companies, find companies, and count email addresses.",
|
||||
"bgColor": "#E0E0E0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { HunterResponse } from '@/tools/hunter/types'
|
||||
|
||||
export const HunterBlock: BlockConfig<HunterResponse> = {
|
||||
type: 'hunter',
|
||||
name: 'Hunter io',
|
||||
name: 'Hunter.io',
|
||||
description: 'Find and verify professional email addresses',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
@@ -45,6 +45,15 @@ export const HunterBlock: BlockConfig<HunterResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'hunter_domain_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'hunter_domain_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
@@ -57,6 +66,7 @@ export const HunterBlock: BlockConfig<HunterResponse> = {
|
||||
],
|
||||
value: () => 'all',
|
||||
condition: { field: 'operation', value: 'hunter_domain_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'seniority',
|
||||
@@ -70,6 +80,7 @@ export const HunterBlock: BlockConfig<HunterResponse> = {
|
||||
],
|
||||
value: () => 'all',
|
||||
condition: { field: 'operation', value: 'hunter_domain_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'department',
|
||||
@@ -77,6 +88,7 @@ export const HunterBlock: BlockConfig<HunterResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., sales, marketing, engineering',
|
||||
condition: { field: 'operation', value: 'hunter_domain_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Email Finder operation inputs
|
||||
{
|
||||
@@ -109,6 +121,7 @@ export const HunterBlock: BlockConfig<HunterResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter company name',
|
||||
condition: { field: 'operation', value: 'hunter_email_finder' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Email Verifier operation inputs
|
||||
{
|
||||
@@ -146,6 +159,54 @@ Return ONLY the search query text - no explanations.`,
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by domain',
|
||||
condition: { field: 'operation', value: 'hunter_discover' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'headcount',
|
||||
title: 'Headcount',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: '1-10', id: '1-10' },
|
||||
{ label: '11-50', id: '11-50' },
|
||||
{ label: '51-200', id: '51-200' },
|
||||
{ label: '201-500', id: '201-500' },
|
||||
{ label: '501-1000', id: '501-1000' },
|
||||
{ label: '1001-5000', id: '1001-5000' },
|
||||
{ label: '5001-10000', id: '5001-10000' },
|
||||
{ label: '10001+', id: '10001+' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'hunter_discover' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'company_type',
|
||||
title: 'Company Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: 'Educational', id: 'educational' },
|
||||
{ label: 'Government Agency', id: 'government agency' },
|
||||
{ label: 'Non Profit', id: 'non profit' },
|
||||
{ label: 'Partnership', id: 'partnership' },
|
||||
{ label: 'Privately Held', id: 'privately held' },
|
||||
{ label: 'Public Company', id: 'public company' },
|
||||
{ label: 'Self Employed', id: 'self employed' },
|
||||
{ label: 'Self Owned', id: 'self owned' },
|
||||
{ label: 'Sole Proprietorship', id: 'sole proprietorship' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'hunter_discover' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'technology',
|
||||
title: 'Technology',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., react, salesforce',
|
||||
condition: { field: 'operation', value: 'hunter_discover' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Find Company operation inputs
|
||||
@@ -172,6 +233,7 @@ Return ONLY the search query text - no explanations.`,
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter company name',
|
||||
condition: { field: 'operation', value: 'hunter_email_count' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
@@ -184,6 +246,7 @@ Return ONLY the search query text - no explanations.`,
|
||||
],
|
||||
value: () => 'all',
|
||||
condition: { field: 'operation', value: 'hunter_email_count' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// API Key (common)
|
||||
{
|
||||
@@ -225,7 +288,14 @@ Return ONLY the search query text - no explanations.`,
|
||||
},
|
||||
params: (params) => {
|
||||
const result: Record<string, unknown> = {}
|
||||
if (params.limit) result.limit = Number(params.limit)
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value === undefined || value === null || value === '') continue
|
||||
if (key === 'limit' || key === 'offset') {
|
||||
result[key] = Number(value)
|
||||
} else {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
},
|
||||
@@ -253,14 +323,88 @@ Return ONLY the search query text - no explanations.`,
|
||||
technology: { type: 'string', description: 'Technology filter' },
|
||||
},
|
||||
outputs: {
|
||||
results: { type: 'json', description: 'Search results' },
|
||||
emails: { type: 'json', description: 'Email addresses found' },
|
||||
// Domain Search
|
||||
domain: { type: 'string', description: 'Domain name' },
|
||||
organization: { type: 'string', description: 'Organization name (domain search)' },
|
||||
pattern: { type: 'string', description: 'Email pattern (e.g., {first}.{last})' },
|
||||
disposable: { type: 'boolean', description: 'Whether the domain is disposable' },
|
||||
webmail: { type: 'boolean', description: 'Whether the domain is a webmail provider' },
|
||||
accept_all: { type: 'boolean', description: 'Whether the server accepts all emails' },
|
||||
linked_domains: { type: 'array', description: 'Linked domains' },
|
||||
emails: {
|
||||
type: 'array',
|
||||
description:
|
||||
'List of emails found for the domain (value, type, confidence, first_name, last_name, position, seniority, department, linkedin, twitter, phone_number, sources, verification)',
|
||||
},
|
||||
// Email Finder
|
||||
email: { type: 'string', description: 'Found email address' },
|
||||
score: { type: 'number', description: 'Confidence score' },
|
||||
result: { type: 'string', description: 'Verification result' },
|
||||
status: { type: 'string', description: 'Status message' },
|
||||
total: { type: 'number', description: 'Total results count' },
|
||||
score: { type: 'number', description: 'Confidence score (0-100)' },
|
||||
first_name: { type: 'string', description: 'Person first name' },
|
||||
last_name: { type: 'string', description: 'Person last name' },
|
||||
position: { type: 'string', description: 'Job position' },
|
||||
linkedin_url: { type: 'string', description: 'LinkedIn profile URL (email-finder, discover)' },
|
||||
phone_number: { type: 'string', description: 'Phone number' },
|
||||
company: { type: 'string', description: 'Company name (email-finder)' },
|
||||
sources: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Source pages where the email was found (domain, uri, extracted_on, last_seen_on, still_on_page)',
|
||||
},
|
||||
verification: {
|
||||
type: 'json',
|
||||
description: 'Email verification information (date, status)',
|
||||
},
|
||||
// Email Verifier
|
||||
result: {
|
||||
type: 'string',
|
||||
description: 'Deliverability result (deliverable, undeliverable, risky)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Verification status (valid, invalid, accept_all, webmail, disposable, unknown)',
|
||||
},
|
||||
regexp: { type: 'boolean', description: 'Email passes regex validation' },
|
||||
gibberish: { type: 'boolean', description: 'Whether email looks auto-generated' },
|
||||
mx_records: { type: 'boolean', description: 'MX records exist for the domain' },
|
||||
smtp_server: { type: 'boolean', description: 'SMTP server reachable' },
|
||||
smtp_check: { type: 'boolean', description: 'Email does not bounce' },
|
||||
block: { type: 'boolean', description: 'Whether the domain blocks verification' },
|
||||
// Discover
|
||||
results: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Companies matching the search (domain, organization, personal_emails, generic_emails, total_emails)',
|
||||
},
|
||||
// Companies Find (flattened)
|
||||
name: { type: 'string', description: 'Company name (companies-find, discover)' },
|
||||
description: { type: 'string', description: 'Company description' },
|
||||
industry: { type: 'string', description: 'Industry classification' },
|
||||
sector: { type: 'string', description: 'Business sector' },
|
||||
size: { type: 'string', description: 'Employee headcount range (e.g., "11-50")' },
|
||||
founded_year: { type: 'number', description: 'Year founded' },
|
||||
location: { type: 'string', description: 'Headquarters location (formatted)' },
|
||||
country: { type: 'string', description: 'Country (full name)' },
|
||||
country_code: { type: 'string', description: 'ISO 3166-1 alpha-2 country code' },
|
||||
state: { type: 'string', description: 'State/province' },
|
||||
city: { type: 'string', description: 'City' },
|
||||
linkedin: { type: 'string', description: 'LinkedIn handle (companies-find)' },
|
||||
twitter: { type: 'string', description: 'Twitter handle' },
|
||||
facebook: { type: 'string', description: 'Facebook handle' },
|
||||
logo: { type: 'string', description: 'Company logo URL' },
|
||||
phone: { type: 'string', description: 'Company phone number' },
|
||||
tech: { type: 'array', description: 'Technologies used by the company' },
|
||||
// Email Count
|
||||
total: { type: 'number', description: 'Total email count' },
|
||||
personal_emails: { type: 'number', description: 'Personal emails count' },
|
||||
generic_emails: { type: 'number', description: 'Generic emails count' },
|
||||
department: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Email count by department (executive, it, finance, management, sales, legal, support, hr, marketing, communication, education, design, health, operations)',
|
||||
},
|
||||
seniority: {
|
||||
type: 'json',
|
||||
description: 'Email count by seniority level (junior, senior, executive)',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { HunterEnrichmentParams, HunterEnrichmentResponse } from '@/tools/hunter/types'
|
||||
import { COMPANY_OUTPUT } from '@/tools/hunter/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const companiesFindTool: ToolConfig<HunterEnrichmentParams, HunterEnrichmentResponse> = {
|
||||
@@ -39,32 +38,57 @@ export const companiesFindTool: ToolConfig<HunterEnrichmentParams, HunterEnrichm
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const c = data.data ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
person: undefined,
|
||||
company: data.data
|
||||
? {
|
||||
name: data.data.name || '',
|
||||
domain: data.data.domain || '',
|
||||
industry: data.data.industry || '',
|
||||
size: data.data.size || '',
|
||||
country: data.data.country || '',
|
||||
linkedin: data.data.linkedin || '',
|
||||
twitter: data.data.twitter || '',
|
||||
}
|
||||
: undefined,
|
||||
name: c.name ?? '',
|
||||
domain: c.domain ?? '',
|
||||
description: c.description ?? '',
|
||||
industry: c.category?.industry ?? '',
|
||||
sector: c.category?.sector ?? '',
|
||||
size:
|
||||
c.metrics?.employeesRange ??
|
||||
(c.metrics?.employees != null ? String(c.metrics.employees) : ''),
|
||||
founded_year: c.foundedYear ?? null,
|
||||
location: c.location ?? '',
|
||||
country: c.geo?.country ?? '',
|
||||
country_code: c.geo?.countryCode ?? '',
|
||||
state: c.geo?.state ?? '',
|
||||
city: c.geo?.city ?? '',
|
||||
linkedin: c.linkedin?.handle ?? '',
|
||||
twitter: c.twitter?.handle ?? '',
|
||||
facebook: c.facebook?.handle ?? '',
|
||||
logo: c.logo ?? '',
|
||||
phone: c.phone ?? '',
|
||||
tech: c.tech ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
person: {
|
||||
type: 'object',
|
||||
description: 'Person information (undefined for companies_find tool)',
|
||||
optional: true,
|
||||
name: { type: 'string', description: 'Company name' },
|
||||
domain: { type: 'string', description: 'Company domain' },
|
||||
description: { type: 'string', description: 'Company description' },
|
||||
industry: { type: 'string', description: 'Industry classification' },
|
||||
sector: { type: 'string', description: 'Business sector' },
|
||||
size: { type: 'string', description: 'Employee headcount range (e.g., "11-50")' },
|
||||
founded_year: { type: 'number', description: 'Year founded', optional: true },
|
||||
location: { type: 'string', description: 'Headquarters location (formatted)' },
|
||||
country: { type: 'string', description: 'Country (full name)' },
|
||||
country_code: { type: 'string', description: 'ISO 3166-1 alpha-2 country code' },
|
||||
state: { type: 'string', description: 'State/province' },
|
||||
city: { type: 'string', description: 'City' },
|
||||
linkedin: { type: 'string', description: 'LinkedIn handle (e.g., company/hunterio)' },
|
||||
twitter: { type: 'string', description: 'Twitter handle' },
|
||||
facebook: { type: 'string', description: 'Facebook handle' },
|
||||
logo: { type: 'string', description: 'Company logo URL' },
|
||||
phone: { type: 'string', description: 'Company phone number' },
|
||||
tech: {
|
||||
type: 'array',
|
||||
description: 'Technologies used by the company',
|
||||
items: { type: 'string', description: 'Technology name' },
|
||||
},
|
||||
company: COMPANY_OUTPUT,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -71,13 +71,12 @@ export const discoverTool: ToolConfig<HunterDiscoverParams, HunterDiscoverRespon
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
// Add optional parameters if provided
|
||||
if (params.query) body.query = params.query
|
||||
if (params.domain) body.organization = { domain: [params.domain] }
|
||||
if (params.headcount) body.headcount = params.headcount
|
||||
if (params.company_type) body.company_type = params.company_type
|
||||
if (params.headcount) body.headcount = [params.headcount]
|
||||
if (params.company_type) body.company_type = { include: [params.company_type] }
|
||||
if (params.technology) {
|
||||
body.technology = {
|
||||
include: [params.technology],
|
||||
@@ -90,18 +89,22 @@ export const discoverTool: ToolConfig<HunterDiscoverParams, HunterDiscoverRespon
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const companies: Array<{
|
||||
domain?: string
|
||||
organization?: string
|
||||
emails_count?: { personal?: number; generic?: number; total?: number }
|
||||
}> = Array.isArray(data?.data) ? data.data : []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
results:
|
||||
data.data?.map((company: any) => ({
|
||||
domain: company.domain || '',
|
||||
name: company.organization || '',
|
||||
headcount: company.headcount,
|
||||
technologies: company.technologies || [],
|
||||
email_count: company.emails_count?.total || 0,
|
||||
})) || [],
|
||||
results: companies.map((c) => ({
|
||||
domain: c.domain ?? '',
|
||||
organization: c.organization ?? '',
|
||||
personal_emails: c.emails_count?.personal ?? 0,
|
||||
generic_emails: c.emails_count?.generic ?? 0,
|
||||
total_emails: c.emails_count?.total ?? 0,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { HunterDomainSearchParams, HunterDomainSearchResponse } from '@/tools/hunter/types'
|
||||
import type {
|
||||
HunterDomainSearchParams,
|
||||
HunterDomainSearchResponse,
|
||||
HunterEmail,
|
||||
} from '@/tools/hunter/types'
|
||||
import { EMAILS_OUTPUT } from '@/tools/hunter/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -77,45 +81,35 @@ export const domainSearchTool: ToolConfig<HunterDomainSearchParams, HunterDomain
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const d = data.data ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
domain: data.data?.domain || '',
|
||||
disposable: data.data?.disposable || false,
|
||||
webmail: data.data?.webmail || false,
|
||||
accept_all: data.data?.accept_all || false,
|
||||
pattern: data.data?.pattern || '',
|
||||
organization: data.data?.organization || '',
|
||||
description: data.data?.description || '',
|
||||
industry: data.data?.industry || '',
|
||||
twitter: data.data?.twitter || '',
|
||||
facebook: data.data?.facebook || '',
|
||||
linkedin: data.data?.linkedin || '',
|
||||
instagram: data.data?.instagram || '',
|
||||
youtube: data.data?.youtube || '',
|
||||
technologies: data.data?.technologies || [],
|
||||
country: data.data?.country || '',
|
||||
state: data.data?.state || '',
|
||||
city: data.data?.city || '',
|
||||
postal_code: data.data?.postal_code || '',
|
||||
street: data.data?.street || '',
|
||||
domain: d.domain ?? '',
|
||||
disposable: d.disposable ?? false,
|
||||
webmail: d.webmail ?? false,
|
||||
accept_all: d.accept_all ?? false,
|
||||
pattern: d.pattern ?? '',
|
||||
organization: d.organization ?? '',
|
||||
linked_domains: d.linked_domains ?? [],
|
||||
emails:
|
||||
data.data?.emails?.map((email: any) => ({
|
||||
value: email.value || '',
|
||||
type: email.type || '',
|
||||
confidence: email.confidence || 0,
|
||||
sources: email.sources || [],
|
||||
first_name: email.first_name || '',
|
||||
last_name: email.last_name || '',
|
||||
position: email.position || '',
|
||||
seniority: email.seniority || '',
|
||||
department: email.department || '',
|
||||
linkedin: email.linkedin || '',
|
||||
twitter: email.twitter || '',
|
||||
phone_number: email.phone_number || '',
|
||||
verification: email.verification || {},
|
||||
})) || [],
|
||||
d.emails?.map((email: Partial<HunterEmail>) => ({
|
||||
value: email.value ?? '',
|
||||
type: email.type ?? '',
|
||||
confidence: email.confidence ?? 0,
|
||||
sources: email.sources ?? [],
|
||||
first_name: email.first_name ?? null,
|
||||
last_name: email.last_name ?? null,
|
||||
position: email.position ?? null,
|
||||
position_raw: email.position_raw ?? null,
|
||||
seniority: email.seniority ?? null,
|
||||
department: email.department ?? null,
|
||||
linkedin: email.linkedin ?? null,
|
||||
twitter: email.twitter ?? null,
|
||||
phone_number: email.phone_number ?? null,
|
||||
verification: email.verification ?? { date: null, status: 'unknown' },
|
||||
})) ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -145,61 +139,10 @@ export const domainSearchTool: ToolConfig<HunterDomainSearchParams, HunterDomain
|
||||
type: 'string',
|
||||
description: 'The organization/company name',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'Description of the organization',
|
||||
},
|
||||
industry: {
|
||||
type: 'string',
|
||||
description: 'Industry classification of the organization',
|
||||
},
|
||||
twitter: {
|
||||
type: 'string',
|
||||
description: 'Twitter handle of the organization',
|
||||
},
|
||||
facebook: {
|
||||
type: 'string',
|
||||
description: 'Facebook page URL of the organization',
|
||||
},
|
||||
linkedin: {
|
||||
type: 'string',
|
||||
description: 'LinkedIn company page URL',
|
||||
},
|
||||
instagram: {
|
||||
type: 'string',
|
||||
description: 'Instagram profile of the organization',
|
||||
},
|
||||
youtube: {
|
||||
type: 'string',
|
||||
description: 'YouTube channel of the organization',
|
||||
},
|
||||
technologies: {
|
||||
linked_domains: {
|
||||
type: 'array',
|
||||
description: 'Technologies used by the organization',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'Technology name',
|
||||
},
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
description: 'Country where the organization is headquartered',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'State/province where the organization is located',
|
||||
},
|
||||
city: {
|
||||
type: 'string',
|
||||
description: 'City where the organization is located',
|
||||
},
|
||||
postal_code: {
|
||||
type: 'string',
|
||||
description: 'Postal code of the organization',
|
||||
},
|
||||
street: {
|
||||
type: 'string',
|
||||
description: 'Street address of the organization',
|
||||
description: 'Other domains linked to the organization',
|
||||
items: { type: 'string', description: 'Domain name' },
|
||||
},
|
||||
emails: EMAILS_OUTPUT,
|
||||
},
|
||||
|
||||
@@ -62,27 +62,46 @@ export const emailFinderTool: ToolConfig<HunterEmailFinderParams, HunterEmailFin
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
const d = data.data ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
email: data.data?.email || '',
|
||||
score: data.data?.score || 0,
|
||||
sources: data.data?.sources || [],
|
||||
verification: data.data?.verification || {},
|
||||
first_name: d.first_name ?? '',
|
||||
last_name: d.last_name ?? '',
|
||||
email: d.email ?? '',
|
||||
score: d.score ?? 0,
|
||||
domain: d.domain ?? '',
|
||||
accept_all: d.accept_all ?? false,
|
||||
position: d.position ?? null,
|
||||
twitter: d.twitter ?? null,
|
||||
linkedin_url: d.linkedin_url ?? null,
|
||||
phone_number: d.phone_number ?? null,
|
||||
company: d.company ?? null,
|
||||
sources: d.sources ?? [],
|
||||
verification: d.verification ?? { date: null, status: 'unknown' },
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
email: {
|
||||
type: 'string',
|
||||
description: 'The found email address',
|
||||
},
|
||||
first_name: { type: 'string', description: "Person's first name" },
|
||||
last_name: { type: 'string', description: "Person's last name" },
|
||||
email: { type: 'string', description: 'The found email address' },
|
||||
score: {
|
||||
type: 'number',
|
||||
description: 'Confidence score (0-100) for the found email address',
|
||||
},
|
||||
domain: { type: 'string', description: 'Domain that was searched' },
|
||||
accept_all: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the server accepts all email addresses (may cause false positives)',
|
||||
},
|
||||
position: { type: 'string', description: 'Job title/position', optional: true },
|
||||
twitter: { type: 'string', description: 'Twitter handle', optional: true },
|
||||
linkedin_url: { type: 'string', description: 'LinkedIn profile URL', optional: true },
|
||||
phone_number: { type: 'string', description: 'Phone number', optional: true },
|
||||
company: { type: 'string', description: 'Company name', optional: true },
|
||||
sources: SOURCES_OUTPUT,
|
||||
verification: VERIFICATION_OUTPUT,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { companiesFindTool } from '@/tools/hunter/companies_find'
|
||||
import { discoverTool } from '@/tools/hunter/discover'
|
||||
import { domainSearchTool } from '@/tools/hunter/domain_search'
|
||||
import { emailFinderTool } from '@/tools/hunter/email_finder'
|
||||
|
||||
const respond = (body: unknown) => new Response(JSON.stringify(body))
|
||||
|
||||
describe('hunter domain_search', () => {
|
||||
const transform = domainSearchTool.transformResponse!
|
||||
|
||||
it('maps the documented response shape', async () => {
|
||||
const result = await transform(
|
||||
respond({
|
||||
data: {
|
||||
domain: 'stripe.com',
|
||||
disposable: false,
|
||||
webmail: false,
|
||||
accept_all: true,
|
||||
pattern: '{first}',
|
||||
organization: 'Stripe',
|
||||
linked_domains: ['stripe.io'],
|
||||
emails: [
|
||||
{
|
||||
value: 'patrick@stripe.com',
|
||||
type: 'personal',
|
||||
confidence: 92,
|
||||
first_name: 'Patrick',
|
||||
last_name: 'Collison',
|
||||
position: 'CEO',
|
||||
seniority: 'executive',
|
||||
department: 'executive',
|
||||
linkedin: null,
|
||||
twitter: 'patrickc',
|
||||
phone_number: null,
|
||||
sources: [],
|
||||
verification: { date: '2024-01-01', status: 'valid' },
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.output.domain).toBe('stripe.com')
|
||||
expect(result.output.linked_domains).toEqual(['stripe.io'])
|
||||
expect(result.output.emails).toHaveLength(1)
|
||||
expect(result.output.emails[0]).toMatchObject({
|
||||
value: 'patrick@stripe.com',
|
||||
first_name: 'Patrick',
|
||||
twitter: 'patrickc',
|
||||
verification: { status: 'valid' },
|
||||
})
|
||||
})
|
||||
|
||||
it('returns safe defaults when fields are missing', async () => {
|
||||
const result = await transform(respond({ data: null }))
|
||||
expect(result.output).toMatchObject({
|
||||
domain: '',
|
||||
disposable: false,
|
||||
webmail: false,
|
||||
accept_all: false,
|
||||
pattern: '',
|
||||
organization: '',
|
||||
linked_domains: [],
|
||||
emails: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('nullifies missing optional email fields', async () => {
|
||||
const result = await transform(
|
||||
respond({
|
||||
data: {
|
||||
emails: [{ value: 'a@b.com', type: 'generic', confidence: 50 }],
|
||||
},
|
||||
})
|
||||
)
|
||||
expect(result.output.emails[0]).toMatchObject({
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
position: null,
|
||||
linkedin: null,
|
||||
verification: { status: 'unknown' },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hunter email_finder', () => {
|
||||
const transform = emailFinderTool.transformResponse!
|
||||
|
||||
it('extracts the documented finder fields', async () => {
|
||||
const result = await transform(
|
||||
respond({
|
||||
data: {
|
||||
first_name: 'Alex',
|
||||
last_name: 'Smith',
|
||||
email: 'alex@acme.com',
|
||||
score: 85,
|
||||
domain: 'acme.com',
|
||||
accept_all: false,
|
||||
position: 'Engineer',
|
||||
twitter: null,
|
||||
linkedin_url: 'https://linkedin.com/in/alex',
|
||||
phone_number: null,
|
||||
company: 'Acme',
|
||||
sources: [],
|
||||
verification: { date: null, status: 'valid' },
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.output).toMatchObject({
|
||||
first_name: 'Alex',
|
||||
email: 'alex@acme.com',
|
||||
score: 85,
|
||||
linkedin_url: 'https://linkedin.com/in/alex',
|
||||
company: 'Acme',
|
||||
verification: { status: 'valid' },
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to safe defaults', async () => {
|
||||
const result = await transform(respond({ data: {} }))
|
||||
expect(result.output).toMatchObject({
|
||||
email: '',
|
||||
score: 0,
|
||||
accept_all: false,
|
||||
sources: [],
|
||||
verification: { date: null, status: 'unknown' },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hunter discover', () => {
|
||||
const transform = discoverTool.transformResponse!
|
||||
|
||||
it('maps documented data array shape', async () => {
|
||||
const result = await transform(
|
||||
respond({
|
||||
data: [
|
||||
{
|
||||
domain: 'hunter.io',
|
||||
organization: 'Hunter',
|
||||
emails_count: { personal: 23, generic: 5, total: 28 },
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.output.results).toEqual([
|
||||
{
|
||||
domain: 'hunter.io',
|
||||
organization: 'Hunter',
|
||||
personal_emails: 23,
|
||||
generic_emails: 5,
|
||||
total_emails: 28,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('returns empty array when data is missing', async () => {
|
||||
const result = await transform(respond({}))
|
||||
expect(result.output.results).toEqual([])
|
||||
})
|
||||
|
||||
it('falls back to zero counts when emails_count is missing', async () => {
|
||||
const result = await transform(
|
||||
respond({ data: [{ domain: 'acme.com', organization: 'Acme' }] })
|
||||
)
|
||||
expect(result.output.results[0]).toEqual({
|
||||
domain: 'acme.com',
|
||||
organization: 'Acme',
|
||||
personal_emails: 0,
|
||||
generic_emails: 0,
|
||||
total_emails: 0,
|
||||
})
|
||||
})
|
||||
|
||||
it('throws when no search params provided', () => {
|
||||
const buildUrl = discoverTool.request.url as (p: Record<string, unknown>) => string
|
||||
expect(() => buildUrl({ apiKey: 'k' })).toThrow(/At least one search parameter/)
|
||||
})
|
||||
|
||||
it('builds body per docs (headcount as plain array, technology wrapped)', () => {
|
||||
const buildBody = discoverTool.request.body as (
|
||||
p: Record<string, unknown>
|
||||
) => Record<string, unknown>
|
||||
const body = buildBody({ apiKey: 'k', headcount: '11-50', technology: 'react' })
|
||||
expect(body).toEqual({
|
||||
headcount: ['11-50'],
|
||||
technology: { include: ['react'] },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hunter companies_find', () => {
|
||||
const transform = companiesFindTool.transformResponse!
|
||||
|
||||
it('flattens nested company fields', async () => {
|
||||
const result = await transform(
|
||||
respond({
|
||||
data: {
|
||||
name: 'Stripe',
|
||||
domain: 'stripe.com',
|
||||
description: 'Payments',
|
||||
category: { industry: 'Fintech', sector: 'Software' },
|
||||
metrics: { employees: '1000+' },
|
||||
foundedYear: 2010,
|
||||
location: 'San Francisco, CA',
|
||||
geo: { country: 'United States', countryCode: 'US', state: 'CA', city: 'SF' },
|
||||
linkedin: { handle: 'company/stripe' },
|
||||
twitter: { handle: 'stripe' },
|
||||
facebook: { handle: 'stripe' },
|
||||
logo: 'https://logo.png',
|
||||
phone: '+1-555',
|
||||
tech: ['react', 'node'],
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
expect(result.output).toEqual({
|
||||
name: 'Stripe',
|
||||
domain: 'stripe.com',
|
||||
description: 'Payments',
|
||||
industry: 'Fintech',
|
||||
sector: 'Software',
|
||||
size: '1000+',
|
||||
founded_year: 2010,
|
||||
location: 'San Francisco, CA',
|
||||
country: 'United States',
|
||||
country_code: 'US',
|
||||
state: 'CA',
|
||||
city: 'SF',
|
||||
linkedin: 'company/stripe',
|
||||
twitter: 'stripe',
|
||||
facebook: 'stripe',
|
||||
logo: 'https://logo.png',
|
||||
phone: '+1-555',
|
||||
tech: ['react', 'node'],
|
||||
})
|
||||
})
|
||||
|
||||
it('prefers employeesRange and coerces numeric employees', async () => {
|
||||
const rangeResult = await transform(
|
||||
respond({ data: { metrics: { employees: 5432, employeesRange: '1001-5000' } } })
|
||||
)
|
||||
expect(rangeResult.output.size).toBe('1001-5000')
|
||||
|
||||
const numericResult = await transform(respond({ data: { metrics: { employees: 5432 } } }))
|
||||
expect(numericResult.output.size).toBe('5432')
|
||||
})
|
||||
|
||||
it('survives missing nested objects', async () => {
|
||||
const result = await transform(respond({ data: {} }))
|
||||
expect(result.output).toMatchObject({
|
||||
name: '',
|
||||
industry: '',
|
||||
sector: '',
|
||||
size: '',
|
||||
country: '',
|
||||
linkedin: '',
|
||||
tech: [],
|
||||
founded_year: null,
|
||||
})
|
||||
})
|
||||
})
|
||||
+61
-101
@@ -71,14 +71,20 @@ export const EMAIL_OUTPUT_PROPERTIES = {
|
||||
type: 'number',
|
||||
description: 'Probability score (0-100) that the email is correct',
|
||||
},
|
||||
first_name: { type: 'string', description: "Person's first name" },
|
||||
last_name: { type: 'string', description: "Person's last name" },
|
||||
position: { type: 'string', description: 'Job title/position' },
|
||||
seniority: { type: 'string', description: 'Seniority level (junior, senior, executive)' },
|
||||
first_name: { type: 'string', description: "Person's first name", optional: true },
|
||||
last_name: { type: 'string', description: "Person's last name", optional: true },
|
||||
position: { type: 'string', description: 'Job title/position', optional: true },
|
||||
position_raw: { type: 'string', description: 'Raw job title as found', optional: true },
|
||||
seniority: {
|
||||
type: 'string',
|
||||
description: 'Seniority level (junior, senior, executive)',
|
||||
optional: true,
|
||||
},
|
||||
department: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Department (executive, it, finance, management, sales, legal, support, hr, marketing, communication)',
|
||||
'Department (executive, it, finance, management, sales, legal, support, hr, marketing, communication, education, design, health, operations)',
|
||||
optional: true,
|
||||
},
|
||||
linkedin: { type: 'string', description: 'LinkedIn profile URL', optional: true },
|
||||
twitter: { type: 'string', description: 'Twitter handle', optional: true },
|
||||
@@ -147,36 +153,16 @@ export const SENIORITY_OUTPUT: OutputProperty = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Output definition for emails_count object in discover results
|
||||
*/
|
||||
export const EMAILS_COUNT_OUTPUT_PROPERTIES = {
|
||||
personal: { type: 'number', description: 'Number of personal email addresses' },
|
||||
generic: { type: 'number', description: 'Number of generic/role-based email addresses' },
|
||||
total: { type: 'number', description: 'Total number of email addresses' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
/**
|
||||
* Complete emails_count object output definition
|
||||
*/
|
||||
export const EMAILS_COUNT_OUTPUT: OutputProperty = {
|
||||
type: 'object',
|
||||
description: 'Email count breakdown',
|
||||
properties: EMAILS_COUNT_OUTPUT_PROPERTIES,
|
||||
}
|
||||
|
||||
/**
|
||||
* Output definition for discover result company objects
|
||||
* Output definition for discover result company objects.
|
||||
* Hunter Discover returns minimal info per company — use Domain Search or
|
||||
* Company Enrichment for richer data on a specific result.
|
||||
*/
|
||||
export const DISCOVER_RESULT_OUTPUT_PROPERTIES = {
|
||||
domain: { type: 'string', description: 'Company domain' },
|
||||
name: { type: 'string', description: 'Company/organization name' },
|
||||
headcount: { type: 'number', description: 'Company size/headcount', optional: true },
|
||||
technologies: {
|
||||
type: 'array',
|
||||
description: 'Technologies used by the company',
|
||||
items: { type: 'string', description: 'Technology name' },
|
||||
},
|
||||
email_count: { type: 'number', description: 'Total number of email addresses found' },
|
||||
organization: { type: 'string', description: 'Organization name' },
|
||||
personal_emails: { type: 'number', description: 'Count of personal emails' },
|
||||
generic_emails: { type: 'number', description: 'Count of generic (role-based) emails' },
|
||||
total_emails: { type: 'number', description: 'Total emails found for the company' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
/**
|
||||
@@ -191,28 +177,6 @@ export const DISCOVER_RESULTS_OUTPUT: OutputProperty = {
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Output definition for company enrichment objects
|
||||
*/
|
||||
export const COMPANY_OUTPUT_PROPERTIES = {
|
||||
name: { type: 'string', description: 'Company name' },
|
||||
domain: { type: 'string', description: 'Company domain' },
|
||||
industry: { type: 'string', description: 'Industry classification' },
|
||||
size: { type: 'string', description: 'Company size/headcount range' },
|
||||
country: { type: 'string', description: 'Country where the company is located' },
|
||||
linkedin: { type: 'string', description: 'LinkedIn company page URL', optional: true },
|
||||
twitter: { type: 'string', description: 'Twitter handle', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
/**
|
||||
* Complete company object output definition
|
||||
*/
|
||||
export const COMPANY_OUTPUT: OutputProperty = {
|
||||
type: 'object',
|
||||
description: 'Company information',
|
||||
properties: COMPANY_OUTPUT_PROPERTIES,
|
||||
}
|
||||
|
||||
// Common parameters for all Hunter.io tools
|
||||
export interface HunterBaseParams {
|
||||
apiKey: string
|
||||
@@ -229,10 +193,10 @@ export interface HunterDiscoverParams extends HunterBaseParams {
|
||||
|
||||
export interface HunterDiscoverResult {
|
||||
domain: string
|
||||
name: string
|
||||
headcount?: number
|
||||
technologies?: string[]
|
||||
email_count?: number
|
||||
organization: string
|
||||
personal_emails: number
|
||||
generic_emails: number
|
||||
total_emails: number
|
||||
}
|
||||
|
||||
export interface HunterDiscoverResponse extends ToolResponse {
|
||||
@@ -262,16 +226,17 @@ export interface HunterEmail {
|
||||
last_seen_on: string
|
||||
still_on_page: boolean
|
||||
}>
|
||||
first_name: string
|
||||
last_name: string
|
||||
position: string
|
||||
seniority: string
|
||||
department: string
|
||||
linkedin: string
|
||||
twitter: string
|
||||
phone_number: string
|
||||
first_name: string | null
|
||||
last_name: string | null
|
||||
position: string | null
|
||||
position_raw: string | null
|
||||
seniority: string | null
|
||||
department: string | null
|
||||
linkedin: string | null
|
||||
twitter: string | null
|
||||
phone_number: string | null
|
||||
verification: {
|
||||
date: string
|
||||
date: string | null
|
||||
status: string
|
||||
}
|
||||
}
|
||||
@@ -284,19 +249,7 @@ export interface HunterDomainSearchResponse extends ToolResponse {
|
||||
accept_all: boolean
|
||||
pattern: string
|
||||
organization: string
|
||||
description: string
|
||||
industry: string
|
||||
twitter: string
|
||||
facebook: string
|
||||
linkedin: string
|
||||
instagram: string
|
||||
youtube: string
|
||||
technologies: string[]
|
||||
country: string
|
||||
state: string
|
||||
city: string
|
||||
postal_code: string
|
||||
street: string
|
||||
linked_domains: string[]
|
||||
emails: HunterEmail[]
|
||||
}
|
||||
}
|
||||
@@ -311,8 +264,17 @@ export interface HunterEmailFinderParams extends HunterBaseParams {
|
||||
|
||||
export interface HunterEmailFinderResponse extends ToolResponse {
|
||||
output: {
|
||||
first_name: string
|
||||
last_name: string
|
||||
email: string
|
||||
score: number
|
||||
domain: string
|
||||
accept_all: boolean
|
||||
position: string | null
|
||||
twitter: string | null
|
||||
linkedin_url: string | null
|
||||
phone_number: string | null
|
||||
company: string | null
|
||||
sources: Array<{
|
||||
domain: string
|
||||
uri: string
|
||||
@@ -321,7 +283,7 @@ export interface HunterEmailFinderResponse extends ToolResponse {
|
||||
still_on_page: boolean
|
||||
}>
|
||||
verification: {
|
||||
date: string
|
||||
date: string | null
|
||||
status: string
|
||||
}
|
||||
}
|
||||
@@ -366,26 +328,24 @@ export interface HunterEnrichmentParams extends HunterBaseParams {
|
||||
|
||||
export interface HunterEnrichmentResponse extends ToolResponse {
|
||||
output: {
|
||||
person?: {
|
||||
first_name: string
|
||||
last_name: string
|
||||
email: string
|
||||
position: string
|
||||
seniority: string
|
||||
department: string
|
||||
linkedin: string
|
||||
twitter: string
|
||||
phone_number: string
|
||||
}
|
||||
company?: {
|
||||
name: string
|
||||
domain: string
|
||||
industry: string
|
||||
size: string
|
||||
country: string
|
||||
linkedin: string
|
||||
twitter: string
|
||||
}
|
||||
name: string
|
||||
domain: string
|
||||
description: string
|
||||
industry: string
|
||||
sector: string
|
||||
size: string
|
||||
founded_year: number | null
|
||||
location: string
|
||||
country: string
|
||||
country_code: string
|
||||
state: string
|
||||
city: string
|
||||
linkedin: string
|
||||
twitter: string
|
||||
facebook: string
|
||||
logo: string
|
||||
phone: string
|
||||
tech: string[]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user