Lead Enrichment — Multi-Source Data Completion
Enrich CRM contact records by filling missing fields from multiple sources. Works with DuckDB workspace entries or standalone JSON data.
Sources (Priority Order)
- LinkedIn (via linkedin-scraper skill) — name, title, company, education, connections
- Web Search (via web_search tool) — email patterns, company info, social profiles
- Company Website (via web_fetch) — team pages, about pages, contact info
- Email Pattern Discovery — derive email from name + company domain
Enrichment Pipeline
Step 1: Assess What's Missing
-- Query the target object to find gaps
SELECT "Name", "Email", "LinkedIn URL", "Company", "Title", "Location"
FROM v_leads
WHERE "Email" IS NULL OR "LinkedIn URL" IS NULL OR "Title" IS NULL;
Step 2: Prioritize by Value
- High priority: Missing email (needed for outreach)
- Medium priority: Missing title/company (needed for personalization)
- Low priority: Missing education, connections count, about text
Step 3: Enrich Per Record
For each record with gaps:
If LinkedIn URL is known but other fields missing:
- Use linkedin-scraper to visit profile
- Extract: title, company, location, education, about
- Update DuckDB record
If LinkedIn URL is missing:
- Search LinkedIn:
{name} {company} or {name} {title}
- Verify match (name + company alignment)
- Store LinkedIn URL, then scrape full profile
If Email is missing:
- Find company domain (web search or LinkedIn company page)
- Try common patterns:
first@domain.com
first.last@domain.com
flast@domain.com
firstl@domain.com
- Optionally verify with web search:
"email" "{name}" site:{domain}
- Check company team/about page for email format clues
If Company info is missing:
- Web search:
"{name}" "{title}" or check LinkedIn
- Fetch company website for: industry, size, description, funding
Step 4: Update Records
-- Update via DuckDB pivot view
UPDATE v_leads SET
"Email" = ?,
"LinkedIn URL" = ?,
"Title" = ?,
"Company" = ?,
"Location" = ?
WHERE id = ?;
Bulk Enrichment Mode
For enriching many records at once:
- Query all incomplete records from DuckDB
- Group by company (scrape company once, apply to all employees)
- Process in batches of 10-20 records
- Report progress after each batch:
Enrichment Progress: 45/120 leads (38%)
├── Emails found: 32/45 (71%)
├── LinkedIn matched: 41/45 (91%)
├── Titles updated: 38/45 (84%)
└── ETA: ~15 min remaining
- Save checkpoint after each batch (in case of interruption)
Enrichment Quality Rules
- Confidence scoring: Mark each enriched field with confidence (high/medium/low)
- High: Direct match from LinkedIn profile or company website
- Medium: Inferred from patterns (email format) or partial match
- Low: Best guess from web search results
- Never overwrite existing data unless explicitly asked
- Flag conflicts: If enriched data contradicts existing data, flag for review
- Dedup check: Before inserting LinkedIn URL, check it's not already assigned to another contact
Email Pattern Discovery
Common corporate email formats by frequency:
first.last@domain.com (most common, ~45%)
first@domain.com (~20%)
flast@domain.com (~15%)
firstl@domain.com (~10%)
first_last@domain.com (~5%)
last.first@domain.com (~3%)
first.l@domain.com (~2%)
Strategy:
- If you know one person's email at the company, derive the pattern
- Search web for
"@{domain}" email format
- Check company team page source code for mailto: links
- Use the most common pattern as fallback
Output
After enrichment, provide a summary:
Enrichment Complete: 120 leads processed
├── Emails: 94 found (78%), 26 still missing
├── LinkedIn: 108 matched (90%), 12 not found
├── Titles: 115 updated (96%)
├── Companies: 118 confirmed (98%)
├── Locations: 89 found (74%)
└── Avg confidence: High (82%), Medium (14%), Low (4%)
Top gaps remaining:
- 26 leads missing email (mostly small/stealth companies)
- 12 leads missing LinkedIn (common names, ambiguous matches)
DuckDB Field Mapping
Standard field names for Ironclaw CRM objects:
| Enrichment Data |
DuckDB Field |
Type |
| Full name |
Name |
text |
| Email address |
Email |
email |
| LinkedIn URL |
LinkedIn URL |
url |
| Job title |
Title |
text |
| Company name |
Company |
text / relation |
| Location |
Location |
text |
| Education |
Education |
text |
| Phone |
Phone |
phone |
| Company size |
Company Size |
text |
| Industry |
Industry |
text |
| Enrichment date |
Enriched At |
date |
| Confidence |
Enrichment Confidence |
enum (high/medium/low) |
1---2name: lead-enrichment-23description: Enrich contact and lead records with LinkedIn profiles, email addresses, company data, and education info. Use when asked to "enrich contacts", "fill in missing data", "find emails for leads", "complete lead profiles", "look up company info", or any bulk data completion task for CRM records.4---5
6# Lead Enrichment — Multi-Source Data Completion
7
8Enrich CRM contact records by filling missing fields from multiple sources. Works with DuckDB workspace entries or standalone JSON data.
9
10## Sources (Priority Order)
11
121. **LinkedIn** (via linkedin-scraper skill) — name, title, company, education, connections
132. **Web Search** (via web_search tool) — email patterns, company info, social profiles
143. **Company Website** (via web_fetch) — team pages, about pages, contact info
154. **Email Pattern Discovery** — derive email from name + company domain
16
17## Enrichment Pipeline
18
19### Step 1: Assess What's Missing
20```sql
21-- Query the target object to find gaps
22SELECT "Name", "Email", "LinkedIn URL", "Company", "Title", "Location"
23FROM v_leads
24WHERE "Email" IS NULL OR "LinkedIn URL" IS NULL OR "Title" IS NULL;
25```
26
27### Step 2: Prioritize by Value
28- **High priority**: Missing email (needed for outreach)
29- **Medium priority**: Missing title/company (needed for personalization)
30- **Low priority**: Missing education, connections count, about text
31
32### Step 3: Enrich Per Record
33
34For each record with gaps:
35
36#### If LinkedIn URL is known but other fields missing:
371. Use linkedin-scraper to visit profile
382. Extract: title, company, location, education, about
393. Update DuckDB record
40
41#### If LinkedIn URL is missing:
421. Search LinkedIn: `{name} {company}` or `{name} {title}`
432. Verify match (name + company alignment)
443. Store LinkedIn URL, then scrape full profile
45
46#### If Email is missing:
471. Find company domain (web search or LinkedIn company page)
482. Try common patterns:
49 - `first@domain.com`
50 - `first.last@domain.com`
51 - `flast@domain.com`
52 - `firstl@domain.com`
533. Optionally verify with web search: `"email" "{name}" site:{domain}`
544. Check company team/about page for email format clues
55
56#### If Company info is missing:
571. Web search: `"{name}" "{title}"` or check LinkedIn
582. Fetch company website for: industry, size, description, funding
59
60### Step 4: Update Records
61```sql
62-- Update via DuckDB pivot view
63UPDATE v_leads SET
64 "Email" = ?,
65 "LinkedIn URL" = ?,
66 "Title" = ?,
67 "Company" = ?,
68 "Location" = ?
69WHERE id = ?;
70```
71
72## Bulk Enrichment Mode
73
74For enriching many records at once:
75
761. **Query all incomplete records** from DuckDB
772. **Group by company** (scrape company once, apply to all employees)
783. **Process in batches** of 10-20 records
794. **Report progress** after each batch:
80 ```
81 Enrichment Progress: 45/120 leads (38%)
82 ├── Emails found: 32/45 (71%)
83 ├── LinkedIn matched: 41/45 (91%)
84 ├── Titles updated: 38/45 (84%)
85 └── ETA: ~15 min remaining
86 ```
875. **Save checkpoint** after each batch (in case of interruption)
88
89## Enrichment Quality Rules
90
91- **Confidence scoring**: Mark each enriched field with confidence (high/medium/low)
92 - High: Direct match from LinkedIn profile or company website
93 - Medium: Inferred from patterns (email format) or partial match
94 - Low: Best guess from web search results
95- **Never overwrite existing data** unless explicitly asked
96- **Flag conflicts**: If enriched data contradicts existing data, flag for review
97- **Dedup check**: Before inserting LinkedIn URL, check it's not already assigned to another contact
98
99## Email Pattern Discovery
100
101Common corporate email formats by frequency:
1021. `first.last@domain.com` (most common, ~45%)
1032. `first@domain.com` (~20%)
1043. `flast@domain.com` (~15%)
1054. `firstl@domain.com` (~10%)
1065. `first_last@domain.com` (~5%)
1076. `last.first@domain.com` (~3%)
1087. `first.l@domain.com` (~2%)
109
110Strategy:
1111. If you know one person's email at the company, derive the pattern
1122. Search web for `"@{domain}" email format`
1133. Check company team page source code for mailto: links
1144. Use the most common pattern as fallback
115
116## Output
117
118After enrichment, provide a summary:
119
120```
121Enrichment Complete: 120 leads processed
122├── Emails: 94 found (78%), 26 still missing
123├── LinkedIn: 108 matched (90%), 12 not found
124├── Titles: 115 updated (96%)
125├── Companies: 118 confirmed (98%)
126├── Locations: 89 found (74%)
127└── Avg confidence: High (82%), Medium (14%), Low (4%)
128
129Top gaps remaining:
130- 26 leads missing email (mostly small/stealth companies)
131- 12 leads missing LinkedIn (common names, ambiguous matches)
132```
133
134## DuckDB Field Mapping
135
136Standard field names for Ironclaw CRM objects:
137
138| Enrichment Data | DuckDB Field | Type |
139|----------------|--------------|------|
140| Full name | Name | text |
141| Email address | Email | email |
142| LinkedIn URL | LinkedIn URL | url |
143| Job title | Title | text |
144| Company name | Company | text / relation |
145| Location | Location | text |
146| Education | Education | text |
147| Phone | Phone | phone |
148| Company size | Company Size | text |
149| Industry | Industry | text |
150| Enrichment date | Enriched At | date |
151| Confidence | Enrichment Confidence | enum (high/medium/low) |