linkedin-secret-sauce 0.5.1 → 0.6.0

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.
@@ -8,8 +8,11 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.enrichBusinessEmail = enrichBusinessEmail;
10
10
  exports.enrichBatch = enrichBatch;
11
+ exports.enrichAllEmails = enrichAllEmails;
12
+ exports.enrichAllBatch = enrichAllBatch;
11
13
  const personal_domains_1 = require("./utils/personal-domains");
12
14
  const disposable_domains_1 = require("./utils/disposable-domains");
15
+ const validation_1 = require("./utils/validation");
13
16
  /**
14
17
  * Default provider costs in USD per lookup
15
18
  */
@@ -62,6 +65,12 @@ function getProviderName(provider, index) {
62
65
  function getProviderCost(providerName) {
63
66
  return DEFAULT_PROVIDER_COSTS[providerName] ?? 0;
64
67
  }
68
+ /**
69
+ * Check if a provider result is a multi-result (returns multiple emails)
70
+ */
71
+ function isMultiResult(result) {
72
+ return result !== null && 'emails' in result && Array.isArray(result.emails);
73
+ }
65
74
  /**
66
75
  * Enrich a single candidate with business email
67
76
  */
@@ -93,7 +102,7 @@ async function enrichBusinessEmail(candidate, options) {
93
102
  raw = await provider(candidate);
94
103
  logger?.debug?.('enrichment.provider.done', {
95
104
  provider: providerName,
96
- hasResult: !!raw?.email,
105
+ hasResult: !!(raw && (isMultiResult(raw) ? raw.emails.length > 0 : raw.email)),
97
106
  });
98
107
  }
99
108
  catch (error) {
@@ -108,7 +117,7 @@ async function enrichBusinessEmail(candidate, options) {
108
117
  raw = await provider(candidate);
109
118
  logger?.debug?.('enrichment.provider.retry.done', {
110
119
  provider: providerName,
111
- hasResult: !!raw?.email,
120
+ hasResult: !!(raw && (isMultiResult(raw) ? raw.emails.length > 0 : raw.email)),
112
121
  });
113
122
  }
114
123
  catch {
@@ -116,6 +125,20 @@ async function enrichBusinessEmail(candidate, options) {
116
125
  }
117
126
  }
118
127
  }
128
+ // Convert multi-result to single result (take best match for backwards compatibility)
129
+ let singleResult = null;
130
+ if (isMultiResult(raw) && raw.emails.length > 0) {
131
+ // Take the first (highest confidence) email
132
+ const best = raw.emails[0];
133
+ singleResult = {
134
+ email: best.email,
135
+ verified: best.verified,
136
+ confidence: best.confidence,
137
+ };
138
+ }
139
+ else if (raw && !isMultiResult(raw)) {
140
+ singleResult = raw;
141
+ }
119
142
  // Deduct cost (even on failure - API was called)
120
143
  if (stepCost > 0) {
121
144
  remaining = Math.max(0, remaining - stepCost);
@@ -134,7 +157,7 @@ async function enrichBusinessEmail(candidate, options) {
134
157
  });
135
158
  }
136
159
  // Normalize result
137
- const normalized = normalizeProviderResult(providerName, raw);
160
+ const normalized = normalizeProviderResult(providerName, singleResult);
138
161
  // Skip if no email found
139
162
  if (!normalized.business_email) {
140
163
  continue;
@@ -216,3 +239,197 @@ async function enrichBatch(candidates, options) {
216
239
  }
217
240
  return results;
218
241
  }
242
+ // =============================================================================
243
+ // Multi-Email Enrichment (Collect All)
244
+ // =============================================================================
245
+ /**
246
+ * Classify an email's type based on domain characteristics
247
+ */
248
+ function classifyEmailType(email) {
249
+ if ((0, disposable_domains_1.isDisposableEmail)(email)) {
250
+ return 'disposable';
251
+ }
252
+ if ((0, personal_domains_1.isPersonalEmail)(email)) {
253
+ return 'personal';
254
+ }
255
+ if ((0, validation_1.isRoleAccount)(email)) {
256
+ return 'role';
257
+ }
258
+ // If it's not personal, disposable, or role, it's likely business
259
+ return 'business';
260
+ }
261
+ /**
262
+ * Enrich a candidate and collect ALL emails from ALL providers
263
+ *
264
+ * Unlike enrichBusinessEmail which stops at the first valid match,
265
+ * this function queries all providers (respecting budget) and returns
266
+ * all found emails with their scores and classifications.
267
+ */
268
+ async function enrichAllEmails(candidate, options) {
269
+ const { providers, maxCostPerEmail = Infinity, retryMs = 200, onCost, logger, } = options;
270
+ // Track results
271
+ const allEmails = [];
272
+ const providersQueried = [];
273
+ let totalCost = 0;
274
+ // Track remaining budget
275
+ let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0 ? maxCostPerEmail : Infinity;
276
+ // Track seen emails to avoid duplicates
277
+ const seenEmails = new Set();
278
+ logger?.debug?.('enrichment.all.start', {
279
+ providers: providers.length,
280
+ maxCostPerEmail,
281
+ });
282
+ for (let i = 0; i < providers.length; i++) {
283
+ const provider = providers[i];
284
+ const providerName = getProviderName(provider, i);
285
+ const stepCost = getProviderCost(providerName);
286
+ // Skip if cost exceeds remaining budget
287
+ if (stepCost > 0 && remaining < stepCost) {
288
+ logger?.debug?.('enrichment.all.skip_budget', {
289
+ provider: providerName,
290
+ cost: stepCost,
291
+ remaining,
292
+ });
293
+ continue;
294
+ }
295
+ providersQueried.push(providerName);
296
+ let raw = null;
297
+ try {
298
+ logger?.debug?.('enrichment.all.provider.start', { provider: providerName });
299
+ raw = await provider(candidate);
300
+ logger?.debug?.('enrichment.all.provider.done', {
301
+ provider: providerName,
302
+ hasResult: !!raw,
303
+ isMulti: isMultiResult(raw),
304
+ });
305
+ }
306
+ catch (error) {
307
+ logger?.warn?.('enrichment.all.provider.error', {
308
+ provider: providerName,
309
+ error: error instanceof Error ? error.message : String(error),
310
+ });
311
+ // Retry once after delay on transient errors
312
+ if (retryMs > 0) {
313
+ await new Promise((r) => setTimeout(r, retryMs));
314
+ try {
315
+ raw = await provider(candidate);
316
+ logger?.debug?.('enrichment.all.provider.retry.done', {
317
+ provider: providerName,
318
+ hasResult: !!raw,
319
+ });
320
+ }
321
+ catch {
322
+ raw = null;
323
+ }
324
+ }
325
+ }
326
+ // Deduct cost (even on failure - API was called)
327
+ if (stepCost > 0) {
328
+ remaining = Math.max(0, remaining - stepCost);
329
+ totalCost += stepCost;
330
+ if (onCost) {
331
+ try {
332
+ await onCost(providerName, stepCost);
333
+ }
334
+ catch {
335
+ // Ignore cost callback errors
336
+ }
337
+ }
338
+ logger?.debug?.('enrichment.all.cost.debit', {
339
+ provider: providerName,
340
+ cost: stepCost,
341
+ remaining,
342
+ totalCost,
343
+ });
344
+ }
345
+ // Process results
346
+ const now = new Date().toISOString();
347
+ if (isMultiResult(raw)) {
348
+ // Provider returned multiple emails
349
+ for (const emailResult of raw.emails) {
350
+ const emailLower = emailResult.email.toLowerCase();
351
+ if (seenEmails.has(emailLower)) {
352
+ continue;
353
+ }
354
+ seenEmails.add(emailLower);
355
+ allEmails.push({
356
+ email: emailResult.email,
357
+ source: providerName,
358
+ confidence: emailResult.confidence ?? 50,
359
+ verified: emailResult.verified ?? false,
360
+ type: classifyEmailType(emailResult.email),
361
+ isCatchAll: emailResult.isCatchAll,
362
+ foundAt: now,
363
+ metadata: emailResult.metadata,
364
+ });
365
+ }
366
+ }
367
+ else if (raw?.email) {
368
+ // Provider returned single email
369
+ const emailLower = raw.email.toLowerCase();
370
+ if (!seenEmails.has(emailLower)) {
371
+ seenEmails.add(emailLower);
372
+ const confidence = typeof raw.score === 'number'
373
+ ? raw.score
374
+ : typeof raw.confidence === 'number'
375
+ ? raw.confidence
376
+ : 50;
377
+ allEmails.push({
378
+ email: raw.email,
379
+ source: providerName,
380
+ confidence,
381
+ verified: raw.verified ?? false,
382
+ type: classifyEmailType(raw.email),
383
+ foundAt: now,
384
+ });
385
+ }
386
+ }
387
+ }
388
+ // Sort by confidence (highest first), then by type (business first)
389
+ const typeOrder = {
390
+ business: 0,
391
+ unknown: 1,
392
+ role: 2,
393
+ personal: 3,
394
+ disposable: 4,
395
+ };
396
+ allEmails.sort((a, b) => {
397
+ // First by confidence (descending)
398
+ if (b.confidence !== a.confidence) {
399
+ return b.confidence - a.confidence;
400
+ }
401
+ // Then by type priority
402
+ return typeOrder[a.type] - typeOrder[b.type];
403
+ });
404
+ logger?.info?.('enrichment.all.complete', {
405
+ emailsFound: allEmails.length,
406
+ providersQueried: providersQueried.length,
407
+ totalCost,
408
+ });
409
+ return {
410
+ emails: allEmails,
411
+ candidate,
412
+ totalCost,
413
+ providersQueried,
414
+ completedAt: new Date().toISOString(),
415
+ };
416
+ }
417
+ /**
418
+ * Enrich multiple candidates and collect all emails (batch version)
419
+ */
420
+ async function enrichAllBatch(candidates, options) {
421
+ const results = [];
422
+ const batchSize = options.batchSize ?? 50;
423
+ const delayMs = options.delayMs ?? 200;
424
+ for (let i = 0; i < candidates.length; i += batchSize) {
425
+ const chunk = candidates.slice(i, i + batchSize);
426
+ // Process batch in parallel
427
+ const batchResults = await Promise.all(chunk.map((candidate) => enrichAllEmails(candidate, options)));
428
+ results.push(...batchResults);
429
+ // Delay between batches to avoid rate limiting
430
+ if (i + batchSize < candidates.length && delayMs > 0) {
431
+ await new Promise((r) => setTimeout(r, delayMs));
432
+ }
433
+ }
434
+ return results;
435
+ }
@@ -4,8 +4,8 @@
4
4
  * Apollo.io public API for email finding.
5
5
  * Uses mixed_people/search endpoint.
6
6
  */
7
- import type { EnrichmentCandidate, ProviderResult, ApolloConfig } from "../types";
7
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, ApolloConfig } from "../types";
8
8
  /**
9
9
  * Create the Apollo provider function
10
10
  */
11
- export declare function createApolloProvider(config: ApolloConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
11
+ export declare function createApolloProvider(config: ApolloConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
@@ -88,8 +88,8 @@ function createApolloProvider(config) {
88
88
  }
89
89
  async function fetchEmail(candidate) {
90
90
  const { name, org, domain } = extractInputs(candidate);
91
- // Build request body
92
- const body = { page: 1, per_page: 1 };
91
+ // Build request body - get more results for multi-email support
92
+ const body = { page: 1, per_page: 10 };
93
93
  if (truthy(name))
94
94
  body["q_person_name"] = String(name);
95
95
  if (truthy(domain))
@@ -112,19 +112,64 @@ function createApolloProvider(config) {
112
112
  (json.people || json.matches || json.data?.people));
113
113
  if (!Array.isArray(people) || people.length === 0)
114
114
  return null;
115
- const p = people[0] || {};
116
- const email = p.email ??
117
- p.primary_email ??
118
- p.emails?.[0]?.email ??
119
- null;
120
- if (!email)
115
+ // Collect all emails from all people
116
+ const emails = [];
117
+ const seenEmails = new Set();
118
+ for (const person of people) {
119
+ const p = person || {};
120
+ // Get primary email
121
+ const primaryEmail = p.email ??
122
+ p.primary_email ??
123
+ null;
124
+ if (primaryEmail) {
125
+ const emailLower = primaryEmail.toLowerCase();
126
+ if (!seenEmails.has(emailLower)) {
127
+ seenEmails.add(emailLower);
128
+ const score = typeof p.email_confidence === "number"
129
+ ? p.email_confidence
130
+ : Number(p.email_confidence ?? 0) || 50;
131
+ const verified = mapVerified(p.email_status);
132
+ emails.push({
133
+ email: primaryEmail,
134
+ verified,
135
+ confidence: score,
136
+ metadata: {
137
+ name: p.name,
138
+ title: p.title,
139
+ company: p.organization?.name,
140
+ linkedin: p.linkedin_url,
141
+ },
142
+ });
143
+ }
144
+ }
145
+ // Also check emails array if present
146
+ const personEmails = p.emails;
147
+ if (Array.isArray(personEmails)) {
148
+ for (const e of personEmails) {
149
+ if (!e.email)
150
+ continue;
151
+ const emailLower = e.email.toLowerCase();
152
+ if (seenEmails.has(emailLower))
153
+ continue;
154
+ seenEmails.add(emailLower);
155
+ emails.push({
156
+ email: e.email,
157
+ verified: mapVerified(e.status),
158
+ confidence: 50, // Secondary emails get lower confidence
159
+ metadata: {
160
+ name: p.name,
161
+ title: p.title,
162
+ company: p.organization?.name,
163
+ },
164
+ });
165
+ }
166
+ }
167
+ }
168
+ if (emails.length === 0)
121
169
  return null;
122
- const score = typeof p.email_confidence === "number"
123
- ? p.email_confidence
124
- : Number(p.email_confidence ?? 0) || undefined;
125
- const verified = mapVerified(p.email_status ??
126
- p.emails?.[0]?.status);
127
- return { email, verified, score };
170
+ // Sort by confidence
171
+ emails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
172
+ return { emails };
128
173
  }
129
174
  catch {
130
175
  return null;
@@ -4,8 +4,8 @@
4
4
  * Generates email pattern candidates based on name + domain, then verifies via MX check.
5
5
  * This is a FREE provider that doesn't require any API keys.
6
6
  */
7
- import type { EnrichmentCandidate, ProviderResult, ConstructConfig } from '../types';
7
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, ConstructConfig } from '../types';
8
8
  /**
9
9
  * Create the construct provider function
10
10
  */
11
- export declare function createConstructProvider(config?: ConstructConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
11
+ export declare function createConstructProvider(config?: ConstructConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
@@ -88,18 +88,30 @@ function createConstructProvider(config) {
88
88
  }
89
89
  const candidates = buildCandidates({ first, last, domain });
90
90
  const max = Math.min(candidates.length, maxAttempts);
91
+ // Collect ALL valid email patterns (not just first match)
92
+ const validEmails = [];
91
93
  for (let i = 0; i < max; i++) {
92
94
  const email = candidates[i];
93
95
  const verification = await (0, mx_1.verifyEmailMx)(email, { timeoutMs });
94
96
  if (verification.valid === true && verification.confidence >= 50) {
95
- return {
97
+ validEmails.push({
96
98
  email,
97
99
  verified: true,
98
- score: verification.confidence,
99
- };
100
+ confidence: verification.confidence,
101
+ isCatchAll: verification.isCatchAll,
102
+ metadata: {
103
+ pattern: email.split('@')[0], // The local part pattern used
104
+ mxRecords: verification.mxRecords,
105
+ },
106
+ });
100
107
  }
101
108
  }
102
- return null;
109
+ if (validEmails.length === 0) {
110
+ return null;
111
+ }
112
+ // Sort by confidence
113
+ validEmails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
114
+ return { emails: validEmails };
103
115
  }
104
116
  // Mark provider name for orchestrator
105
117
  fetchEmail.__name = 'construct';
@@ -4,8 +4,8 @@
4
4
  * Hunter.io public API for email finding.
5
5
  * Uses email-finder endpoint with name+domain, falls back to domain-search.
6
6
  */
7
- import type { EnrichmentCandidate, ProviderResult, HunterConfig } from "../types";
7
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, HunterConfig } from "../types";
8
8
  /**
9
9
  * Create the Hunter provider function
10
10
  */
11
- export declare function createHunterProvider(config: HunterConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
11
+ export declare function createHunterProvider(config: HunterConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
@@ -97,6 +97,7 @@ function createHunterProvider(config) {
97
97
  async function fetchEmail(candidate) {
98
98
  const { first, last, domain } = extractInputs(candidate);
99
99
  let url = null;
100
+ let isEmailFinder = false;
100
101
  // Use email-finder if we have name components
101
102
  if (truthy(first) && truthy(last) && truthy(domain)) {
102
103
  const qs = new URLSearchParams({
@@ -106,13 +107,14 @@ function createHunterProvider(config) {
106
107
  last_name: String(last),
107
108
  });
108
109
  url = `${API_BASE}/email-finder?${qs.toString()}`;
110
+ isEmailFinder = true;
109
111
  }
110
- // Fall back to domain-search if only domain available
112
+ // Fall back to domain-search if only domain available (can return multiple)
111
113
  else if (truthy(domain)) {
112
114
  const qs = new URLSearchParams({
113
115
  api_key: String(apiKey),
114
116
  domain: String(domain),
115
- limit: "1",
117
+ limit: "10", // Get more results
116
118
  });
117
119
  url = `${API_BASE}/domain-search?${qs.toString()}`;
118
120
  }
@@ -121,34 +123,58 @@ function createHunterProvider(config) {
121
123
  }
122
124
  try {
123
125
  const json = await requestWithRetry(url, 1, 100);
124
- // Parse email-finder response shape
125
- const ef = (json && (json.data || json.result));
126
- if (ef && (ef.email || ef.score !== undefined)) {
127
- const email = ef.email ?? null;
128
- const score = typeof ef.score === "number"
129
- ? ef.score
130
- : Number(ef.score ?? 0) || undefined;
131
- const verified = mapVerified(ef?.verification?.status ?? ef?.status);
132
- if (!email)
133
- return null;
134
- return { email, verified, score };
126
+ // Parse email-finder response shape (single result)
127
+ if (isEmailFinder) {
128
+ const ef = (json && (json.data || json.result));
129
+ if (ef && (ef.email || ef.score !== undefined)) {
130
+ const email = ef.email ?? null;
131
+ const score = typeof ef.score === "number"
132
+ ? ef.score
133
+ : Number(ef.score ?? 0) || undefined;
134
+ const verified = mapVerified(ef?.verification?.status ?? ef?.status);
135
+ if (!email)
136
+ return null;
137
+ return { email, verified, score };
138
+ }
135
139
  }
136
- // Parse domain-search response shape
140
+ // Parse domain-search response shape (can have multiple emails)
137
141
  const ds = (json &&
138
142
  json.data &&
139
143
  Array.isArray(json.data.emails)
140
144
  ? json.data.emails
141
145
  : null);
142
146
  if (ds && ds.length > 0) {
143
- const firstHit = ds[0];
144
- const email = firstHit?.value || firstHit?.email || null;
145
- const score = typeof firstHit?.confidence === "number"
146
- ? firstHit.confidence
147
- : Number(firstHit?.confidence ?? 0) || undefined;
148
- const verified = mapVerified(firstHit?.verification?.status ?? firstHit?.status);
149
- if (!email)
147
+ // Return all emails as multi-result
148
+ const emails = [];
149
+ const seenEmails = new Set();
150
+ for (const hit of ds) {
151
+ const email = hit?.value || hit?.email;
152
+ if (!email)
153
+ continue;
154
+ const emailLower = email.toLowerCase();
155
+ if (seenEmails.has(emailLower))
156
+ continue;
157
+ seenEmails.add(emailLower);
158
+ const score = typeof hit?.confidence === "number"
159
+ ? hit.confidence
160
+ : Number(hit?.confidence ?? 0) || 50;
161
+ const verified = mapVerified(hit?.verification?.status ?? hit?.status);
162
+ emails.push({
163
+ email,
164
+ verified,
165
+ confidence: score,
166
+ metadata: {
167
+ firstName: hit.first_name,
168
+ lastName: hit.last_name,
169
+ position: hit.position,
170
+ },
171
+ });
172
+ }
173
+ if (emails.length === 0)
150
174
  return null;
151
- return { email, verified, score };
175
+ // Sort by confidence
176
+ emails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
177
+ return { emails };
152
178
  }
153
179
  return null;
154
180
  }
@@ -4,8 +4,8 @@
4
4
  * YOUR private database of ~500M scraped LinkedIn profiles with emails.
5
5
  * This is FREE and unlimited - it's your own service.
6
6
  */
7
- import type { EnrichmentCandidate, ProviderResult, LddConfig } from "../types";
7
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, LddConfig } from "../types";
8
8
  /**
9
9
  * Create the LDD provider function
10
10
  */
11
- export declare function createLddProvider(config: LddConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
11
+ export declare function createLddProvider(config: LddConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
@@ -88,17 +88,25 @@ function createLddProvider(config) {
88
88
  if (!data.success || !data.data) {
89
89
  return null;
90
90
  }
91
- // Find first valid email
92
- const emails = data.data.emails || [];
93
- const validEmail = emails.find((e) => e.email_address && e.email_address.includes("@"));
94
- if (!validEmail) {
91
+ // Return ALL valid emails from the profile
92
+ const profileEmails = data.data.emails || [];
93
+ const validEmails = profileEmails.filter((e) => e.email_address && e.email_address.includes("@"));
94
+ if (validEmails.length === 0) {
95
95
  return null;
96
96
  }
97
- return {
98
- email: validEmail.email_address,
97
+ // Build multi-result with all emails
98
+ const emails = validEmails.map((e) => ({
99
+ email: e.email_address,
99
100
  verified: true, // LDD data is pre-verified
100
- score: 90, // High confidence from your own database
101
- };
101
+ confidence: 90, // High confidence from your own database
102
+ metadata: {
103
+ emailType: e.email_type,
104
+ fullName: data.data.full_name,
105
+ linkedinUsername: data.data.linkedin_username,
106
+ linkedinUrl: data.data.linkedin_profile_url,
107
+ },
108
+ }));
109
+ return { emails };
102
110
  }
103
111
  catch {
104
112
  return null;
@@ -7,8 +7,41 @@
7
7
  * Supports two authentication methods:
8
8
  * 1. Direct token: Pass `apiToken` directly (for pre-authenticated scenarios)
9
9
  * 2. Credentials: Pass `email` and `password` for automatic login with token caching
10
+ *
11
+ * Exports:
12
+ * - createSmartProspectProvider: For email enrichment (waterfall pattern)
13
+ * - createSmartProspectClient: Full client with search/fetch capabilities
14
+ */
15
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, SmartProspectConfig, SmartProspectSearchFilters, SmartProspectSearchResponse, SmartProspectFetchResponse, SmartProspectContact, SmartProspectLocationResponse } from "../types";
16
+ /**
17
+ * SmartProspect Location lookup options
10
18
  */
11
- import type { EnrichmentCandidate, ProviderResult, SmartProspectConfig } from "../types";
19
+ export interface SmartProspectLocationOptions {
20
+ search?: string;
21
+ limit?: number;
22
+ offset?: number;
23
+ }
24
+ /**
25
+ * SmartProspect Client interface for direct API access
26
+ */
27
+ export interface SmartProspectClient {
28
+ /** Search for contacts (FREE - no credits used) */
29
+ search: (filters: SmartProspectSearchFilters) => Promise<SmartProspectSearchResponse>;
30
+ /** Fetch/enrich emails for specific contact IDs (COSTS CREDITS) */
31
+ fetch: (contactIds: string[]) => Promise<SmartProspectFetchResponse>;
32
+ /** Search and immediately fetch all results (COSTS CREDITS for fetched contacts) */
33
+ searchAndFetch: (filters: SmartProspectSearchFilters) => Promise<{
34
+ searchResponse: SmartProspectSearchResponse;
35
+ fetchResponse: SmartProspectFetchResponse | null;
36
+ contacts: SmartProspectContact[];
37
+ }>;
38
+ /** Lookup countries (for typeahead) */
39
+ getCountries: (options?: SmartProspectLocationOptions) => Promise<SmartProspectLocationResponse>;
40
+ /** Lookup states (for typeahead) */
41
+ getStates: (options?: SmartProspectLocationOptions) => Promise<SmartProspectLocationResponse>;
42
+ /** Lookup cities (for typeahead) */
43
+ getCities: (options?: SmartProspectLocationOptions) => Promise<SmartProspectLocationResponse>;
44
+ }
12
45
  /**
13
46
  * Create the SmartProspect provider function
14
47
  *
@@ -16,4 +49,33 @@ import type { EnrichmentCandidate, ProviderResult, SmartProspectConfig } from ".
16
49
  * 1. Direct token: Pass `apiToken` in config
17
50
  * 2. Credentials: Pass `email` and `password` in config for auto-login
18
51
  */
19
- export declare function createSmartProspectProvider(config: SmartProspectConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
52
+ export declare function createSmartProspectProvider(config: SmartProspectConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
53
+ /**
54
+ * Create a SmartProspect client for direct API access
55
+ *
56
+ * This provides access to the full SmartProspect API for:
57
+ * - Searching contacts with comprehensive filters (FREE)
58
+ * - Fetching/enriching contact emails (COSTS CREDITS)
59
+ * - Combined search + fetch operations
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const client = createSmartProspectClient({
64
+ * email: 'user@example.com',
65
+ * password: 'password123'
66
+ * });
67
+ *
68
+ * // Search only (FREE)
69
+ * const results = await client.search({
70
+ * title: ['CEO', 'CTO'],
71
+ * company: ['Google', 'Microsoft'],
72
+ * level: ['C-Level'],
73
+ * companyHeadCount: ['1001-5000', '5001-10000'],
74
+ * limit: 25
75
+ * });
76
+ *
77
+ * // Fetch specific contacts (COSTS CREDITS)
78
+ * const enriched = await client.fetch(['contact-id-1', 'contact-id-2']);
79
+ * ```
80
+ */
81
+ export declare function createSmartProspectClient(config: SmartProspectConfig): SmartProspectClient | null;