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.
- package/dist/enrichment/auth/index.d.ts +1 -1
- package/dist/enrichment/auth/index.js +6 -1
- package/dist/enrichment/auth/smartlead-auth.d.ts +32 -0
- package/dist/enrichment/auth/smartlead-auth.js +165 -0
- package/dist/enrichment/index.d.ts +4 -2
- package/dist/enrichment/index.js +43 -1
- package/dist/enrichment/matching.d.ts +239 -0
- package/dist/enrichment/matching.js +619 -0
- package/dist/enrichment/orchestrator.d.ts +13 -1
- package/dist/enrichment/orchestrator.js +220 -3
- package/dist/enrichment/providers/apollo.d.ts +2 -2
- package/dist/enrichment/providers/apollo.js +59 -14
- package/dist/enrichment/providers/construct.d.ts +2 -2
- package/dist/enrichment/providers/construct.js +16 -4
- package/dist/enrichment/providers/hunter.d.ts +2 -2
- package/dist/enrichment/providers/hunter.js +48 -22
- package/dist/enrichment/providers/ldd.d.ts +2 -2
- package/dist/enrichment/providers/ldd.js +16 -8
- package/dist/enrichment/providers/smartprospect.d.ts +64 -2
- package/dist/enrichment/providers/smartprospect.js +391 -35
- package/dist/enrichment/types.d.ts +139 -11
- package/dist/enrichment/types.js +50 -1
- package/package.json +16 -15
|
@@ -8,9 +8,14 @@
|
|
|
8
8
|
* Supports two authentication methods:
|
|
9
9
|
* 1. Direct token: Pass `apiToken` directly (for pre-authenticated scenarios)
|
|
10
10
|
* 2. Credentials: Pass `email` and `password` for automatic login with token caching
|
|
11
|
+
*
|
|
12
|
+
* Exports:
|
|
13
|
+
* - createSmartProspectProvider: For email enrichment (waterfall pattern)
|
|
14
|
+
* - createSmartProspectClient: Full client with search/fetch capabilities
|
|
11
15
|
*/
|
|
12
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
17
|
exports.createSmartProspectProvider = createSmartProspectProvider;
|
|
18
|
+
exports.createSmartProspectClient = createSmartProspectClient;
|
|
14
19
|
const smartlead_auth_1 = require("../auth/smartlead-auth");
|
|
15
20
|
const DEFAULT_API_URL = "https://prospect-api.smartlead.ai/api/search-email-leads";
|
|
16
21
|
/**
|
|
@@ -252,6 +257,22 @@ function createSmartProspectProvider(config) {
|
|
|
252
257
|
};
|
|
253
258
|
}
|
|
254
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Calculate confidence score for a contact
|
|
262
|
+
*/
|
|
263
|
+
function calculateConfidence(contact) {
|
|
264
|
+
const isVerified = contact.verificationStatus === "valid";
|
|
265
|
+
const isCatchAll = contact.verificationStatus === "catch_all";
|
|
266
|
+
const deliverability = contact.emailDeliverability || 0;
|
|
267
|
+
let confidence = deliverability * 100;
|
|
268
|
+
if (isVerified) {
|
|
269
|
+
confidence = Math.max(confidence, 90);
|
|
270
|
+
}
|
|
271
|
+
else if (isCatchAll) {
|
|
272
|
+
confidence = Math.min(confidence, 75);
|
|
273
|
+
}
|
|
274
|
+
return confidence;
|
|
275
|
+
}
|
|
255
276
|
async function fetchEmail(candidate) {
|
|
256
277
|
const { firstName, lastName } = extractNames(candidate);
|
|
257
278
|
if (!firstName) {
|
|
@@ -270,64 +291,399 @@ function createSmartProspectProvider(config) {
|
|
|
270
291
|
candidate.companyDomain ||
|
|
271
292
|
candidate.company_domain ||
|
|
272
293
|
undefined;
|
|
273
|
-
// Build search filters
|
|
294
|
+
// Build search filters using correct API field names
|
|
274
295
|
const filters = {
|
|
275
296
|
name: [fullName],
|
|
276
|
-
limit:
|
|
297
|
+
limit: 10, // Get more results to return multiple emails
|
|
277
298
|
};
|
|
278
299
|
if (company) {
|
|
279
|
-
filters.
|
|
300
|
+
filters.companyName = [company];
|
|
280
301
|
}
|
|
281
302
|
if (domain) {
|
|
282
|
-
filters.
|
|
303
|
+
filters.companyDomain = [domain];
|
|
283
304
|
}
|
|
284
305
|
// Step 1: Search for contacts (FREE)
|
|
285
306
|
const searchResult = await searchContacts(filters);
|
|
286
307
|
if (!searchResult.success || searchResult.data.list.length === 0) {
|
|
287
308
|
return null;
|
|
288
309
|
}
|
|
289
|
-
//
|
|
310
|
+
// Score and sort all matches
|
|
290
311
|
const matches = searchResult.data.list;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
if (!bestMatch) {
|
|
312
|
+
const scoredMatches = matches
|
|
313
|
+
.map((contact) => ({
|
|
314
|
+
contact,
|
|
315
|
+
score: calculateMatchScore(contact, fullName, company, title),
|
|
316
|
+
}))
|
|
317
|
+
.filter((m) => m.score > 0) // Only include matches with some relevance
|
|
318
|
+
.sort((a, b) => b.score - a.score);
|
|
319
|
+
if (scoredMatches.length === 0) {
|
|
301
320
|
return null;
|
|
302
321
|
}
|
|
303
|
-
// Step 2: Fetch
|
|
304
|
-
|
|
322
|
+
// Step 2: Fetch emails for ALL matching contacts (COSTS CREDITS)
|
|
323
|
+
// Note: This costs more credits but gives us all available emails
|
|
324
|
+
const contactIds = scoredMatches.map((m) => m.contact.id);
|
|
325
|
+
const fetchResult = await fetchContacts(contactIds);
|
|
305
326
|
if (!fetchResult.success || fetchResult.data.list.length === 0) {
|
|
306
327
|
return null;
|
|
307
328
|
}
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
|
|
329
|
+
// Build multi-result with all found emails
|
|
330
|
+
const emails = [];
|
|
331
|
+
const seenEmails = new Set();
|
|
332
|
+
for (const enrichedContact of fetchResult.data.list) {
|
|
333
|
+
const email = enrichedContact?.email;
|
|
334
|
+
if (!email)
|
|
335
|
+
continue;
|
|
336
|
+
const emailLower = email.toLowerCase();
|
|
337
|
+
if (seenEmails.has(emailLower))
|
|
338
|
+
continue;
|
|
339
|
+
seenEmails.add(emailLower);
|
|
340
|
+
const isVerified = enrichedContact.verificationStatus === "valid";
|
|
341
|
+
const isCatchAll = enrichedContact.verificationStatus === "catch_all";
|
|
342
|
+
emails.push({
|
|
343
|
+
email,
|
|
344
|
+
verified: isVerified || isCatchAll,
|
|
345
|
+
confidence: calculateConfidence(enrichedContact),
|
|
346
|
+
isCatchAll,
|
|
347
|
+
metadata: {
|
|
348
|
+
fullName: enrichedContact.fullName,
|
|
349
|
+
title: enrichedContact.title,
|
|
350
|
+
company: enrichedContact.company?.name,
|
|
351
|
+
linkedin: enrichedContact.linkedin,
|
|
352
|
+
verificationStatus: enrichedContact.verificationStatus,
|
|
353
|
+
matchScore: scoredMatches.find((m) => m.contact.id === enrichedContact.id)?.score,
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (emails.length === 0) {
|
|
311
358
|
return null;
|
|
312
359
|
}
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
360
|
+
// Sort by confidence (highest first)
|
|
361
|
+
emails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
|
|
362
|
+
// Return multi-result format
|
|
363
|
+
return { emails };
|
|
364
|
+
}
|
|
365
|
+
// Mark provider name for orchestrator
|
|
366
|
+
fetchEmail.__name = "smartprospect";
|
|
367
|
+
return fetchEmail;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Create a SmartProspect client for direct API access
|
|
371
|
+
*
|
|
372
|
+
* This provides access to the full SmartProspect API for:
|
|
373
|
+
* - Searching contacts with comprehensive filters (FREE)
|
|
374
|
+
* - Fetching/enriching contact emails (COSTS CREDITS)
|
|
375
|
+
* - Combined search + fetch operations
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```ts
|
|
379
|
+
* const client = createSmartProspectClient({
|
|
380
|
+
* email: 'user@example.com',
|
|
381
|
+
* password: 'password123'
|
|
382
|
+
* });
|
|
383
|
+
*
|
|
384
|
+
* // Search only (FREE)
|
|
385
|
+
* const results = await client.search({
|
|
386
|
+
* title: ['CEO', 'CTO'],
|
|
387
|
+
* company: ['Google', 'Microsoft'],
|
|
388
|
+
* level: ['C-Level'],
|
|
389
|
+
* companyHeadCount: ['1001-5000', '5001-10000'],
|
|
390
|
+
* limit: 25
|
|
391
|
+
* });
|
|
392
|
+
*
|
|
393
|
+
* // Fetch specific contacts (COSTS CREDITS)
|
|
394
|
+
* const enriched = await client.fetch(['contact-id-1', 'contact-id-2']);
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
function createSmartProspectClient(config) {
|
|
398
|
+
const { apiToken, email, password, apiUrl = DEFAULT_API_URL, loginUrl, eagerInit = true, } = config;
|
|
399
|
+
// Check if we have valid auth config
|
|
400
|
+
const hasDirectToken = !!apiToken;
|
|
401
|
+
const hasCredentials = !!email && !!password;
|
|
402
|
+
if (!hasDirectToken && !hasCredentials) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Get the current auth token
|
|
407
|
+
*/
|
|
408
|
+
async function getAuthToken() {
|
|
409
|
+
if (hasDirectToken) {
|
|
410
|
+
return apiToken;
|
|
320
411
|
}
|
|
321
|
-
|
|
322
|
-
|
|
412
|
+
return (0, smartlead_auth_1.getSmartLeadToken)({
|
|
413
|
+
credentials: { email: email, password: password },
|
|
414
|
+
loginUrl,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Handle 401 errors
|
|
419
|
+
*/
|
|
420
|
+
async function handleAuthError() {
|
|
421
|
+
if (hasCredentials) {
|
|
422
|
+
(0, smartlead_auth_1.clearSmartLeadToken)(email);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Eager initialization
|
|
426
|
+
if (eagerInit && hasCredentials) {
|
|
427
|
+
getAuthToken().catch(() => { });
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Build the request payload with proper defaults
|
|
431
|
+
* Field names match the actual SmartProspect API
|
|
432
|
+
*/
|
|
433
|
+
function buildSearchPayload(filters) {
|
|
434
|
+
const payload = {};
|
|
435
|
+
// Contact-based filters
|
|
436
|
+
if (filters.name?.length)
|
|
437
|
+
payload.name = filters.name;
|
|
438
|
+
if (filters.firstName?.length)
|
|
439
|
+
payload.firstName = filters.firstName;
|
|
440
|
+
if (filters.lastName?.length)
|
|
441
|
+
payload.lastName = filters.lastName;
|
|
442
|
+
// Title filters
|
|
443
|
+
if (filters.title?.length)
|
|
444
|
+
payload.title = filters.title;
|
|
445
|
+
payload.titleExactMatch = filters.titleExactMatch ?? false;
|
|
446
|
+
// Department & Level
|
|
447
|
+
if (filters.department?.length)
|
|
448
|
+
payload.department = filters.department;
|
|
449
|
+
if (filters.level?.length)
|
|
450
|
+
payload.level = filters.level;
|
|
451
|
+
// Company filters (note: API uses companyName and companyDomain)
|
|
452
|
+
if (filters.companyName?.length)
|
|
453
|
+
payload.companyName = filters.companyName;
|
|
454
|
+
if (filters.companyDomain?.length)
|
|
455
|
+
payload.companyDomain = filters.companyDomain;
|
|
456
|
+
// Industry filters (note: API uses companyIndustry and companySubIndustry)
|
|
457
|
+
if (filters.companyIndustry?.length)
|
|
458
|
+
payload.companyIndustry = filters.companyIndustry;
|
|
459
|
+
if (filters.companySubIndustry?.length)
|
|
460
|
+
payload.companySubIndustry = filters.companySubIndustry;
|
|
461
|
+
// Company size filters
|
|
462
|
+
if (filters.companyHeadCount?.length)
|
|
463
|
+
payload.companyHeadCount = filters.companyHeadCount;
|
|
464
|
+
if (filters.companyRevenue?.length)
|
|
465
|
+
payload.companyRevenue = filters.companyRevenue;
|
|
466
|
+
// Location filters
|
|
467
|
+
if (filters.country?.length)
|
|
468
|
+
payload.country = filters.country;
|
|
469
|
+
if (filters.state?.length)
|
|
470
|
+
payload.state = filters.state;
|
|
471
|
+
if (filters.city?.length)
|
|
472
|
+
payload.city = filters.city;
|
|
473
|
+
// Workflow options
|
|
474
|
+
payload.dontDisplayOwnedContact = filters.dontDisplayOwnedContact ?? true;
|
|
475
|
+
// Pagination
|
|
476
|
+
payload.limit = filters.limit ?? 25;
|
|
477
|
+
if (filters.scroll_id)
|
|
478
|
+
payload.scroll_id = filters.scroll_id;
|
|
479
|
+
return payload;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Search for contacts (FREE - no credits)
|
|
483
|
+
*/
|
|
484
|
+
async function search(filters) {
|
|
485
|
+
const payload = buildSearchPayload(filters);
|
|
486
|
+
const makeRequest = async (token) => {
|
|
487
|
+
return requestWithRetry(`${apiUrl}/search-contacts`, {
|
|
488
|
+
method: "POST",
|
|
489
|
+
headers: {
|
|
490
|
+
Authorization: `Bearer ${token}`,
|
|
491
|
+
"Content-Type": "application/json",
|
|
492
|
+
Accept: "application/json",
|
|
493
|
+
},
|
|
494
|
+
body: JSON.stringify(payload),
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
try {
|
|
498
|
+
const token = await getAuthToken();
|
|
499
|
+
return await makeRequest(token);
|
|
500
|
+
}
|
|
501
|
+
catch (err) {
|
|
502
|
+
if (err instanceof Error &&
|
|
503
|
+
err.message.includes("401") &&
|
|
504
|
+
hasCredentials) {
|
|
505
|
+
await handleAuthError();
|
|
506
|
+
try {
|
|
507
|
+
const freshToken = await getAuthToken();
|
|
508
|
+
return await makeRequest(freshToken);
|
|
509
|
+
}
|
|
510
|
+
catch {
|
|
511
|
+
// Retry failed
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
success: false,
|
|
516
|
+
message: err instanceof Error ? err.message : "Search failed",
|
|
517
|
+
data: { list: [], total_count: 0 },
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Fetch/enrich emails for specific contact IDs (COSTS CREDITS)
|
|
523
|
+
*/
|
|
524
|
+
async function fetch(contactIds) {
|
|
525
|
+
if (!contactIds.length) {
|
|
526
|
+
return {
|
|
527
|
+
success: true,
|
|
528
|
+
message: "No contacts to fetch",
|
|
529
|
+
data: {
|
|
530
|
+
list: [],
|
|
531
|
+
total_count: 0,
|
|
532
|
+
metrics: {
|
|
533
|
+
totalContacts: 0,
|
|
534
|
+
totalEmails: 0,
|
|
535
|
+
noEmailFound: 0,
|
|
536
|
+
invalidEmails: 0,
|
|
537
|
+
catchAllEmails: 0,
|
|
538
|
+
verifiedEmails: 0,
|
|
539
|
+
completed: 0,
|
|
540
|
+
},
|
|
541
|
+
leads_found: 0,
|
|
542
|
+
email_fetched: 0,
|
|
543
|
+
verification_status_list: [],
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
const makeRequest = async (token) => {
|
|
548
|
+
return requestWithRetry(`${apiUrl}/fetch-contacts`, {
|
|
549
|
+
method: "POST",
|
|
550
|
+
headers: {
|
|
551
|
+
Authorization: `Bearer ${token}`,
|
|
552
|
+
"Content-Type": "application/json",
|
|
553
|
+
Accept: "application/json",
|
|
554
|
+
},
|
|
555
|
+
body: JSON.stringify({ contactIds }),
|
|
556
|
+
});
|
|
557
|
+
};
|
|
558
|
+
try {
|
|
559
|
+
const token = await getAuthToken();
|
|
560
|
+
return await makeRequest(token);
|
|
561
|
+
}
|
|
562
|
+
catch (err) {
|
|
563
|
+
if (err instanceof Error &&
|
|
564
|
+
err.message.includes("401") &&
|
|
565
|
+
hasCredentials) {
|
|
566
|
+
await handleAuthError();
|
|
567
|
+
try {
|
|
568
|
+
const freshToken = await getAuthToken();
|
|
569
|
+
return await makeRequest(freshToken);
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
// Retry failed
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
success: false,
|
|
577
|
+
message: err instanceof Error ? err.message : "Fetch failed",
|
|
578
|
+
data: {
|
|
579
|
+
list: [],
|
|
580
|
+
total_count: 0,
|
|
581
|
+
metrics: {
|
|
582
|
+
totalContacts: 0,
|
|
583
|
+
totalEmails: 0,
|
|
584
|
+
noEmailFound: 0,
|
|
585
|
+
invalidEmails: 0,
|
|
586
|
+
catchAllEmails: 0,
|
|
587
|
+
verifiedEmails: 0,
|
|
588
|
+
completed: 0,
|
|
589
|
+
},
|
|
590
|
+
leads_found: 0,
|
|
591
|
+
email_fetched: 0,
|
|
592
|
+
verification_status_list: [],
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Search and immediately fetch all results
|
|
599
|
+
*/
|
|
600
|
+
async function searchAndFetch(filters) {
|
|
601
|
+
const searchResponse = await search(filters);
|
|
602
|
+
if (!searchResponse.success || searchResponse.data.list.length === 0) {
|
|
603
|
+
return {
|
|
604
|
+
searchResponse,
|
|
605
|
+
fetchResponse: null,
|
|
606
|
+
contacts: [],
|
|
607
|
+
};
|
|
323
608
|
}
|
|
609
|
+
const contactIds = searchResponse.data.list.map((c) => c.id);
|
|
610
|
+
const fetchResponse = await fetch(contactIds);
|
|
324
611
|
return {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
612
|
+
searchResponse,
|
|
613
|
+
fetchResponse,
|
|
614
|
+
contacts: fetchResponse.success ? fetchResponse.data.list : [],
|
|
328
615
|
};
|
|
329
616
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
617
|
+
/**
|
|
618
|
+
* Generic location lookup (countries, states, cities)
|
|
619
|
+
*/
|
|
620
|
+
async function getLocations(type, options = {}) {
|
|
621
|
+
const { search: searchQuery, limit = 20, offset = 0 } = options;
|
|
622
|
+
const params = new URLSearchParams();
|
|
623
|
+
params.set('limit', String(limit));
|
|
624
|
+
if (offset > 0)
|
|
625
|
+
params.set('offset', String(offset));
|
|
626
|
+
if (searchQuery)
|
|
627
|
+
params.set('search', searchQuery);
|
|
628
|
+
const makeRequest = async (token) => {
|
|
629
|
+
return requestWithRetry(`${apiUrl}/${type}?${params.toString()}`, {
|
|
630
|
+
method: "GET",
|
|
631
|
+
headers: {
|
|
632
|
+
Authorization: `Bearer ${token}`,
|
|
633
|
+
Accept: "application/json",
|
|
634
|
+
},
|
|
635
|
+
});
|
|
636
|
+
};
|
|
637
|
+
try {
|
|
638
|
+
const token = await getAuthToken();
|
|
639
|
+
return await makeRequest(token);
|
|
640
|
+
}
|
|
641
|
+
catch (err) {
|
|
642
|
+
if (err instanceof Error &&
|
|
643
|
+
err.message.includes("401") &&
|
|
644
|
+
hasCredentials) {
|
|
645
|
+
await handleAuthError();
|
|
646
|
+
try {
|
|
647
|
+
const freshToken = await getAuthToken();
|
|
648
|
+
return await makeRequest(freshToken);
|
|
649
|
+
}
|
|
650
|
+
catch {
|
|
651
|
+
// Retry failed
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return {
|
|
655
|
+
success: false,
|
|
656
|
+
message: err instanceof Error ? err.message : "Lookup failed",
|
|
657
|
+
data: [],
|
|
658
|
+
pagination: { limit, offset, page: 1, count: 0 },
|
|
659
|
+
search: searchQuery || null,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Lookup countries (for typeahead)
|
|
665
|
+
*/
|
|
666
|
+
async function getCountries(options) {
|
|
667
|
+
return getLocations('countries', options);
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Lookup states (for typeahead)
|
|
671
|
+
*/
|
|
672
|
+
async function getStates(options) {
|
|
673
|
+
return getLocations('states', options);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Lookup cities (for typeahead)
|
|
677
|
+
*/
|
|
678
|
+
async function getCities(options) {
|
|
679
|
+
return getLocations('cities', options);
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
search,
|
|
683
|
+
fetch,
|
|
684
|
+
searchAndFetch,
|
|
685
|
+
getCountries,
|
|
686
|
+
getStates,
|
|
687
|
+
getCities,
|
|
688
|
+
};
|
|
333
689
|
}
|
|
@@ -30,10 +30,62 @@ export interface ProviderResult {
|
|
|
30
30
|
score?: number;
|
|
31
31
|
confidence?: number;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Extended provider result that can return multiple emails
|
|
35
|
+
*/
|
|
36
|
+
export interface ProviderMultiResult {
|
|
37
|
+
emails: Array<{
|
|
38
|
+
email: string;
|
|
39
|
+
verified?: boolean;
|
|
40
|
+
confidence?: number;
|
|
41
|
+
isCatchAll?: boolean;
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Email type classification
|
|
47
|
+
*/
|
|
48
|
+
export type EmailType = 'business' | 'personal' | 'disposable' | 'role' | 'unknown';
|
|
49
|
+
/**
|
|
50
|
+
* Individual enriched email with full metadata
|
|
51
|
+
*/
|
|
52
|
+
export interface EnrichedEmail {
|
|
53
|
+
/** The email address */
|
|
54
|
+
email: string;
|
|
55
|
+
/** Which provider found this email */
|
|
56
|
+
source: ProviderName;
|
|
57
|
+
/** Confidence score (0-100) */
|
|
58
|
+
confidence: number;
|
|
59
|
+
/** Whether the email was verified */
|
|
60
|
+
verified: boolean;
|
|
61
|
+
/** Email type classification */
|
|
62
|
+
type: EmailType;
|
|
63
|
+
/** Whether this is a catch-all domain */
|
|
64
|
+
isCatchAll?: boolean;
|
|
65
|
+
/** ISO timestamp of when this was found */
|
|
66
|
+
foundAt: string;
|
|
67
|
+
/** Additional provider-specific metadata */
|
|
68
|
+
metadata?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Result containing all found emails from all providers
|
|
72
|
+
*/
|
|
73
|
+
export interface MultiEmailResult {
|
|
74
|
+
/** All found emails, sorted by confidence (highest first) */
|
|
75
|
+
emails: EnrichedEmail[];
|
|
76
|
+
/** The original candidate */
|
|
77
|
+
candidate: EnrichmentCandidate;
|
|
78
|
+
/** Total cost incurred across all providers */
|
|
79
|
+
totalCost: number;
|
|
80
|
+
/** List of providers that were queried */
|
|
81
|
+
providersQueried: ProviderName[];
|
|
82
|
+
/** ISO timestamp of when enrichment completed */
|
|
83
|
+
completedAt: string;
|
|
84
|
+
}
|
|
33
85
|
/**
|
|
34
86
|
* Provider function signature
|
|
35
87
|
*/
|
|
36
|
-
export type ProviderFunc = (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
|
|
88
|
+
export type ProviderFunc = (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
|
|
37
89
|
/**
|
|
38
90
|
* Input candidate for enrichment - flexible to accept various naming conventions
|
|
39
91
|
*/
|
|
@@ -193,12 +245,16 @@ export interface EnrichmentClientConfig {
|
|
|
193
245
|
* Enrichment client interface
|
|
194
246
|
*/
|
|
195
247
|
export interface EnrichmentClient {
|
|
196
|
-
/** Enrich a single candidate */
|
|
248
|
+
/** Enrich a single candidate - returns first valid business email (original behavior) */
|
|
197
249
|
enrich: (candidate: EnrichmentCandidate) => Promise<CanonicalEmail>;
|
|
198
|
-
/** Enrich multiple candidates in batches */
|
|
250
|
+
/** Enrich multiple candidates in batches - returns first valid business email per candidate */
|
|
199
251
|
enrichBatch: (candidates: EnrichmentCandidate[], options?: BatchEnrichmentOptions) => Promise<Array<{
|
|
200
252
|
candidate: EnrichmentCandidate;
|
|
201
253
|
} & CanonicalEmail>>;
|
|
254
|
+
/** Enrich a single candidate - returns ALL found emails from ALL providers */
|
|
255
|
+
enrichAll: (candidate: EnrichmentCandidate) => Promise<MultiEmailResult>;
|
|
256
|
+
/** Enrich multiple candidates - returns ALL found emails for each candidate */
|
|
257
|
+
enrichAllBatch: (candidates: EnrichmentCandidate[], options?: BatchEnrichmentOptions) => Promise<MultiEmailResult[]>;
|
|
202
258
|
}
|
|
203
259
|
/**
|
|
204
260
|
* Available provider names
|
|
@@ -260,26 +316,77 @@ export interface SmartProspectContact {
|
|
|
260
316
|
verificationStatus?: string;
|
|
261
317
|
catchAllStatus?: string;
|
|
262
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* SmartProspect Search Filters
|
|
321
|
+
*
|
|
322
|
+
* Comprehensive filter interface matching SmartProspect/Smartlead's actual API.
|
|
323
|
+
* Field names and values match their exact API structure.
|
|
324
|
+
*/
|
|
263
325
|
export interface SmartProspectSearchFilters {
|
|
326
|
+
/** Full name (searches across first + last) */
|
|
264
327
|
name?: string[];
|
|
328
|
+
/** First name only */
|
|
265
329
|
firstName?: string[];
|
|
330
|
+
/** Last name only */
|
|
266
331
|
lastName?: string[];
|
|
332
|
+
/** Job titles to search (free text, e.g., "CEO", "Software Engineer") */
|
|
267
333
|
title?: string[];
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
334
|
+
/** Whether to match titles exactly (default: false) */
|
|
335
|
+
titleExactMatch?: boolean;
|
|
336
|
+
/** Departments - must use exact API values */
|
|
337
|
+
department?: SmartProspectDepartment[];
|
|
338
|
+
/** Seniority levels - must use exact API values */
|
|
339
|
+
level?: SmartProspectLevel[];
|
|
340
|
+
/** Company names (e.g., "Google", "Microsoft") */
|
|
341
|
+
companyName?: string[];
|
|
342
|
+
/** Company domains (e.g., "google.com", "microsoft.com") */
|
|
343
|
+
companyDomain?: string[];
|
|
344
|
+
/** Industries - must use exact API values */
|
|
345
|
+
companyIndustry?: SmartProspectIndustry[];
|
|
346
|
+
/** Sub-industries - must use exact API values */
|
|
347
|
+
companySubIndustry?: string[];
|
|
348
|
+
/** Headcount ranges - must use exact API values */
|
|
349
|
+
companyHeadCount?: SmartProspectHeadcount[];
|
|
350
|
+
/** Revenue ranges - must use exact API values */
|
|
351
|
+
companyRevenue?: SmartProspectRevenue[];
|
|
352
|
+
/** Countries (e.g., "United States", "United Kingdom", "Germany") */
|
|
273
353
|
country?: string[];
|
|
354
|
+
/** States with country (e.g., "California, United States", "Bavaria, Germany") */
|
|
274
355
|
state?: string[];
|
|
356
|
+
/** Cities with state and country (e.g., "San Francisco, California, United States") */
|
|
275
357
|
city?: string[];
|
|
276
|
-
|
|
277
|
-
companyRevenue?: string[];
|
|
358
|
+
/** Don't include contacts already owned/in campaigns (default: true) */
|
|
278
359
|
dontDisplayOwnedContact?: boolean;
|
|
360
|
+
/** Number of results to return (default: 25, max: 100) */
|
|
279
361
|
limit?: number;
|
|
280
|
-
|
|
362
|
+
/** Scroll ID for cursor-based pagination (from previous response) */
|
|
281
363
|
scroll_id?: string;
|
|
282
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* SmartProspect Department values (exact API values)
|
|
367
|
+
*/
|
|
368
|
+
export type SmartProspectDepartment = 'Engineering' | 'Finance & Administration' | 'Human Resources' | 'IT & IS' | 'Marketing' | 'Operations' | 'Other' | 'Support' | 'Sales';
|
|
369
|
+
/**
|
|
370
|
+
* SmartProspect Level/Seniority values (exact API values)
|
|
371
|
+
*/
|
|
372
|
+
export type SmartProspectLevel = 'Staff' | 'Manager-Level' | 'Director-Level' | 'VP-Level' | 'C-Level';
|
|
373
|
+
/**
|
|
374
|
+
* SmartProspect Headcount ranges (exact API values)
|
|
375
|
+
*/
|
|
376
|
+
export type SmartProspectHeadcount = '0 - 25' | '25 - 100' | '100 - 250' | '250 - 1000' | '1K - 10K' | '10K - 50K' | '50K - 100K' | '> 100K';
|
|
377
|
+
/**
|
|
378
|
+
* SmartProspect Revenue ranges (exact API values)
|
|
379
|
+
*/
|
|
380
|
+
export type SmartProspectRevenue = '$0 - 1M' | '$1 - 10M' | '$10 - 50M' | '$50 - 100M' | '$100 - 250M' | '$250 - 500M' | '$500M - 1B' | '> $1B';
|
|
381
|
+
/**
|
|
382
|
+
* SmartProspect Industry values (exact API values)
|
|
383
|
+
*/
|
|
384
|
+
export type SmartProspectIndustry = 'Software & Internet' | 'Business Services' | 'Real Estate & Construction' | 'Financial Services' | 'Healthcare, Pharmaceuticals, & Biotech' | 'Retail' | 'Consumer Services' | 'Education' | 'Media & Entertainment' | 'Travel, Recreation, and Leisure' | 'Transportation & Storage' | 'Manufacturing' | 'Wholesale & Distribution' | 'Non-Profit' | 'Energy & Utilities' | 'Government' | 'Agriculture & Mining' | 'Computers & Electronics' | 'Telecommunications' | 'Other';
|
|
385
|
+
/**
|
|
386
|
+
* SmartProspect Sub-Industry values (exact API values - partial list)
|
|
387
|
+
* Note: This is a subset of available sub-industries. The API accepts many more.
|
|
388
|
+
*/
|
|
389
|
+
export declare const SMARTPROSPECT_SUB_INDUSTRIES: readonly ["Internet", "Information Technology and Services", "Information Services", "Computer Software", "Computer & Network Security", "Computer Games", "Glass, Ceramics & Concrete", "Construction", "Commercial Real Estate", "Civil Engineering", "Building Materials", "Architecture & Planning", "Writing and Editing", "Translation and Localization", "Think Tanks", "Staffing and Recruiting", "Security and Investigations", "Public Safety", "Public Relations and Communications", "Program Development", "Professional Training & Coaching", "Market Research", "Marketing and Advertising", "Management Consulting", "Legal Services", "Law Practice", "Law Enforcement", "International Trade and Development", "Import and Export", "Human Resources", "Graphic Design", "Facilities Services", "Executive Office", "Events Services", "Environmental Services", "Design", "Business Supplies and Equipment", "Animation", "Alternative Dispute Resolution", "Outsourcing/Offshoring"];
|
|
283
390
|
export interface SmartProspectSearchResponse {
|
|
284
391
|
success: boolean;
|
|
285
392
|
message: string;
|
|
@@ -290,6 +397,27 @@ export interface SmartProspectSearchResponse {
|
|
|
290
397
|
total_count: number;
|
|
291
398
|
};
|
|
292
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* SmartProspect Location lookup response (countries, states, cities)
|
|
402
|
+
*/
|
|
403
|
+
export interface SmartProspectLocationItem {
|
|
404
|
+
id: number;
|
|
405
|
+
country_name?: string;
|
|
406
|
+
state_name?: string;
|
|
407
|
+
city_name?: string;
|
|
408
|
+
}
|
|
409
|
+
export interface SmartProspectLocationResponse {
|
|
410
|
+
success: boolean;
|
|
411
|
+
message: string;
|
|
412
|
+
data: SmartProspectLocationItem[];
|
|
413
|
+
pagination: {
|
|
414
|
+
limit: number;
|
|
415
|
+
offset: number;
|
|
416
|
+
page: number;
|
|
417
|
+
count: number;
|
|
418
|
+
};
|
|
419
|
+
search: string | null;
|
|
420
|
+
}
|
|
293
421
|
export interface SmartProspectFetchResponse {
|
|
294
422
|
success: boolean;
|
|
295
423
|
message: string;
|