La plupart des équipes commerciales qualifient leurs leads à la main. C'est long, subjectif, et les critères changent selon l'humeur du jour.
Voici comment j'ai remplacé ce processus pour un client B2B en une journée de travail.
Le problème de départ
Mon client recevait ~80 leads par semaine via son formulaire de contact. Le commercial passait 3-4h à trier, noter, et décider qui rappeler en premier.
Les critères de scoring étaient dans sa tête :
- Budget mentionné ou non
- Secteur d'activité (certains valent 3x plus)
- Urgence exprimée
- Taille d'entreprise estimée
- Qualité de la description du besoin
Objectif : automatiser ce tri, scorer 0-100, envoyer les HOT directement en Slack.
L'architecture
Formulaire Contact
↓
Webhook n8n
↓
Enrichissement (Clearbit ou manuel)
↓
Scoring IA (GPT-4o-mini)
↓
Stockage (Airtable / Notion)
↓
Alertes Slack si score > 70
Le prompt de scoring
C'est la partie la plus importante. Un bon prompt donne des résultats consistants.
const SCORING_PROMPT = `
Tu es un expert en qualification de leads B2B pour une agence de développement web & automation.
CRITÈRES DE SCORING (total 100 points) :
- Budget mentionné ≥ 5000€ : +25 pts
- Budget mentionné 2000-5000€ : +15 pts
- Urgence exprimée (< 1 mois) : +20 pts
- Secteur prioritaire (SaaS, e-commerce, finance) : +15 pts
- Décideur identifiable (CEO, CTO, Directeur) : +15 pts
- Description précise du besoin (> 50 mots) : +10 pts
- Entreprise identifiable (nom réel, site web) : +10 pts
- Email pro (pas gmail/hotmail) : +5 pts
TIERS :
- HOT : 75-100 (appel dans les 4h)
- WARM : 50-74 (appel dans les 24h)
- COLD : 0-49 (email de nurturing)
Retourne UNIQUEMENT un JSON valide avec cette structure :
{
"score": number,
"tier": "HOT" | "WARM" | "COLD",
"signals_detected": string[],
"recommended_action": string,
"reasoning": string
}
`
Le code de qualification
interface LeadData {
name: string
email: string
company?: string
message: string
budget?: string
urgency?: string
}
interface LeadScore {
score: number
tier: 'HOT' | 'WARM' | 'COLD'
signals_detected: string[]
recommended_action: string
reasoning: string
}
async function scoreLead(lead: LeadData): Promise<LeadScore> {
const userMessage = `
Lead à qualifier :
- Nom : ${lead.name}
- Email : ${lead.email}
- Entreprise : ${lead.company ?? 'Non renseignée'}
- Budget : ${lead.budget ?? 'Non mentionné'}
- Urgence : ${lead.urgency ?? 'Non mentionnée'}
- Message : ${lead.message}
`.trim()
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: SCORING_PROMPT },
{ role: 'user', content: userMessage },
],
response_format: { type: 'json_object' },
temperature: 0.1, // faible pour des résultats consistants
})
return JSON.parse(response.choices[0].message.content ?? '{}') as LeadScore
}
Intégration n8n
Dans n8n, le workflow ressemble à ça :
[Webhook] → [Fonction JS : formatLead()] → [HTTP Request : /api/score-lead] → [Switch : tier]
↓
HOT → [Slack #leads-chauds]
WARM → [Airtable + email]
COLD → [Séquence nurturing]
La clé : le nœud Switch utilise {{ $json.tier }} pour router automatiquement.
Résultats après 6 semaines
- Temps de traitement : 3-4h/semaine → 10 min de review des HOT
- Taux de conversion lead → appel : +34% (les bons leads rappelés plus vite)
- Coût IA : ~$2/mois pour 80 leads/semaine avec GPT-4o-mini
- Consistance : même score pour le même lead, quelle que soit l'heure
Les pièges à éviter
1. Prompt trop générique Un prompt qui dit juste "note ce lead de 1 à 10" donnera des résultats inconsistants. Définissez des critères précis et pondérés.
2. Temperature trop haute
Pour du scoring, mettez temperature: 0.1 ou 0. Vous voulez de la reproductibilité, pas de la créativité.
3. Oublier le fallback
const score = await scoreLead(lead).catch(() => ({
score: 50,
tier: 'WARM' as const,
signals_detected: [],
recommended_action: 'Review manuel requis',
reasoning: 'Erreur lors du scoring automatique',
}))
4. Ne pas logger les décisions
Stockez toujours le reasoning et les signals_detected. C'est crucial pour affiner le prompt plus tard.
Calibrer le score dans le temps
Un prompt de scoring qui fonctionne bien à J1 peut dériver après quelques semaines : de nouveaux types de leads arrivent, le marché change, et les critères pondérés à la main ne reflètent plus la réalité. Le signal le plus fiable pour détecter ça n'est pas technique, c'est commercial : demander chaque mois au commercial de contredire le score sur 5 à 10 leads au hasard.
interface ScoreFeedback {
leadId: string
aiScore: number
aiTier: 'HOT' | 'WARM' | 'COLD'
humanTier: 'HOT' | 'WARM' | 'COLD' // ce que le commercial aurait mis
agreement: boolean
}
Si le taux d'accord tombe sous 80%, c'est le signal qu'il faut revoir les critères pondérés du prompt — pas ajouter une couche de complexité par-dessus, juste réajuster les poids en fonction de ce qui a réellement changé (un secteur qui convertit moins bien qu'avant, un budget minimum qui a évolué). Sur le client cité plus haut, cette revue mensuelle a pris 15 minutes et a permis d'ajuster le poids du critère "secteur prioritaire" après l'ouverture d'un nouveau marché vertical — sans ce garde-fou, le score serait resté calé sur des hypothèses vieilles de plusieurs mois.
Pour aller plus loin
Ce système basique peut être enrichi avec :
- Données firmographiques (Clearbit, Hunter) pour compléter le profil
- Historique d'interaction (pages visitées, emails ouverts)
- Scoring décroissant (un lead WARM qui ne répond pas en 48h descend à COLD)
- A/B testing de prompts pour optimiser la précision
Le ROI de ce type de système est immédiat : dès la première semaine, votre commercial sait exactement sur qui concentrer son énergie.