Send a Slack alert when a Salesforce deal changes stage using an agent skill

low complexityCost: Usage-based

Prerequisites

Prerequisites
  • Claude Code or another agent that supports the Agent Skills standard
  • Salesforce instance URL stored as SALESFORCE_INSTANCE_URL environment variable (e.g., https://yourorg.my.salesforce.com)
  • Salesforce access token stored as SALESFORCE_ACCESS_TOKEN environment variable
  • Slack Bot Token stored as SLACK_BOT_TOKEN environment variable
  • Slack channel ID stored as SLACK_CHANNEL_ID environment variable

Overview

This approach creates an agent skill that queries Salesforce for recently modified opportunities, checks for stage changes, and posts alerts to Slack. Unlike flow-based approaches, this runs on-demand or on a schedule — ideal for a periodic check rather than real-time alerts.

Step 1: Create the skill

Create .claude/skills/sf-deal-stage-alerts/SKILL.md:

---
name: sf-deal-stage-alerts
description: Check for recent Salesforce opportunity stage changes and post alerts to Slack
disable-model-invocation: true
allowed-tools: Bash(python *)
---
 
Check for Salesforce opportunities that changed stage in the last hour and post alerts to Slack.
 
Run: `python $SKILL_DIR/scripts/check_stages.py`

Step 2: Write the script

Create .claude/skills/sf-deal-stage-alerts/scripts/check_stages.py:

#!/usr/bin/env python3
import os, sys, requests
 
INSTANCE_URL = os.environ.get("SALESFORCE_INSTANCE_URL")
ACCESS_TOKEN = os.environ.get("SALESFORCE_ACCESS_TOKEN")
SLACK_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
SLACK_CHANNEL = os.environ.get("SLACK_CHANNEL_ID")
 
if not all([INSTANCE_URL, ACCESS_TOKEN, SLACK_TOKEN, SLACK_CHANNEL]):
    print("ERROR: Set SALESFORCE_INSTANCE_URL, SALESFORCE_ACCESS_TOKEN, SLACK_BOT_TOKEN, SLACK_CHANNEL_ID")
    sys.exit(1)
 
SF_HEADERS = {
    "Authorization": f"Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
}
 
# Query recently modified opportunities
soql = (
    "SELECT Id, Name, Amount, StageName, LastModifiedDate "
    "FROM Opportunity "
    "WHERE LastModifiedDate > LAST_N_HOURS:1 AND StageName != null"
)
resp = requests.get(
    f"{INSTANCE_URL}/services/data/v59.0/query",
    headers=SF_HEADERS,
    params={"q": soql},
)
resp.raise_for_status()
records = resp.json().get("records", [])
 
if not records:
    print("No opportunities modified in the last hour")
    sys.exit(0)
 
# Post to Slack
from slack_sdk import WebClient
slack = WebClient(token=SLACK_TOKEN)
 
for opp in records:
    amount = float(opp.get("Amount") or 0)
    opp_url = f"{INSTANCE_URL}/{opp['Id']}"
 
    slack.chat_postMessage(
        channel=SLACK_CHANNEL,
        text=f"Deal updated: {opp['Name']}",
        blocks=[
            {"type": "section", "text": {"type": "mrkdwn",
                "text": f"🔄 *Deal Stage Changed*\n*{opp['Name']}* is in *{opp['StageName']}*\nAmount: ${amount:,.0f}"}},
            {"type": "context", "elements": [{"type": "mrkdwn",
                "text": f"<{opp_url}|View in Salesforce>"}]}
        ]
    )
 
print(f"Posted {len(records)} deal alerts to Slack")

Step 3: Run it

/sf-deal-stage-alerts

Step 4: Schedule (optional)

For hourly checks, schedule via Cowork or cron:

# crontab — run every hour
0 * * * * cd /path/to/project && python .claude/skills/sf-deal-stage-alerts/scripts/check_stages.py

When to use this approach

  • You want periodic digest-style alerts, not real-time
  • You don't want to maintain Salesforce flows or Connected Apps for n8n
  • You want to run checks on demand during pipeline reviews

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.