Skip to main content

Lead Scoring

A realistic B2B lead scoring pipeline that demonstrates record types, field access, arithmetic, conditionals, namespace imports, guards, and coalesce — most constellation-lang features in a single pipeline.

note

This recipe combines most constellation-lang features in one pipeline. Study it after completing the simpler recipes to see how features compose.

Use Case

Score business leads based on company size, revenue, engagement signals, and text analysis. Classify each lead as hot, warm, or cold.

The Pipeline

# lead-scoring-pipeline.cst

# Type definitions
type CompanyInfo = {
name: String,
industry: String,
employeeCount: Int,
annualRevenue: Int
}

type EngagementData = {
websiteVisits: Int,
emailOpens: Int,
contentDownloads: Int,
description: String
}

# Namespace imports
use stdlib.math
use stdlib.string as str
use stdlib.compare

# Inputs
in company: CompanyInfo
in engagement: EngagementData

@example("technology,software,AI")
in industryKeywords: String

@example(60)
in minScoreThreshold: Int

@example(2)
in scoreMultiplier: Int

# Feature extraction - text analysis
descriptionText = Trim(engagement.description)
normalizedDesc = Lowercase(descriptionText)
descWordCount = WordCount(normalizedDesc)
hasIndustryMatch = Contains(normalizedDesc, str.trim(industryKeywords))

# Company size scoring (conditional)
isLargeCompany = company.employeeCount > 500
isMediumCompany = company.employeeCount >= 50 and company.employeeCount <= 500
companySizeScore = if (isLargeCompany) 100 else if (isMediumCompany) 70 else 30

# Revenue scoring
revenueBase = company.annualRevenue / 10000
revenueScore = if (revenueBase > 100) 100 else revenueBase

# Engagement scoring
totalEngagement = engagement.websiteVisits + engagement.emailOpens + engagement.contentDownloads
hasHighEngagement = totalEngagement > 10
engagementScore = if (hasHighEngagement) 100 else if (totalEngagement >= 5) 60 else 20

# Text quality scoring
hasDetailedDescription = descWordCount > 50
textQualityScore = if (hasDetailedDescription) 100 else if (descWordCount >= 20) 60 else 25

# Qualification logic (boolean operators)
isQualified = revenueScore >= 50 and engagementScore >= 60
isHighPriority = isLargeCompany or (hasIndustryMatch and hasHighEngagement)

# Weighted final score
rawTotalScore = companySizeScore / 4 + revenueScore * 3 / 10 + engagementScore / 4 + textQualityScore / 5
industryBonus = if (hasIndustryMatch) 15 else 0
adjustedScore = rawTotalScore + industryBonus
finalScore = if (adjustedScore > 100) 100 else adjustedScore

# Classification
isHotLead = finalScore >= 80
isWarmLead = finalScore >= 50 and finalScore < 80
meetsMinimum = finalScore >= minScoreThreshold

# Guard and coalesce for tiered bonuses
tier1Bonus = 50 when finalScore >= 90
tier2Bonus = 30 when finalScore >= 70
tier3Bonus = 10 when finalScore >= 50
tieredBonus = tier1Bonus ?? tier2Bonus ?? tier3Bonus ?? 0

out finalScore
out isHotLead
out isWarmLead
out isQualified
out isHighPriority
out meetsMinimum
out tieredBonus

Explanation

SectionFeatures Used
Type definitionstype X = { field: Type }
Namespace importsuse stdlib.math, use stdlib.string as str
Field accesscompany.name, engagement.description
Module callsTrim(...), Lowercase(...), WordCount(...), Contains(...)
Arithmetic+, -, *, /
Comparisons>, >=, <=, ==
Boolean logicand, or, not
Conditionalsif (cond) x else y with nesting
Guardsexpr when condition
Coalescea ?? b ?? c ?? default

Running the Example

Input

{
"company": {
"name": "Acme Corp",
"industry": "technology",
"employeeCount": 250,
"annualRevenue": 5000000
},
"engagement": {
"websiteVisits": 8,
"emailOpens": 5,
"contentDownloads": 3,
"description": "Acme Corp is a technology company focused on AI and machine learning solutions."
},
"industryKeywords": "technology,software,AI",
"minScoreThreshold": 60,
"scoreMultiplier": 2
}

Output

{
"finalScore": 77,
"isHotLead": false,
"isWarmLead": true,
"isQualified": true,
"isHighPriority": true,
"meetsMinimum": true,
"tieredBonus": 30
}
tip

Use intermediate boolean variables like isLargeCompany and hasHighEngagement. They make complex scoring logic readable and self-documenting.

Best Practices

  1. Extract features first — normalize text and compute metrics before scoring
  2. Use intermediate variablesisLargeCompany, hasHighEngagement make the scoring logic readable
  3. Cap scores — use if (score > 100) 100 else score to prevent unbounded values
  4. Use guards for tiered logicexpr when condition with chained ?? is cleaner than deeply nested if/else