linkedin-secret-sauce 0.12.2 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +276 -40
- package/dist/enrichment/index.d.ts +22 -3
- package/dist/enrichment/index.js +29 -38
- package/dist/enrichment/matching.d.ts +4 -8
- package/dist/enrichment/matching.js +6 -5
- package/dist/enrichment/orchestrator.d.ts +1 -1
- package/dist/enrichment/orchestrator.js +51 -44
- package/dist/enrichment/providers/index.d.ts +0 -2
- package/dist/enrichment/providers/index.js +1 -8
- package/dist/enrichment/providers/snovio.d.ts +4 -4
- package/dist/enrichment/providers/snovio.js +14 -14
- package/dist/enrichment/types.d.ts +18 -71
- package/dist/enrichment/types.js +3 -8
- package/dist/enrichment/utils/rate-limiter.js +0 -2
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -2
- package/docs/COSIALL_PROFILE_EMAILS.md +342 -0
- package/docs/ENRICHMENT.md +580 -0
- package/docs/INTEGRATION.md +403 -0
- package/docs/PLAYGROUND.md +553 -0
- package/docs/SALES_SEARCH.md +171 -0
- package/docs/api/.nojekyll +1 -0
- package/docs/api/assets/hierarchy.js +1 -0
- package/docs/api/assets/highlight.css +92 -0
- package/docs/api/assets/icons.js +18 -0
- package/docs/api/assets/icons.svg +1 -0
- package/docs/api/assets/main.js +60 -0
- package/docs/api/assets/navigation.js +1 -0
- package/docs/api/assets/search.js +1 -0
- package/docs/api/assets/style.css +1633 -0
- package/docs/api/classes/LinkedInClientError.html +37 -0
- package/docs/api/functions/_testGetAccountCookies.html +4 -0
- package/docs/api/functions/_testGetAccountEntry.html +4 -0
- package/docs/api/functions/_testGetAllAccountIds.html +3 -0
- package/docs/api/functions/_testGetPoolState.html +3 -0
- package/docs/api/functions/adminResetAccount.html +1 -0
- package/docs/api/functions/adminSetCooldown.html +1 -0
- package/docs/api/functions/buildCookieHeader.html +1 -0
- package/docs/api/functions/clearAllSmartLeadTokens.html +2 -0
- package/docs/api/functions/clearRequestHistory.html +1 -0
- package/docs/api/functions/clearSessionAccount.html +1 -0
- package/docs/api/functions/clearSmartLeadToken.html +2 -0
- package/docs/api/functions/createEnrichmentClient.html +8 -0
- package/docs/api/functions/extractCsrfToken.html +1 -0
- package/docs/api/functions/extractLinkedInHandle.html +7 -0
- package/docs/api/functions/fetchCookiesFromCosiall.html +14 -0
- package/docs/api/functions/fetchProfileEmailsFromCosiall.html +18 -0
- package/docs/api/functions/forceRefreshCookies.html +1 -0
- package/docs/api/functions/getAccountForSession.html +1 -0
- package/docs/api/functions/getAccountsSummary.html +1 -0
- package/docs/api/functions/getCompaniesBatch.html +5 -0
- package/docs/api/functions/getCompanyById.html +9 -0
- package/docs/api/functions/getCompanyByUrl.html +1 -0
- package/docs/api/functions/getConfig.html +1 -0
- package/docs/api/functions/getCookiePoolHealth.html +1 -0
- package/docs/api/functions/getProfileByUrn.html +17 -0
- package/docs/api/functions/getProfileByVanity.html +10 -0
- package/docs/api/functions/getProfilesBatch.html +1 -0
- package/docs/api/functions/getRequestHistory.html +1 -0
- package/docs/api/functions/getSalesNavigatorProfileDetails.html +1 -0
- package/docs/api/functions/getSalesNavigatorProfileFull.html +16 -0
- package/docs/api/functions/getSmartLeadToken.html +1 -0
- package/docs/api/functions/getSmartLeadTokenCacheStats.html +2 -0
- package/docs/api/functions/getSmartLeadUser.html +2 -0
- package/docs/api/functions/getSnapshot.html +1 -0
- package/docs/api/functions/getYearsAtCompanyOptions.html +2 -0
- package/docs/api/functions/getYearsInPositionOptions.html +2 -0
- package/docs/api/functions/getYearsOfExperienceOptions.html +2 -0
- package/docs/api/functions/incrementMetric.html +1 -0
- package/docs/api/functions/initializeCookiePool.html +1 -0
- package/docs/api/functions/initializeLinkedInClient.html +1 -0
- package/docs/api/functions/isBusinessEmail.html +4 -0
- package/docs/api/functions/isDisposableDomain.html +4 -0
- package/docs/api/functions/isDisposableEmail.html +4 -0
- package/docs/api/functions/isPersonalDomain.html +4 -0
- package/docs/api/functions/isPersonalEmail.html +4 -0
- package/docs/api/functions/isRoleAccount.html +4 -0
- package/docs/api/functions/isValidEmailSyntax.html +4 -0
- package/docs/api/functions/parseFullProfile.html +15 -0
- package/docs/api/functions/parseSalesSearchResults.html +1 -0
- package/docs/api/functions/reportAccountFailure.html +1 -0
- package/docs/api/functions/reportAccountSuccess.html +1 -0
- package/docs/api/functions/resolveCompanyUniversalName.html +1 -0
- package/docs/api/functions/searchSalesLeads.html +16 -0
- package/docs/api/functions/selectAccountForRequest.html +1 -0
- package/docs/api/functions/setAccountForSession.html +1 -0
- package/docs/api/functions/typeahead.html +1 -0
- package/docs/api/functions/verifyEmailMx.html +1 -0
- package/docs/api/hierarchy.html +1 -0
- package/docs/api/index.html +12 -0
- package/docs/api/interfaces/AccountCookies.html +4 -0
- package/docs/api/interfaces/BatchEnrichmentOptions.html +14 -0
- package/docs/api/interfaces/CacheAdapter.html +6 -0
- package/docs/api/interfaces/CanonicalEmail.html +14 -0
- package/docs/api/interfaces/Company.html +17 -0
- package/docs/api/interfaces/ConstructConfig.html +8 -0
- package/docs/api/interfaces/CosiallProfileEmailsResponse.html +11 -0
- package/docs/api/interfaces/EnrichmentCandidate.html +34 -0
- package/docs/api/interfaces/EnrichmentClient.html +10 -0
- package/docs/api/interfaces/EnrichmentClientConfig.html +12 -0
- package/docs/api/interfaces/EnrichmentLogger.html +6 -0
- package/docs/api/interfaces/EnrichmentOptions.html +10 -0
- package/docs/api/interfaces/HunterConfig.html +3 -0
- package/docs/api/interfaces/LddConfig.html +4 -0
- package/docs/api/interfaces/LddProfileData.html +6 -0
- package/docs/api/interfaces/LinkedInClientConfig.html +20 -0
- package/docs/api/interfaces/LinkedInCookie.html +9 -0
- package/docs/api/interfaces/LinkedInPosition.html +14 -0
- package/docs/api/interfaces/LinkedInProfile.html +21 -0
- package/docs/api/interfaces/LinkedInSpotlightBadge.html +5 -0
- package/docs/api/interfaces/LinkedInTenure.html +3 -0
- package/docs/api/interfaces/Metrics.html +22 -0
- package/docs/api/interfaces/MetricsSnapshot.html +23 -0
- package/docs/api/interfaces/ProfileEducation.html +8 -0
- package/docs/api/interfaces/ProfileEmailsLookupOptions.html +9 -0
- package/docs/api/interfaces/ProfilePosition.html +12 -0
- package/docs/api/interfaces/ProfileSkill.html +3 -0
- package/docs/api/interfaces/ProviderResult.html +11 -0
- package/docs/api/interfaces/ProvidersConfig.html +14 -0
- package/docs/api/interfaces/RequestHistoryEntry.html +8 -0
- package/docs/api/interfaces/SalesLeadSearchResult.html +31 -0
- package/docs/api/interfaces/SalesNavigatorContactInfo.html +5 -0
- package/docs/api/interfaces/SalesNavigatorPosition.html +11 -0
- package/docs/api/interfaces/SalesNavigatorProfile.html +9 -0
- package/docs/api/interfaces/SalesNavigatorProfileFull.html +24 -0
- package/docs/api/interfaces/SearchSalesResult.html +5 -0
- package/docs/api/interfaces/SmartLeadAuthConfig.html +6 -0
- package/docs/api/interfaces/SmartLeadCredentials.html +3 -0
- package/docs/api/interfaces/SmartLeadLoginResponse.html +3 -0
- package/docs/api/interfaces/SmartLeadUser.html +8 -0
- package/docs/api/interfaces/SmartProspectConfig.html +19 -0
- package/docs/api/interfaces/SmartProspectContact.html +24 -0
- package/docs/api/interfaces/SmartProspectSearchFilters.html +42 -0
- package/docs/api/interfaces/TypeaheadItem.html +3 -0
- package/docs/api/interfaces/TypeaheadResult.html +3 -0
- package/docs/api/interfaces/VerificationResult.html +16 -0
- package/docs/api/types/CostCallback.html +2 -0
- package/docs/api/types/Geo.html +4 -0
- package/docs/api/types/LddApiResponse.html +1 -0
- package/docs/api/types/ProviderFunc.html +2 -0
- package/docs/api/types/ProviderName.html +2 -0
- package/docs/api/types/SalesSearchFilters.html +7 -0
- package/docs/api/types/TypeaheadType.html +1 -0
- package/docs/api/variables/COMPANY_SIZE_OPTIONS.html +1 -0
- package/docs/api/variables/DEFAULT_PROVIDER_ORDER.html +19 -0
- package/docs/api/variables/DISPOSABLE_DOMAINS.html +2 -0
- package/docs/api/variables/FUNCTION_OPTIONS.html +1 -0
- package/docs/api/variables/INDUSTRY_OPTIONS.html +1 -0
- package/docs/api/variables/LANGUAGE_OPTIONS.html +1 -0
- package/docs/api/variables/PERSONAL_DOMAINS.html +2 -0
- package/docs/api/variables/PROVIDER_COSTS.html +13 -0
- package/docs/api/variables/REGION_OPTIONS.html +1 -0
- package/docs/api/variables/SENIORITY_OPTIONS.html +3 -0
- package/docs/api/variables/YEARS_OPTIONS.html +1 -0
- package/docs/index.html +98 -0
- package/package.json +40 -28
- package/dist/enrichment/providers/apollo.d.ts +0 -11
- package/dist/enrichment/providers/apollo.js +0 -181
- package/dist/enrichment/providers/bouncer.d.ts +0 -67
- package/dist/enrichment/providers/bouncer.js +0 -231
- package/dist/enrichment/providers/dropcontact.d.ts +0 -22
- package/dist/enrichment/providers/dropcontact.js +0 -206
|
@@ -738,7 +738,7 @@ function createLinkedInEnricher(smartProspectConfig, defaultOptions = {}) {
|
|
|
738
738
|
* Strategy (optimized for FlexIQ):
|
|
739
739
|
* 1. Query LDD + SmartProspect in PARALLEL (both FREE for you)
|
|
740
740
|
* 2. Try email pattern guessing with MX verification (FREE)
|
|
741
|
-
* 3. Only use Hunter/
|
|
741
|
+
* 3. Only use Hunter/Snov.io if confidence is below threshold (PAID - last resort)
|
|
742
742
|
*
|
|
743
743
|
* @example
|
|
744
744
|
* ```ts
|
|
@@ -957,7 +957,7 @@ async function getEmailsForLinkedInContact(contactOrLead, config, options = {})
|
|
|
957
957
|
// ==========================================================================
|
|
958
958
|
if (!skipPaidProviders && finalBestConfidence < paidProviderThreshold) {
|
|
959
959
|
// Only use paid providers if we have low confidence or no results
|
|
960
|
-
// Hunter.io - Email Finder ($0.
|
|
960
|
+
// Hunter.io - Email Finder ($0.015/lookup)
|
|
961
961
|
if (config.hunter?.apiKey) {
|
|
962
962
|
result.providersQueried.push("hunter");
|
|
963
963
|
// Extract linkedin handle from contact if available
|
|
@@ -981,7 +981,9 @@ async function getEmailsForLinkedInContact(contactOrLead, config, options = {})
|
|
|
981
981
|
result.emails = mergeAndBoostEmails();
|
|
982
982
|
}
|
|
983
983
|
// Snovio - Email Finder ($0.02/lookup) - best for catch-all domains
|
|
984
|
-
if (config.snovio?.
|
|
984
|
+
if (config.snovio?.clientId &&
|
|
985
|
+
config.snovio?.clientSecret &&
|
|
986
|
+
companyDomain) {
|
|
985
987
|
result.providersQueried.push("snovio");
|
|
986
988
|
await querySnovio(contact, config.snovio, companyDomain, addEmail, result);
|
|
987
989
|
// Re-merge after Snovio
|
|
@@ -1004,8 +1006,7 @@ async function getEmailsForLinkedInContact(contactOrLead, config, options = {})
|
|
|
1004
1006
|
pattern: 5,
|
|
1005
1007
|
bounceban: 6, // BounceBan catch-all verification (FREE single)
|
|
1006
1008
|
hunter: 7,
|
|
1007
|
-
|
|
1008
|
-
snovio: 9,
|
|
1009
|
+
snovio: 8,
|
|
1009
1010
|
};
|
|
1010
1011
|
result.emails.sort((a, b) => {
|
|
1011
1012
|
if (b.confidence !== a.confidence) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Runs providers in sequence until a verified email is found that meets
|
|
5
5
|
* the confidence threshold. Supports budget tracking and cost callbacks.
|
|
6
6
|
*/
|
|
7
|
-
import type { CanonicalEmail, EnrichmentCandidate, ProviderFunc, BatchEnrichmentOptions, CostCallback, EnrichmentLogger, MultiEmailResult } from
|
|
7
|
+
import type { CanonicalEmail, EnrichmentCandidate, ProviderFunc, BatchEnrichmentOptions, CostCallback, EnrichmentLogger, MultiEmailResult } from "./types";
|
|
8
8
|
export interface OrchestratorOptions {
|
|
9
9
|
/** Provider functions in order */
|
|
10
10
|
providers: ProviderFunc[];
|
|
@@ -20,18 +20,16 @@ const validation_1 = require("./utils/validation");
|
|
|
20
20
|
* - ldd: FREE (subscription-based)
|
|
21
21
|
* - smartprospect: FREE (included in SmartLead subscription)
|
|
22
22
|
* - construct: FREE (pattern guessing + MX check)
|
|
23
|
-
* -
|
|
23
|
+
* - bounceban: FREE single / $0.003 bulk (catch-all specialist)
|
|
24
|
+
* - hunter: $0.015/email (Growth tier)
|
|
24
25
|
* - snovio: $0.02/email (email finding + verification)
|
|
25
|
-
* - hunter: $0.005/email
|
|
26
|
-
* - dropcontact: $0.01/email (not in default order)
|
|
27
26
|
*/
|
|
28
27
|
const _PROVIDER_COSTS = {
|
|
29
28
|
construct: 0,
|
|
30
29
|
ldd: 0,
|
|
31
30
|
smartprospect: 0,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
bouncer: 0.006,
|
|
31
|
+
bounceban: 0.003,
|
|
32
|
+
hunter: 0.015,
|
|
35
33
|
snovio: 0.02,
|
|
36
34
|
};
|
|
37
35
|
/**
|
|
@@ -49,9 +47,9 @@ function normalizeProviderResult(providerName, raw) {
|
|
|
49
47
|
};
|
|
50
48
|
}
|
|
51
49
|
const email = String(raw.email);
|
|
52
|
-
const confidence = typeof raw.score ===
|
|
50
|
+
const confidence = typeof raw.score === "number"
|
|
53
51
|
? raw.score
|
|
54
|
-
: typeof raw.confidence ===
|
|
52
|
+
: typeof raw.confidence === "number"
|
|
55
53
|
? raw.confidence
|
|
56
54
|
: undefined;
|
|
57
55
|
const verified = Boolean(raw.verified);
|
|
@@ -67,7 +65,8 @@ function normalizeProviderResult(providerName, raw) {
|
|
|
67
65
|
* Get provider name from function
|
|
68
66
|
*/
|
|
69
67
|
function getProviderName(provider, index) {
|
|
70
|
-
return provider.__name ??
|
|
68
|
+
return (provider.__name ??
|
|
69
|
+
`provider-${index}`);
|
|
71
70
|
}
|
|
72
71
|
/**
|
|
73
72
|
* Get provider cost
|
|
@@ -79,7 +78,7 @@ function getProviderCost(providerName) {
|
|
|
79
78
|
* Check if a provider result is a multi-result (returns multiple emails)
|
|
80
79
|
*/
|
|
81
80
|
function isMultiResult(result) {
|
|
82
|
-
return result !== null &&
|
|
81
|
+
return result !== null && "emails" in result && Array.isArray(result.emails);
|
|
83
82
|
}
|
|
84
83
|
/**
|
|
85
84
|
* Enrich a single candidate with business email
|
|
@@ -87,8 +86,10 @@ function isMultiResult(result) {
|
|
|
87
86
|
async function enrichBusinessEmail(candidate, options) {
|
|
88
87
|
const { providers, maxCostPerEmail = Infinity, confidenceThreshold = 0, retryMs = 200, onCost, logger, } = options;
|
|
89
88
|
// Track remaining budget
|
|
90
|
-
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
91
|
-
|
|
89
|
+
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
90
|
+
? maxCostPerEmail
|
|
91
|
+
: Infinity;
|
|
92
|
+
logger?.debug?.("enrichment.start", {
|
|
92
93
|
providers: providers.length,
|
|
93
94
|
confidenceThreshold,
|
|
94
95
|
maxCostPerEmail,
|
|
@@ -99,7 +100,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
99
100
|
const stepCost = getProviderCost(providerName);
|
|
100
101
|
// Skip if cost exceeds remaining budget
|
|
101
102
|
if (stepCost > 0 && remaining < stepCost) {
|
|
102
|
-
logger?.debug?.(
|
|
103
|
+
logger?.debug?.("enrichment.skip_budget", {
|
|
103
104
|
provider: providerName,
|
|
104
105
|
cost: stepCost,
|
|
105
106
|
remaining,
|
|
@@ -108,15 +109,15 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
108
109
|
}
|
|
109
110
|
let raw = null;
|
|
110
111
|
try {
|
|
111
|
-
logger?.debug?.(
|
|
112
|
+
logger?.debug?.("enrichment.provider.start", { provider: providerName });
|
|
112
113
|
raw = await provider(candidate);
|
|
113
|
-
logger?.debug?.(
|
|
114
|
+
logger?.debug?.("enrichment.provider.done", {
|
|
114
115
|
provider: providerName,
|
|
115
116
|
hasResult: !!(raw && (isMultiResult(raw) ? raw.emails.length > 0 : raw.email)),
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
119
|
catch (error) {
|
|
119
|
-
logger?.warn?.(
|
|
120
|
+
logger?.warn?.("enrichment.provider.error", {
|
|
120
121
|
provider: providerName,
|
|
121
122
|
error: error instanceof Error ? error.message : String(error),
|
|
122
123
|
});
|
|
@@ -125,7 +126,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
125
126
|
await new Promise((r) => setTimeout(r, retryMs));
|
|
126
127
|
try {
|
|
127
128
|
raw = await provider(candidate);
|
|
128
|
-
logger?.debug?.(
|
|
129
|
+
logger?.debug?.("enrichment.provider.retry.done", {
|
|
129
130
|
provider: providerName,
|
|
130
131
|
hasResult: !!(raw && (isMultiResult(raw) ? raw.emails.length > 0 : raw.email)),
|
|
131
132
|
});
|
|
@@ -160,7 +161,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
160
161
|
// Ignore cost callback errors
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
|
-
logger?.debug?.(
|
|
164
|
+
logger?.debug?.("enrichment.cost.debit", {
|
|
164
165
|
provider: providerName,
|
|
165
166
|
cost: stepCost,
|
|
166
167
|
remaining,
|
|
@@ -174,7 +175,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
174
175
|
}
|
|
175
176
|
// Filter out personal emails
|
|
176
177
|
if ((0, personal_domains_1.isPersonalEmail)(normalized.business_email)) {
|
|
177
|
-
logger?.debug?.(
|
|
178
|
+
logger?.debug?.("enrichment.skip_personal", {
|
|
178
179
|
provider: providerName,
|
|
179
180
|
email: normalized.business_email,
|
|
180
181
|
});
|
|
@@ -182,7 +183,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
182
183
|
}
|
|
183
184
|
// Filter out disposable emails
|
|
184
185
|
if ((0, disposable_domains_1.isDisposableEmail)(normalized.business_email)) {
|
|
185
|
-
logger?.debug?.(
|
|
186
|
+
logger?.debug?.("enrichment.skip_disposable", {
|
|
186
187
|
provider: providerName,
|
|
187
188
|
email: normalized.business_email,
|
|
188
189
|
});
|
|
@@ -190,8 +191,10 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
190
191
|
}
|
|
191
192
|
// Check confidence threshold
|
|
192
193
|
const score = normalized.business_email_confidence;
|
|
193
|
-
if (confidenceThreshold > 0 &&
|
|
194
|
-
|
|
194
|
+
if (confidenceThreshold > 0 &&
|
|
195
|
+
score !== undefined &&
|
|
196
|
+
score < confidenceThreshold) {
|
|
197
|
+
logger?.debug?.("enrichment.skip_low_confidence", {
|
|
195
198
|
provider: providerName,
|
|
196
199
|
score,
|
|
197
200
|
threshold: confidenceThreshold,
|
|
@@ -200,14 +203,14 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
200
203
|
}
|
|
201
204
|
// Check if verified
|
|
202
205
|
if (!normalized.business_email_verified) {
|
|
203
|
-
logger?.debug?.(
|
|
206
|
+
logger?.debug?.("enrichment.skip_unverified", {
|
|
204
207
|
provider: providerName,
|
|
205
208
|
email: normalized.business_email,
|
|
206
209
|
});
|
|
207
210
|
continue;
|
|
208
211
|
}
|
|
209
212
|
// Success!
|
|
210
|
-
logger?.info?.(
|
|
213
|
+
logger?.info?.("enrichment.success", {
|
|
211
214
|
provider: providerName,
|
|
212
215
|
email: normalized.business_email,
|
|
213
216
|
confidence: normalized.business_email_confidence,
|
|
@@ -215,7 +218,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
215
218
|
return normalized;
|
|
216
219
|
}
|
|
217
220
|
// No provider found a valid email
|
|
218
|
-
logger?.debug?.(
|
|
221
|
+
logger?.debug?.("enrichment.not_found", {
|
|
219
222
|
candidateHasName: !!(candidate.firstName || candidate.name),
|
|
220
223
|
candidateHasDomain: !!(candidate.domain || candidate.company),
|
|
221
224
|
});
|
|
@@ -224,7 +227,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
224
227
|
business_email_source: null,
|
|
225
228
|
business_email_verified: false,
|
|
226
229
|
last_checked_at: new Date().toISOString(),
|
|
227
|
-
status:
|
|
230
|
+
status: "not_found",
|
|
228
231
|
};
|
|
229
232
|
}
|
|
230
233
|
/**
|
|
@@ -241,7 +244,7 @@ async function enrichBatch(candidates, options) {
|
|
|
241
244
|
// Combine results with candidates (handle both fulfilled and rejected)
|
|
242
245
|
batchResults.forEach((result, idx) => {
|
|
243
246
|
const candidate = chunk[idx];
|
|
244
|
-
if (result.status ===
|
|
247
|
+
if (result.status === "fulfilled") {
|
|
245
248
|
results.push({ candidate, ...result.value });
|
|
246
249
|
}
|
|
247
250
|
else {
|
|
@@ -252,7 +255,7 @@ async function enrichBatch(candidates, options) {
|
|
|
252
255
|
business_email_source: null,
|
|
253
256
|
business_email_verified: false,
|
|
254
257
|
last_checked_at: new Date().toISOString(),
|
|
255
|
-
status: `error: ${result.reason instanceof Error ? result.reason.message :
|
|
258
|
+
status: `error: ${result.reason instanceof Error ? result.reason.message : "unknown"}`,
|
|
256
259
|
});
|
|
257
260
|
}
|
|
258
261
|
});
|
|
@@ -271,16 +274,16 @@ async function enrichBatch(candidates, options) {
|
|
|
271
274
|
*/
|
|
272
275
|
function classifyEmailType(email) {
|
|
273
276
|
if ((0, disposable_domains_1.isDisposableEmail)(email)) {
|
|
274
|
-
return
|
|
277
|
+
return "disposable";
|
|
275
278
|
}
|
|
276
279
|
if ((0, personal_domains_1.isPersonalEmail)(email)) {
|
|
277
|
-
return
|
|
280
|
+
return "personal";
|
|
278
281
|
}
|
|
279
282
|
if ((0, validation_1.isRoleAccount)(email)) {
|
|
280
|
-
return
|
|
283
|
+
return "role";
|
|
281
284
|
}
|
|
282
285
|
// If it's not personal, disposable, or role, it's likely business
|
|
283
|
-
return
|
|
286
|
+
return "business";
|
|
284
287
|
}
|
|
285
288
|
/**
|
|
286
289
|
* Enrich a candidate and collect ALL emails from ALL providers
|
|
@@ -296,10 +299,12 @@ async function enrichAllEmails(candidate, options) {
|
|
|
296
299
|
const providersQueried = [];
|
|
297
300
|
let totalCost = 0;
|
|
298
301
|
// Track remaining budget
|
|
299
|
-
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
302
|
+
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
303
|
+
? maxCostPerEmail
|
|
304
|
+
: Infinity;
|
|
300
305
|
// Track seen emails to avoid duplicates
|
|
301
306
|
const seenEmails = new Set();
|
|
302
|
-
logger?.debug?.(
|
|
307
|
+
logger?.debug?.("enrichment.all.start", {
|
|
303
308
|
providers: providers.length,
|
|
304
309
|
maxCostPerEmail,
|
|
305
310
|
});
|
|
@@ -309,7 +314,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
309
314
|
const stepCost = getProviderCost(providerName);
|
|
310
315
|
// Skip if cost exceeds remaining budget
|
|
311
316
|
if (stepCost > 0 && remaining < stepCost) {
|
|
312
|
-
logger?.debug?.(
|
|
317
|
+
logger?.debug?.("enrichment.all.skip_budget", {
|
|
313
318
|
provider: providerName,
|
|
314
319
|
cost: stepCost,
|
|
315
320
|
remaining,
|
|
@@ -319,16 +324,18 @@ async function enrichAllEmails(candidate, options) {
|
|
|
319
324
|
providersQueried.push(providerName);
|
|
320
325
|
let raw = null;
|
|
321
326
|
try {
|
|
322
|
-
logger?.debug?.(
|
|
327
|
+
logger?.debug?.("enrichment.all.provider.start", {
|
|
328
|
+
provider: providerName,
|
|
329
|
+
});
|
|
323
330
|
raw = await provider(candidate);
|
|
324
|
-
logger?.debug?.(
|
|
331
|
+
logger?.debug?.("enrichment.all.provider.done", {
|
|
325
332
|
provider: providerName,
|
|
326
333
|
hasResult: !!raw,
|
|
327
334
|
isMulti: isMultiResult(raw),
|
|
328
335
|
});
|
|
329
336
|
}
|
|
330
337
|
catch (error) {
|
|
331
|
-
logger?.warn?.(
|
|
338
|
+
logger?.warn?.("enrichment.all.provider.error", {
|
|
332
339
|
provider: providerName,
|
|
333
340
|
error: error instanceof Error ? error.message : String(error),
|
|
334
341
|
});
|
|
@@ -337,7 +344,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
337
344
|
await new Promise((r) => setTimeout(r, retryMs));
|
|
338
345
|
try {
|
|
339
346
|
raw = await provider(candidate);
|
|
340
|
-
logger?.debug?.(
|
|
347
|
+
logger?.debug?.("enrichment.all.provider.retry.done", {
|
|
341
348
|
provider: providerName,
|
|
342
349
|
hasResult: !!raw,
|
|
343
350
|
});
|
|
@@ -359,7 +366,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
359
366
|
// Ignore cost callback errors
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
|
-
logger?.debug?.(
|
|
369
|
+
logger?.debug?.("enrichment.all.cost.debit", {
|
|
363
370
|
provider: providerName,
|
|
364
371
|
cost: stepCost,
|
|
365
372
|
remaining,
|
|
@@ -393,9 +400,9 @@ async function enrichAllEmails(candidate, options) {
|
|
|
393
400
|
const emailLower = raw.email.toLowerCase();
|
|
394
401
|
if (!seenEmails.has(emailLower)) {
|
|
395
402
|
seenEmails.add(emailLower);
|
|
396
|
-
const confidence = typeof raw.score ===
|
|
403
|
+
const confidence = typeof raw.score === "number"
|
|
397
404
|
? raw.score
|
|
398
|
-
: typeof raw.confidence ===
|
|
405
|
+
: typeof raw.confidence === "number"
|
|
399
406
|
? raw.confidence
|
|
400
407
|
: 50;
|
|
401
408
|
allEmails.push({
|
|
@@ -425,7 +432,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
425
432
|
// Then by type priority
|
|
426
433
|
return typeOrder[a.type] - typeOrder[b.type];
|
|
427
434
|
});
|
|
428
|
-
logger?.info?.(
|
|
435
|
+
logger?.info?.("enrichment.all.complete", {
|
|
429
436
|
emailsFound: allEmails.length,
|
|
430
437
|
providersQueried: providersQueried.length,
|
|
431
438
|
totalCost,
|
|
@@ -451,7 +458,7 @@ async function enrichAllBatch(candidates, options) {
|
|
|
451
458
|
const batchResults = await Promise.allSettled(chunk.map((candidate) => enrichAllEmails(candidate, options)));
|
|
452
459
|
// Handle both fulfilled and rejected results
|
|
453
460
|
batchResults.forEach((result, idx) => {
|
|
454
|
-
if (result.status ===
|
|
461
|
+
if (result.status === "fulfilled") {
|
|
455
462
|
results.push(result.value);
|
|
456
463
|
}
|
|
457
464
|
else {
|
|
@@ -7,7 +7,5 @@ export { createSmartProspectProvider } from "./smartprospect";
|
|
|
7
7
|
export { createCosiallProvider } from "./cosiall";
|
|
8
8
|
export { createTryKittProvider, findEmailWithTryKitt, verifyEmailWithTryKitt, } from "./trykitt";
|
|
9
9
|
export { createHunterProvider } from "./hunter";
|
|
10
|
-
export { createDropcontactProvider } from "./dropcontact";
|
|
11
|
-
export { createBouncerProvider, verifyEmailWithBouncer, checkCatchAllDomain, verifyEmailsBatch, } from "./bouncer";
|
|
12
10
|
export { createBounceBanProvider, verifyEmailWithBounceBan, verifyEmailsBatchWithBounceBan, checkCatchAllWithBounceBan, } from "./bounceban";
|
|
13
11
|
export { createSnovioProvider, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache, } from "./snovio";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Email Enrichment Providers
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.createSnovioProvider = exports.checkCatchAllWithBounceBan = exports.verifyEmailsBatchWithBounceBan = exports.verifyEmailWithBounceBan = exports.createBounceBanProvider = exports.
|
|
6
|
+
exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.createSnovioProvider = exports.checkCatchAllWithBounceBan = exports.verifyEmailsBatchWithBounceBan = exports.verifyEmailWithBounceBan = exports.createBounceBanProvider = exports.createHunterProvider = exports.verifyEmailWithTryKitt = exports.findEmailWithTryKitt = exports.createTryKittProvider = exports.createCosiallProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = void 0;
|
|
7
7
|
var construct_1 = require("./construct");
|
|
8
8
|
Object.defineProperty(exports, "createConstructProvider", { enumerable: true, get: function () { return construct_1.createConstructProvider; } });
|
|
9
9
|
var ldd_1 = require("./ldd");
|
|
@@ -18,13 +18,6 @@ Object.defineProperty(exports, "findEmailWithTryKitt", { enumerable: true, get:
|
|
|
18
18
|
Object.defineProperty(exports, "verifyEmailWithTryKitt", { enumerable: true, get: function () { return trykitt_1.verifyEmailWithTryKitt; } });
|
|
19
19
|
var hunter_1 = require("./hunter");
|
|
20
20
|
Object.defineProperty(exports, "createHunterProvider", { enumerable: true, get: function () { return hunter_1.createHunterProvider; } });
|
|
21
|
-
var dropcontact_1 = require("./dropcontact");
|
|
22
|
-
Object.defineProperty(exports, "createDropcontactProvider", { enumerable: true, get: function () { return dropcontact_1.createDropcontactProvider; } });
|
|
23
|
-
var bouncer_1 = require("./bouncer");
|
|
24
|
-
Object.defineProperty(exports, "createBouncerProvider", { enumerable: true, get: function () { return bouncer_1.createBouncerProvider; } });
|
|
25
|
-
Object.defineProperty(exports, "verifyEmailWithBouncer", { enumerable: true, get: function () { return bouncer_1.verifyEmailWithBouncer; } });
|
|
26
|
-
Object.defineProperty(exports, "checkCatchAllDomain", { enumerable: true, get: function () { return bouncer_1.checkCatchAllDomain; } });
|
|
27
|
-
Object.defineProperty(exports, "verifyEmailsBatch", { enumerable: true, get: function () { return bouncer_1.verifyEmailsBatch; } });
|
|
28
21
|
var bounceban_1 = require("./bounceban");
|
|
29
22
|
Object.defineProperty(exports, "createBounceBanProvider", { enumerable: true, get: function () { return bounceban_1.createBounceBanProvider; } });
|
|
30
23
|
Object.defineProperty(exports, "verifyEmailWithBounceBan", { enumerable: true, get: function () { return bounceban_1.verifyEmailWithBounceBan; } });
|
|
@@ -30,8 +30,8 @@ export declare function createSnovioProvider(config: SnovioConfig): (candidate:
|
|
|
30
30
|
* 'Doe',
|
|
31
31
|
* 'example.com',
|
|
32
32
|
* {
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* clientId: process.env.SNOVIO_CLIENT_ID,
|
|
34
|
+
* clientSecret: process.env.SNOVIO_CLIENT_SECRET,
|
|
35
35
|
* }
|
|
36
36
|
* );
|
|
37
37
|
* ```
|
|
@@ -43,8 +43,8 @@ export declare function findEmailsWithSnovio(firstName: string, lastName: string
|
|
|
43
43
|
* @example
|
|
44
44
|
* ```typescript
|
|
45
45
|
* const result = await verifyEmailWithSnovio('test@example.com', {
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* clientId: process.env.SNOVIO_CLIENT_ID,
|
|
47
|
+
* clientSecret: process.env.SNOVIO_CLIENT_SECRET,
|
|
48
48
|
* });
|
|
49
49
|
* ```
|
|
50
50
|
*/
|
|
@@ -68,7 +68,7 @@ function extractNameAndDomain(candidate) {
|
|
|
68
68
|
/**
|
|
69
69
|
* Get OAuth access token from Snov.io
|
|
70
70
|
*/
|
|
71
|
-
async function getAccessToken(
|
|
71
|
+
async function getAccessToken(clientId, clientSecret, apiUrl, timeoutMs) {
|
|
72
72
|
// Return cached token if valid
|
|
73
73
|
if (cachedAccessToken && Date.now() < tokenExpiresAt) {
|
|
74
74
|
return cachedAccessToken;
|
|
@@ -83,8 +83,8 @@ async function getAccessToken(userId, apiSecret, apiUrl, timeoutMs) {
|
|
|
83
83
|
},
|
|
84
84
|
body: JSON.stringify({
|
|
85
85
|
grant_type: "client_credentials",
|
|
86
|
-
client_id:
|
|
87
|
-
client_secret:
|
|
86
|
+
client_id: clientId,
|
|
87
|
+
client_secret: clientSecret,
|
|
88
88
|
}),
|
|
89
89
|
signal: controller.signal,
|
|
90
90
|
});
|
|
@@ -151,8 +151,8 @@ async function findEmailsByName(firstName, lastName, domain, accessToken, apiUrl
|
|
|
151
151
|
* when pattern guessing fails or for catch-all domains.
|
|
152
152
|
*/
|
|
153
153
|
function createSnovioProvider(config) {
|
|
154
|
-
const {
|
|
155
|
-
if (!
|
|
154
|
+
const { clientId, clientSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
155
|
+
if (!clientId || !clientSecret) {
|
|
156
156
|
return (0, noop_provider_1.createNoOpProvider)("snovio");
|
|
157
157
|
}
|
|
158
158
|
async function findEmails(candidate) {
|
|
@@ -162,7 +162,7 @@ function createSnovioProvider(config) {
|
|
|
162
162
|
}
|
|
163
163
|
const { firstName, lastName, domain } = nameAndDomain;
|
|
164
164
|
// Get access token
|
|
165
|
-
const accessToken = await getAccessToken(
|
|
165
|
+
const accessToken = await getAccessToken(clientId, clientSecret, apiUrl, timeoutMs);
|
|
166
166
|
if (!accessToken) {
|
|
167
167
|
return null;
|
|
168
168
|
}
|
|
@@ -210,15 +210,15 @@ function createSnovioProvider(config) {
|
|
|
210
210
|
* 'Doe',
|
|
211
211
|
* 'example.com',
|
|
212
212
|
* {
|
|
213
|
-
*
|
|
214
|
-
*
|
|
213
|
+
* clientId: process.env.SNOVIO_CLIENT_ID,
|
|
214
|
+
* clientSecret: process.env.SNOVIO_CLIENT_SECRET,
|
|
215
215
|
* }
|
|
216
216
|
* );
|
|
217
217
|
* ```
|
|
218
218
|
*/
|
|
219
219
|
async function findEmailsWithSnovio(firstName, lastName, domain, config) {
|
|
220
|
-
const {
|
|
221
|
-
const accessToken = await getAccessToken(
|
|
220
|
+
const { clientId, clientSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
221
|
+
const accessToken = await getAccessToken(clientId, clientSecret, apiUrl, timeoutMs);
|
|
222
222
|
if (!accessToken) {
|
|
223
223
|
return null;
|
|
224
224
|
}
|
|
@@ -230,14 +230,14 @@ async function findEmailsWithSnovio(firstName, lastName, domain, config) {
|
|
|
230
230
|
* @example
|
|
231
231
|
* ```typescript
|
|
232
232
|
* const result = await verifyEmailWithSnovio('test@example.com', {
|
|
233
|
-
*
|
|
234
|
-
*
|
|
233
|
+
* clientId: process.env.SNOVIO_CLIENT_ID,
|
|
234
|
+
* clientSecret: process.env.SNOVIO_CLIENT_SECRET,
|
|
235
235
|
* });
|
|
236
236
|
* ```
|
|
237
237
|
*/
|
|
238
238
|
async function verifyEmailWithSnovio(email, config) {
|
|
239
|
-
const {
|
|
240
|
-
const accessToken = await getAccessToken(
|
|
239
|
+
const { clientId, clientSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
240
|
+
const accessToken = await getAccessToken(clientId, clientSecret, apiUrl, timeoutMs);
|
|
241
241
|
if (!accessToken) {
|
|
242
242
|
return null;
|
|
243
243
|
}
|
|
@@ -24,10 +24,20 @@ export interface CanonicalEmail {
|
|
|
24
24
|
/**
|
|
25
25
|
* Raw result from a provider before normalization
|
|
26
26
|
*/
|
|
27
|
+
/**
|
|
28
|
+
* Result returned by a single-email provider
|
|
29
|
+
*/
|
|
27
30
|
export interface ProviderResult {
|
|
31
|
+
/** The email address found */
|
|
28
32
|
email?: string | null;
|
|
33
|
+
/** Whether the email was verified */
|
|
29
34
|
verified?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Confidence score (0-100)
|
|
37
|
+
* @deprecated Use `confidence` instead - both are supported for backwards compatibility
|
|
38
|
+
*/
|
|
30
39
|
score?: number;
|
|
40
|
+
/** Confidence score (0-100) */
|
|
31
41
|
confidence?: number;
|
|
32
42
|
}
|
|
33
43
|
/**
|
|
@@ -157,12 +167,6 @@ export interface LddConfig {
|
|
|
157
167
|
apiUrl: string;
|
|
158
168
|
apiToken: string;
|
|
159
169
|
}
|
|
160
|
-
/**
|
|
161
|
-
* Dropcontact provider configuration
|
|
162
|
-
*/
|
|
163
|
-
export interface DropcontactConfig {
|
|
164
|
-
apiKey: string;
|
|
165
|
-
}
|
|
166
170
|
/**
|
|
167
171
|
* TryKitt.ai provider configuration
|
|
168
172
|
*
|
|
@@ -354,57 +358,6 @@ export interface BounceBanVerifyResponse {
|
|
|
354
358
|
/** Remaining credits */
|
|
355
359
|
credits_remaining?: number;
|
|
356
360
|
}
|
|
357
|
-
/**
|
|
358
|
-
* Bouncer.io provider configuration
|
|
359
|
-
*
|
|
360
|
-
* Bouncer provides SMTP email verification with 99%+ accuracy.
|
|
361
|
-
* Best for verifying pattern-guessed emails on non-catch-all domains.
|
|
362
|
-
*
|
|
363
|
-
* @see https://docs.usebouncer.com
|
|
364
|
-
*/
|
|
365
|
-
export interface BouncerConfig {
|
|
366
|
-
/** Bouncer API key */
|
|
367
|
-
apiKey: string;
|
|
368
|
-
/** API URL override (default: https://api.usebouncer.com/v1.1) */
|
|
369
|
-
apiUrl?: string;
|
|
370
|
-
/** Timeout in ms (default: 30000) */
|
|
371
|
-
timeoutMs?: number;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Bouncer verification result status
|
|
375
|
-
*/
|
|
376
|
-
export type BouncerStatus = "deliverable" | "undeliverable" | "risky" | "unknown";
|
|
377
|
-
/**
|
|
378
|
-
* Bouncer verification result reason
|
|
379
|
-
*/
|
|
380
|
-
export type BouncerReason = "accepted_email" | "rejected_email" | "invalid_domain" | "invalid_email" | "unavailable_smtp" | "dns_error" | "low_deliverability" | "low_quality" | "catch_all" | "full_mailbox" | "role_account" | "disposable" | "timeout" | "unknown";
|
|
381
|
-
/**
|
|
382
|
-
* Bouncer API response for single email verification
|
|
383
|
-
*/
|
|
384
|
-
export interface BouncerVerifyResponse {
|
|
385
|
-
status: BouncerStatus;
|
|
386
|
-
reason: BouncerReason;
|
|
387
|
-
email: string;
|
|
388
|
-
domain: string;
|
|
389
|
-
account: string;
|
|
390
|
-
/** Whether this is a free email provider (Gmail, Yahoo, etc.) */
|
|
391
|
-
free: boolean;
|
|
392
|
-
/** Whether this is a disposable email */
|
|
393
|
-
disposable: boolean;
|
|
394
|
-
/** Whether this is a role-based email */
|
|
395
|
-
role: boolean;
|
|
396
|
-
/** Whether domain is catch-all (accepts all emails) */
|
|
397
|
-
acceptAll: boolean;
|
|
398
|
-
/** Did you mean suggestion for typos */
|
|
399
|
-
didYouMean?: string;
|
|
400
|
-
/** DNS information */
|
|
401
|
-
dns?: {
|
|
402
|
-
type: string;
|
|
403
|
-
record: string;
|
|
404
|
-
};
|
|
405
|
-
/** Toxicity score (0-5, higher = more risky) */
|
|
406
|
-
toxicity?: number;
|
|
407
|
-
}
|
|
408
361
|
/**
|
|
409
362
|
* Snov.io provider configuration
|
|
410
363
|
*
|
|
@@ -414,10 +367,10 @@ export interface BouncerVerifyResponse {
|
|
|
414
367
|
* @see https://snov.io/api
|
|
415
368
|
*/
|
|
416
369
|
export interface SnovioConfig {
|
|
417
|
-
/** Snov.io
|
|
418
|
-
|
|
419
|
-
/** Snov.io
|
|
420
|
-
|
|
370
|
+
/** Snov.io OAuth client ID (get from https://app.snov.io/api) */
|
|
371
|
+
clientId: string;
|
|
372
|
+
/** Snov.io OAuth client secret */
|
|
373
|
+
clientSecret: string;
|
|
421
374
|
/** API URL override (default: https://api.snov.io) */
|
|
422
375
|
apiUrl?: string;
|
|
423
376
|
/** Timeout in ms (default: 30000) */
|
|
@@ -490,9 +443,6 @@ export interface ProvidersConfig {
|
|
|
490
443
|
/** TryKitt.ai AI email finder (FREE for individuals, unlimited) */
|
|
491
444
|
trykitt?: TryKittConfig;
|
|
492
445
|
hunter?: HunterConfig;
|
|
493
|
-
dropcontact?: DropcontactConfig;
|
|
494
|
-
/** Bouncer.io for SMTP email verification (99%+ accuracy) */
|
|
495
|
-
bouncer?: BouncerConfig;
|
|
496
446
|
/** BounceBan catch-all verification (FREE single, 85-95% accuracy) */
|
|
497
447
|
bounceban?: BounceBanConfig;
|
|
498
448
|
/** Snov.io for email finding (98% delivery rate) */
|
|
@@ -506,7 +456,7 @@ export interface EnrichmentOptions {
|
|
|
506
456
|
maxCostPerEmail?: number;
|
|
507
457
|
/** Minimum confidence threshold 0-100 (default: 0) */
|
|
508
458
|
confidenceThreshold?: number;
|
|
509
|
-
/** Provider order (default: ['ldd', 'smartprospect', '
|
|
459
|
+
/** Provider order (default: ['ldd', 'smartprospect', 'cosiall', 'trykitt', 'construct', 'bounceban', 'hunter', 'snovio']) */
|
|
510
460
|
providerOrder?: ProviderName[];
|
|
511
461
|
/** Retry delay in ms on transient errors (default: 200) */
|
|
512
462
|
retryMs?: number;
|
|
@@ -575,7 +525,7 @@ export interface EnrichmentClient {
|
|
|
575
525
|
/**
|
|
576
526
|
* Available provider names
|
|
577
527
|
*/
|
|
578
|
-
export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "trykitt" | "hunter" | "
|
|
528
|
+
export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "trykitt" | "hunter" | "bounceban" | "snovio";
|
|
579
529
|
/**
|
|
580
530
|
* Default provider order - 3-Phase Strategy
|
|
581
531
|
*
|
|
@@ -590,10 +540,9 @@ export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "
|
|
|
590
540
|
* - bounceban: BounceBan catch-all verification (FREE single, $0.003/bulk)
|
|
591
541
|
*
|
|
592
542
|
* PHASE 3 - Paid finders (only if Phase 2 inconclusive):
|
|
593
|
-
* - hunter: Hunter.io email finder ($0.
|
|
543
|
+
* - hunter: Hunter.io email finder ($0.015/email)
|
|
594
544
|
* - snovio: Snov.io email finder ($0.02/email)
|
|
595
545
|
*
|
|
596
|
-
* Note: bouncer, dropcontact available but not in default order
|
|
597
546
|
*/
|
|
598
547
|
export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
|
|
599
548
|
/**
|
|
@@ -606,10 +555,8 @@ export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
|
|
|
606
555
|
* - trykitt: FREE (unlimited for individuals, $0.005/email high volume)
|
|
607
556
|
* - construct: FREE (pattern guessing + MX check)
|
|
608
557
|
* - bounceban: FREE single / $0.003/email bulk (catch-all specialist)
|
|
609
|
-
* -
|
|
558
|
+
* - hunter: $0.015/email (Growth tier)
|
|
610
559
|
* - snovio: $0.02/email (email finding + verification)
|
|
611
|
-
* - hunter: $0.005/email
|
|
612
|
-
* - dropcontact: $0.01/email (not in default order)
|
|
613
560
|
*/
|
|
614
561
|
export declare const PROVIDER_COSTS: Record<ProviderName, number>;
|
|
615
562
|
/**
|