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_URLenvironment variable (e.g.,https://yourorg.my.salesforce.com) - Salesforce access token stored as
SALESFORCE_ACCESS_TOKENenvironment variable - Slack Bot Token stored as
SLACK_BOT_TOKENenvironment variable - Slack channel ID stored as
SLACK_CHANNEL_IDenvironment 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-alertsStep 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.pyWhen 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.