9.3 Custom Code Examples
You know how to add Custom Code columns and write basic scripts. Now let's explore real-world examples you can adapt for your automations. Each example shows the business problem, the solution, and how to implement it.
Example 1: Lead Scoring Algorithm
The Business Problem
Challenge: You need to score leads based on multiple factors—company size, industry, engagement, contact role, and more. Each factor has different weight.
Manual approach: Would require spreadsheet with complex formulas, or manual review
Custom code solution: Automated scoring that adapts to your criteria
The Setup
Create these columns first:
| Column Name | Type | Description |
|---|---|---|
| Text | Contact email | |
| Contact Role | Text | Job title |
| Email Opens | Number | Email open count |
| Link Clicks | Number | Link click count |
| Company Data | (From enrichment) | Company information |
| Lead Score | Custom Code | This is what we're building |
The Code
When adding the Lead Score column:
Click + Add Column
Choose Custom Code column type
Enter configuration:
- Column name:
lead_score - Display label: "Lead Score"
- Output type:
Map<String, Object>
- Column name:
Write your code:
// Custom lead scoring based on multiple factors
let score = 0
// Factor 1: Company size score
let companySize = [Company Size] || 0
if (companySize > 1000) {
score += 30
} else if (companySize > 100) {
score += 20
} else if (companySize > 10) {
score += 10
}
// Factor 2: Industry score
let industry = [Industry]
let targetIndustries = ['Technology', 'Finance', 'Healthcare']
if (targetIndustries.includes(industry)) {
score += 25
}
// Factor 3: Engagement score
let emailOpens = [Email Opens] || 0
let linkClicks = [Link Clicks] || 0
score += emailOpens * 2
score += linkClicks * 5
// Factor 4: Role score
let role = [Contact Role]
if (role && (role.includes('Director') || role.includes('VP'))) {
score += 20
}
if (role && (role.includes('C-level') || role.includes('CEO'))) {
score += 30
}
// Factor 5: Email domain score (educational institutions)
let email = [Email]
if (email && email.endsWith('.edu')) {
score += 15
}
// Return structured result
return {
total_score: score,
grade: score > 70 ? 'A' : score > 50 ? 'B' : score > 30 ? 'C' : 'D',
qualified: score > 50
}How It Works
Input: Multiple data points about the lead
Processing:
- Company size (0-30 points): Larger companies score higher
- Industry (0-25 points): Target industries get bonus
- Engagement (0-∞ points): More interaction = higher score
- Role (0-30 points): Decision-makers score higher
- Email domain (0-15 points): Educational institutions get bonus
Output: Object with three fields:
total_score: Numeric score (0-100+)grade: Letter grade (A/B/C/D)qualified: Boolean (true if score > 50)
Lead Scoring Results
Custom code calculates scores based on multiple factors
| Role | Company | Industry | Score | Grade | |
|---|---|---|---|---|---|
| john@bigtech.com | VP Engineering | 1500 employees | Technology | 85 | A |
| sarah@startup.io | Developer | 25 employees | Technology | 45 | C |
| mike@hospital.edu | CTO | 800 employees | Healthcare | 75 | B |
| jane@retail.com | Manager | 50 employees | Retail | 25 | D |
Scoring Factors:
- ✅ Company Size: 0-30 points
- ✅ Industry Match: 0-25 points
- ✅ Decision Maker Role: 0-30 points
- ✅ Email Domain: 0-15 points
Using the Results
In another column (e.g., filter qualified leads):
Create a new column and reference the Lead Score:
let scoring = [Lead Score]
let isQualified = scoring.qualified || false
if (isQualified) {
return "ROUTE TO SALES"
} else {
return "ROUTE TO NURTURE"
}Or as a condition on other columns:
- Open the column's Run Configuration
- Add a condition: "Only run if lead is qualified"
- The system checks
Lead Score.qualified == true
Customizing for Your Business
Change target industries:
Find this line in the code:
let targetIndustries = ['Technology', 'Finance', 'Healthcare']Replace with your industries:
let targetIndustries = ['Manufacturing', 'Retail', 'Logistics']Adjust scoring weights:
Give more weight to engagement:
score += emailOpens * 5 // Changed from 2
score += linkClicks * 10 // Changed from 5Add new factors:
// Factor 6: Geographic score
let country = [Country]
if (country === 'United States') {
score += 20
}Example 2: Data Cleaning and Standardization
The Business Problem
Challenge: Phone numbers come in various formats—some with country codes, some with dashes, some with parentheses. You need consistent format for CRM integration.
Examples of messy data:
(555) 123-4567555-123-45675551234567+1 555 123 45671-555-123-4567
Need: All formatted as +1 (555) 123-4567
The Setup
Create these columns:
| Column Name | Type | Description |
|---|---|---|
| Raw Phone | Text | Phone number as entered |
| Standardized Phone | Custom Code | Cleaned format |
The Code
When adding Standardized Phone column:
Add Custom Code column
Configuration:
- Column name:
standardized_phone - Display label: "Phone Number"
- Output type:
String
- Column name:
Write code:
// Standardize phone numbers
let phone = [Raw Phone]
// Return null if no phone
if (!phone) return null
// Remove all non-numeric characters
phone = phone.replace(/[^\d]/g, '')
// Format based on length
if (phone.length === 10) {
// US format (no country code)
return `+1 (${phone.slice(0,3)}) ${phone.slice(3,6)}-${phone.slice(6)}`
} else if (phone.length === 11 && phone[0] === '1') {
// US with leading 1
return `+${phone[0]} (${phone.slice(1,4)}) ${phone.slice(4,7)}-${phone.slice(7)}`
} else if (phone.length > 11) {
// International (keep as is with + prefix)
return `+${phone}`
} else {
// Invalid length - return cleaned but not formatted
return phone
}How It Works
Step 1: Remove all non-numeric characters
(555) 123-4567→5551234567+1-555-123-4567→15551234567
Step 2: Identify format based on length
- 10 digits: US number without country code
- 11 digits starting with 1: US number with country code
- Other: International format
Step 3: Apply formatting
- US:
+1 (555) 123-4567 - International:
+followed by digits
Phone Number Standardization
Custom code converts messy formats to consistent standard
(555) 123-4567+1 (555) 123-4567555-123-4567+1 (555) 123-45675551234567+1 (555) 123-4567+1 555 123 4567+1 (555) 123-45671-555-123-4567+1 (555) 123-4567What the code does:
- Removes all non-numeric characters
- Detects format based on digit count
- Applies US phone number formatting
- Adds country code (+1)
Testing
Test data to add:
| Raw Phone | Expected Result |
|---|---|
(555) 123-4567 | +1 (555) 123-4567 |
5551234567 | +1 (555) 123-4567 |
+1 555-123-4567 | +1 (555) 123-4567 |
442012345678 | +442012345678 |
Add these rows and verify all formats convert correctly!
Variations
For other countries (e.g., UK):
let phone = [Raw Phone]
if (!phone) return null
phone = phone.replace(/[^\d]/g, '')
if (phone.length === 10 && !phone.startsWith('44')) {
// UK number without country code
return `+44 ${phone.slice(0,4)} ${phone.slice(4,7)} ${phone.slice(7)}`
} else if (phone.length === 12 && phone.startsWith('44')) {
// UK with country code
return `+${phone.slice(0,2)} ${phone.slice(2,6)} ${phone.slice(6,9)} ${phone.slice(9)}`
} else {
return `+${phone}`
}For email standardization:
let email = [Raw Email]
if (!email) return null
// Convert to lowercase
email = email.toLowerCase().trim()
// Validate format
if (!email.includes('@') || !email.includes('.')) {
return null // Invalid email
}
return emailExample 3: Smart Caching with Conditional Processing
The Business Problem
Challenge: API enrichment is expensive ($1 per call). You don't want to call API every time—only when data is stale or missing.
Solution: Cache results, only refresh if older than 7 days
Savings: 90% reduction in API calls (refresh weekly vs. daily)
The Setup
Create these columns:
| Column Name | Type | Description |
|---|---|---|
| Text | Email to enrich | |
| Cached Enrichment | (Previous enrichment) | Previous result |
| Smart Enrichment | Custom Code | Smart caching logic |
The Code
When adding Smart Enrichment column:
Add Custom Code column
Configuration:
- Column name:
smart_enrichment - Display label: "Enrichment (Cached)"
- Output type:
Map<String, Object>
- Column name:
Write code:
let email = [Email]
let existingData = [Cached Enrichment]
// Check if cached data exists and is recent
if (existingData && existingData.cached_at) {
try {
// Calculate cache age in days
let cachedDate = new Date(existingData.cached_at)
let currentDate = new Date()
let cacheAge = (currentDate - cachedDate) / (1000 * 60 * 60 * 24)
// If cache is less than 7 days old, return it
if (cacheAge < 7) {
return {
from_cache: true,
cache_age_days: Math.round(cacheAge * 10) / 10,
data: existingData
}
}
} catch (e) {
// If date parsing fails, proceed to fetch fresh data
}
}
// No cache or cache is stale
// In real implementation, this would trigger your enrichment column
return {
from_cache: false,
needs_refresh: true,
email: email,
message: 'Cache miss - fresh data needed'
}How It Works
Decision tree:
Is there cached data?
├─ NO → Fetch fresh (call API)
└─ YES → Is cache < 7 days old?
├─ YES → Return cached (save $1)
└─ NO → Fetch fresh (call API)Cache hit (within 7 days):
- Returns existing data
- Sets
from_cache: true - Shows cache age
Cache miss (> 7 days or no cache):
- Returns marker
- Would trigger API call (in real implementation)
- Updates cache
Smart Caching in Action
Only refreshes data older than 7 days
| Last Enriched | Status | Cost | |
|---|---|---|---|
| john@example.com | 2 days ago | Using Cache | $0 |
| sarah@example.com | 5 days ago | Using Cache | $0 |
| mike@example.com | 8 days ago | Refreshing | $1 |
| jane@example.com | Never | New Call | $1 |
❌ Without Caching
$30,000
30,000 API calls per month
✅ With Caching
$4,300
4,300 API calls per month (86% savings!)
Cost Savings
Without caching:
- 1,000 leads
- Run daily for 30 days
- 30,000 API calls
- Cost: $30,000
With caching (7-day refresh):
- Initial: 1,000 API calls
- Weekly refresh: ~143 calls per day (1000 ÷ 7)
- Monthly: ~4,300 calls
- Cost: $4,300
- Savings: $25,700 (86%)
Example 4: Data Aggregation and Reporting
The Business Problem
Challenge: Calculate summary statistics from multiple columns—total orders, average order value, conversion rate.
Manual approach: Export to Excel, create pivot table
Custom code solution: Real-time calculation in taible
The Setup
Create these columns:
| Column Name | Type | Description |
|---|---|---|
| Orders | (Collection of orders) | Array of orders |
| Leads | (Collection of leads) | Array of leads |
| Summary Stats | Custom Code | Aggregated statistics |
The Code
// Get data
let orders = [Orders] || []
let leads = [Leads] || []
// Calculate order statistics
let totalOrders = orders.length
let totalRevenue = orders.reduce((sum, order) => sum + (order.amount || 0), 0)
let avgOrderValue = totalOrders > 0 ? totalRevenue / totalOrders : 0
// Calculate lead statistics
let totalLeads = leads.length
let qualifiedLeads = leads.filter(lead => lead.qualified === true).length
let conversionRate = totalLeads > 0 ? (totalOrders / totalLeads) * 100 : 0
// Find top order
let topOrder = orders.reduce((max, order) =>
(order.amount || 0) > (max.amount || 0) ? order : max
, {})
let topOrderAmount = topOrder.amount || 0
// Calculate time-based metrics
let now = new Date()
let ordersThisMonth = orders.filter(order => {
if (!order.date) return false
let orderDate = new Date(order.date)
return orderDate.getMonth() === now.getMonth() &&
orderDate.getFullYear() === now.getFullYear()
}).length
// Return comprehensive summary
return {
// Order metrics
total_orders: totalOrders,
total_revenue: Math.round(totalRevenue * 100) / 100,
avg_order_value: Math.round(avgOrderValue * 100) / 100,
top_order_amount: topOrderAmount,
orders_this_month: ordersThisMonth,
// Lead metrics
total_leads: totalLeads,
qualified_leads: qualifiedLeads,
qualification_rate: Math.round((qualifiedLeads / totalLeads) * 1000) / 10,
// Performance metrics
conversion_rate: Math.round(conversionRate * 100) / 100,
// Summary
status: totalOrders > 0 ? 'ACTIVE' : 'NO_ORDERS',
performance: conversionRate > 5 ? 'GOOD' : conversionRate > 2 ? 'AVERAGE' : 'NEEDS_IMPROVEMENT'
}Output Example
The cell will display a structured object:
Real-Time Summary Statistics
Custom code calculates metrics from multiple columns
Using in Other Columns
Access individual metrics in another column:
let stats = [Summary Stats]
let revenue = stats.total_revenue || 0
let conversionRate = stats.conversion_rate || 0
return `Revenue: $${revenue}, Conversion: ${conversionRate}%`Or display formatted in a dashboard
Common Code Patterns
Pattern 1: Safe Data Access
Always use fallback values to prevent errors:
let value = [Column Name] || 'default'
let number = [Number Column] || 0
let array = [List Column] || []Pattern 2: Conditional Return
Exit early if required data is missing:
if (!requiredField) return null
// ... rest of code only runs if field existsPattern 3: Error Handling
Wrap risky operations:
try {
let result = riskyOperation()
return result
} catch (e) {
return { error: true, message: e.message }
}Pattern 4: Calculate and Format
Round numbers for display:
let total = items.reduce((sum, item) => sum + item.amount, 0)
return Math.round(total * 100) / 100 // 2 decimal placesPattern 5: Filter and Transform Collections
Process arrays:
let filtered = items.filter(item => item.status === 'active')
let names = filtered.map(item => item.name)
return namesViewing Your Code Results
In the Cell
When a Custom Code column executes successfully, you'll see:
Custom Code Column Configuration
Write your own logic to process data
Access column data, write custom logic, and return results
- A green checkmark badge with the result
- Click the cell to expand and see the full object structure
- If it returns an object, you'll see a structured view
In the Sidebar
Click any Custom Code cell to open the sidebar and see:
- Full JSON structure of the result
- All nested properties and values
- Copy values for use elsewhere
Summary: Custom Code Examples
You now have real-world examples:
✅ Lead Scoring: Multi-factor scoring algorithm with grades
✅ Data Cleaning: Phone number standardization with format detection
✅ Smart Caching: Conditional processing to save API costs
✅ Data Aggregation: Calculate summary statistics from collections
Next steps:
- Choose an example that fits your use case
- Adapt the code to your column names
- Test with sample data
- Customize logic for your business rules
Next Steps
You've completed Chapter 9: Custom Code Columns! You can now:
✅ Understand when to use custom code ✅ Add Custom Code columns via UI ✅ Write scripts for automation ✅ Implement real-world examples ✅ Access data, handle errors, and optimize performance
Next chapter will explore additional advanced features and best practices for building complex automations.
Let's continue building! 🚀