A chat conversation that ends without a CRM record is a lost lead. This article covers how to link WhatsApp conversations to CRM contacts, auto-create leads from chat sessions, push behavioral scores to CRM fields, and trigger follow-up scheduling from conversation signals.
Linking Chat Sessions to CRM Records
CREATE TABLE crm_links (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
client_id INT UNSIGNED NOT NULL,
conversation_id BIGINT UNSIGNED NOT NULL,
crm_contact_id VARCHAR(255) NOT NULL,
crm_lead_id VARCHAR(255) DEFAULT NULL,
crm_system VARCHAR(50) NOT NULL DEFAULT 'internal',
linked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uq_conv_crm (conversation_id, crm_system),
FOREIGN KEY (conversation_id) REFERENCES conversations(id)
);
Auto-Creating Leads from WhatsApp Conversations
def extract_lead_fields(conversation_id: int, db) -> dict | None:
"""Extract structured lead data from conversation messages."""
messages = get_conversation_messages(conversation_id, db)
full_text = "\n".join(f"{m['role']}: {m['content']}" for m in messages)
prompt = """Extract the following fields from the conversation below.
Return JSON only. Use null for missing fields.
Fields: name, email, phone, company, product_interest, urgency_level (low/medium/high)
CONVERSATION:
""" + full_text
raw = generate_response("You are a data extraction assistant.", [{"role": "user", "content": prompt}])
try:
data = json.loads(raw)
# Only create a lead if we have at least name + one contact method
if data.get('name') and (data.get('email') or data.get('phone')):
return data
except json.JSONDecodeError:
pass
return None
def create_lead_from_conversation(client_id: int, conversation_id: int, db):
fields = extract_lead_fields(conversation_id, db)
if not fields:
return None
lead_id = crm_api.create_lead(client_id, fields)
db.execute("UPDATE crm_links SET crm_lead_id = ? WHERE conversation_id = ?",
lead_id, conversation_id)
return lead_id
Pushing Behavioral Scores to CRM Fields
the behavioral AI platform scores from the the chatbot platform adapter can be pushed as CRM custom fields. Map them deliberately:
intent_score→ CRM "Lead Score" fieldobjection_type→ CRM "Primary Objection" fieldfollow_up_tone→ CRM "Recommended Approach" field
Never push raw numeric scores without a label. A score of 0.73 means nothing to a salesperson. "High intent — timing objection — recommend urgency alignment" is actionable.
What to Watch For
- Extraction accuracy — LLM field extraction is not 100% reliable. Always validate extracted emails with a regex and phone numbers with a library before storing.
- Duplicate leads — Check by phone or email before creating a new lead. A returning prospect should update an existing CRM record, not create a duplicate.
- CRM rate limits — Most CRM APIs have rate limits. Push lead data via a background task, not synchronously in the message processing flow.