Alright folks, I've been living in the trenches of lead data migration and enrichment for the last quarter, and I've finally dialed in a workflow that feels robust. The core challenge I wanted to solve: our sales team was getting raw, unqualified leads into Gemini, which meant a lot of manual look-up work before the first touch. The goal was to automate enrichment *before* the lead record is even created in our CRM, using Clearbit's API.
Here's the high-level flow we built:
1. Lead captured on website form (minimal fields: email, name, company).
2. Form data sent to a middleware service (we use Zapier, but could be Make, a custom Node.js app, etc.).
3. Middleware calls Clearbit's Person & Company Enrichment API using the email domain.
4. Data is cleaned, merged, and deduplicated.
5. **Enriched lead payload is posted to Gemini via its API.**
The real "gotcha" wasn't the API calls themselves, but handling the data quality and fallbacks. Clearbit doesn't have data on everyone, and you need to decide what's a "hard fail" vs. what data you can proceed with.
Here’s a simplified version of the enrichment logic we run in our middleware. The key is structuring the final object to match Gemini's expected lead fields.
```javascript
// Pseudocode/Node.js style logic
async function enrichAndCreateLead(rawLead) {
const clearbitData = await fetchClearbitData(rawLead.email);
// Build the final lead object, with fallbacks
const enrichedLead = {
email: rawLead.email,
firstName: rawLead.firstName || clearbitData.person?.name?.givenName || '',
lastName: rawLead.lastName || clearbitData.person?.name?.familyName || '',
company: {
name: rawLead.companyName || clearbitData.company?.name || 'Unknown'
},
// Custom fields in Gemini - mapped from Clearbit
customFields: {
industry: clearbitData.company?.category?.industry || null,
linkedinUrl: clearbitData.person?.linkedin?.handle ? ` https://linkedin.com/in/${clearbitData.person.linkedin.handle}` : null,
companyFunding: clearbitData.company?.metrics?.raised || null,
jobTitle: clearbitData.person?.employment?.title || rawLead.jobTitle || null
}
};
// Critical validation: don't bomb out if enrichment fails
if (!enrichedLead.firstName && !enrichedLead.lastName) {
enrichedLead.firstName = 'Prospect'; // Placeholder to avoid empty required fields in Gemini
}
// Now POST to Gemini's lead creation endpoint
const geminiResponse = await createGeminiLead(enrichedLead);
return geminiResponse;
}
```
**Pitfalls & Learnings:**
* **API Costs & Timeouts:** Clearbit API calls add latency. We implemented a caching layer for common domains (like `@gmail.com`) to avoid unnecessary calls and control costs.
* **Data Conflicts:** What if the user-provided company name differs from Clearbit's? We added a simple rule: if the user provided a company name > 2 characters, we use it. Otherwise, we default to Clearbit's. Log the discrepancy for later review.
* **Field Mapping:** Gemini's custom field API names are crucial. You'll need to pre-configure those fields in Gemini and map them exactly in your payload. We keep a config file for this mapping.
* **Rollback Strategy:** If the Gemini POST fails after a successful Clearbit call (and you've been billed for it), what then? We log the enriched data to a dead-letter queue for manual review and retry. It's saved us a few times during Gemini API maintenance windows.
The result? Lead records in Gemini are now populated with company size, industry, LinkedIn profiles, and funding stage right from the first view. It's shaved about 5-7 minutes of research per lead for the sales team. The migration of our legacy "raw" leads to this new system was a whole other project involving batch enrichment and deduplication merges – maybe a topic for another thread!
Has anyone else built a similar pre-CRM enrichment pipeline? I'm particularly curious about how you've handled lead scoring logic based on the enriched data before the Gemini create call.
Backup twice, migrate once.