Nothing kills deal momentum faster than a stage change that nobody notices. When a prospect moves to Negotiation or a deal slips back to Discovery, your sales leaders need to know immediately, not in next week’s pipeline review. This bot watches Salesforce and posts deal alerts directly to Slack in real time.

Architecture Overview

The bot runs as a simple Python script on a schedule. Every few minutes it queries Salesforce for recently modified opportunities, compares their current stage to the last known stage, and posts a formatted message to Slack when something changes.

Component Tool Purpose
CRM connection simple_salesforce Query opportunity data
Alert delivery slack_sdk Post messages to channels
State tracking SQLite Remember last-known stages
Scheduling cron / Task Scheduler Run the script periodically

Setting Up Dependencies

pip install simple-salesforce slack-sdk

You will need a Slack Bot Token from your Slack workspace. Create a new app at api.slack.com, give it the chat:write scope, and install it to your workspace.

Tracking Stage Changes

The key challenge is knowing when a stage actually changed. The bot stores the last-known stage for each opportunity in a lightweight SQLite database:

import sqlite3

def get_db():
    conn = sqlite3.connect("deal_stages.db")
    conn.execute("""
        CREATE TABLE IF NOT EXISTS stages (
            opp_id TEXT PRIMARY KEY,
            stage TEXT,
            last_checked TEXT
        )
    """)
    return conn

def has_stage_changed(conn, opp_id, current_stage):
    row = conn.execute(
        "SELECT stage FROM stages WHERE opp_id = ?", (opp_id,)
    ).fetchone()
    if row is None or row[0] != current_stage:
        conn.execute(
            "REPLACE INTO stages VALUES (?, ?, datetime('now'))",
            (opp_id, current_stage),
        )
        conn.commit()
        return row is not None  # Only alert on changes, not new records
    return False

Querying Salesforce for Recent Changes

Pull opportunities modified in the last 15 minutes to catch any changes since the last run:

from simple_salesforce import Salesforce
import os

sf = Salesforce(
    username=os.environ["SF_USERNAME"],
    password=os.environ["SF_PASSWORD"],
    security_token=os.environ["SF_TOKEN"],
)

query = """
    SELECT Id, Name, StageName, Amount, Owner.Name
    FROM Opportunity
    WHERE LastModifiedDate = LAST_N_MINUTES:15
    AND Amount > 0
"""
results = sf.query_all(query)

Posting to Slack

When a stage change is detected, the bot sends a rich message to your designated Slack channel:

from slack_sdk import WebClient

slack = WebClient(token=os.environ["SLACK_BOT_TOKEN"])

def post_deal_alert(opp):
    amount = f"${opp['Amount']:,.0f}"
    msg = (
        f":rotating_light: *Deal Stage Change*\n"
        f"*{opp['Name']}* moved to *{opp['StageName']}*\n"
        f"Amount: {amount} | Owner: {opp['Owner']['Name']}"
    )
    slack.chat_postMessage(channel="#deal-alerts", text=msg)

Putting It All Together

The main loop ties everything together:

def main():
    conn = get_db()
    results = sf.query_all(query)
    for record in results["records"]:
        if has_stage_changed(conn, record["Id"], record["StageName"]):
            post_deal_alert(record)
    conn.close()

if __name__ == "__main__":
    main()

Schedule it with cron to run every 10 minutes:

*/10 * * * * cd /path/to/bot && python deal_alerts.py

Key Takeaways

  • A Slack deal alert bot can be built in under 100 lines of Python using simple_salesforce and slack_sdk
  • SQLite provides a lightweight way to track stage state without adding infrastructure
  • Scheduling with cron keeps the solution simple and avoids the complexity of webhooks or streaming APIs
  • The bot ensures deal movement is visible to the entire team the moment it happens, not days later in a pipeline review