DEV Community

shashank ms
shashank ms

Posted on

Integrating LLM with Existing CRM: A Step-by-Step Guide

We are going to build a sales outreach agent that reads customer context from an existing CRM and drafts personalized follow-up emails. This helps sales teams automate pipeline touchpoints without replacing their current tools. I will use Oxlo.ai as the inference backend because its request-based pricing keeps costs flat even when I stuff long CRM histories into the prompt.

What you'll need

Step 1: Init the Oxlo.ai client

I start by importing the SDK and pointing the client at Oxlo.ai. This is a drop-in replacement for the standard OpenAI client.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

Step 2: Create a mock CRM

Most teams already have a CRM, so I will simulate one with a local SQLite database containing leads and interaction history.

import sqlite3

def init_crm(db_path="crm.db"):
    conn = sqlite3.connect(db_path)
    cur = conn.cursor()
    cur.execute("""
        CREATE TABLE IF NOT EXISTS leads (
            id INTEGER PRIMARY KEY,
            name TEXT,
            company TEXT,
            status TEXT,
            last_contact TEXT
        )
    """)
    cur.execute("""
        CREATE TABLE IF NOT EXISTS interactions (
            id INTEGER PRIMARY KEY,
            lead_id INTEGER,
            note TEXT,
            created_at TEXT
        )
    """)
    cur.execute(
        "INSERT OR IGNORE INTO leads VALUES "
        "(1, 'Alice Smith', 'Acme Corp', 'Qualified', '2025-05-01')"
    )
    cur.execute(
        "INSERT OR IGNORE INTO interactions VALUES "
        "(1, 1, 'Alice asked for pricing on 2025-05-01. She is comparing vendors.', '2025-05-01')"
    )
    conn.commit()
    return conn

Step 3: Fetch lead context

I need a helper that pulls the lead record and all related notes so the LLM has full context to work with.

def get_lead_context(conn, lead_id):
    cur = conn.cursor()
    cur.execute(
        "SELECT name, company, status, last_contact FROM leads WHERE id = ?",
        (lead_id,),
    )
    lead = cur.fetchone()
    cur.execute(
        "SELECT note, created_at FROM interactions WHERE lead_id = ? ORDER BY created_at",
        (lead_id,),
    )
    notes = cur.fetchall()
    return {"lead": lead, "notes": notes}

Step 4: Define the system prompt

The system prompt grounds the model in the CRM data and constrains the output format.

SYSTEM_PROMPT = """You are a sales assistant that drafts personalized outreach emails based on CRM context.
You will receive a JSON blob containing a lead's profile and interaction history.
Draft a concise, professional follow-up email.
Rules:
- Reference specific details from the interaction history.
- Do not invent facts not present in the context.
- Output only the email body, no subject line or salutation prefix like "Email:".
- Keep the tone consultative, not pushy.
"""

Step 5: Build the outreach generator

This function assembles the CRM context into a user message and calls Oxlo.ai. I am using Llama 3.3 70B because it handles structured instructions and long context reliably.

import json

def draft_outreach(lead_context):
    user_message = json.dumps(lead_context, indent=2)
    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
    )
    return response.choices[0].message.content

Run it

Now I wire the pieces together, execute the pipeline, and print the result.

if __name__ == "__main__":
    conn = init_crm()
    context = get_lead_context(conn, 1)
    email_body = draft_outreach(context)
    print("--- Generated Outreach ---")
    print(email_body)

Example output:

--- Generated Outreach ---
Hi Alice,

I hope you have had a chance to review the pricing we shared on May 1st. I know Acme Corp is evaluating vendors right now, so I wanted to check in and see if any questions have come up on your end.

Let me know if you need a deeper walkthrough of the specific features we discussed.

Best regards,
Sales Team

Wrap-up

To productionize this, connect the script to your live CRM via its REST API instead of SQLite, and schedule it with a cron job or a lightweight task queue like Celery. If you are processing large batches of leads with lengthy interaction histories, Oxlo.ai's request-based pricing means your bill stays flat regardless of how much context you include in each prompt. You can explore the details at https://oxlo.ai/pricing.

Top comments (0)