Series 4 — Part 8 of 8

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" field
  • objection_type → CRM "Primary Objection" field
  • follow_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.