Tutorial ยท Claude Salesforce Integration

How to Integrate Claude with Salesforce: CRM AI Automation Guide

Salesforce is where your revenue data lives. But Salesforce alone doesn't tell you why a deal stalled, what the customer actually said on the last call, or which accounts are at risk based on activity patterns. That analysis requires reading across emails, meeting notes, support tickets, and CRM fields โ€” and then making a judgement call.

That's exactly what Claude Salesforce integration automates. Connect Claude to your Salesforce org via MCP, and your reps get deal intelligence, automated activity logging, AI-drafted outreach, and risk scoring โ€” all without switching tools or manually entering data. This guide covers the complete technical implementation: OAuth setup, MCP server architecture, the key use cases, and enterprise deployment patterns.

For organisations that want this deployed and maintained without building it internally, our Claude API Integration service handles the full Salesforce integration build. If you want to understand the architecture first, read on.

What Claude + Salesforce Actually Does

Before getting into implementation, it's worth being specific about what this integration enables. These are the four use cases that deliver measurable ROI in the first 90 days.

๐Ÿ“

Automatic Activity Logging

Claude reads meeting transcripts and emails, extracts the relevant CRM fields, and logs the activity against the correct Account and Opportunity โ€” without rep intervention.

๐ŸŽฏ

Deal Intelligence & Risk Scoring

Claude reads the full deal history โ€” notes, emails, stage changes, last activity date โ€” and produces a risk assessment with the specific signals driving it.

โœ‰๏ธ

AI-Drafted Outreach

Give Claude access to the Account record and recent activity, and it drafts a personalised follow-up email in the rep's voice โ€” ready to review and send in 30 seconds.

๐Ÿ”

Prospect Research Synthesis

Before a discovery call, Claude reads the Account record, recent news, and LinkedIn data via MCP tools and produces a 5-point briefing note โ€” pre-call in under 2 minutes.

Step 1: Salesforce Connected App Setup

Every Claude-Salesforce integration starts with a Connected App in your Salesforce org. This is what authorises Claude's MCP server to query and update your CRM data.

Creating the Connected App

In Salesforce, navigate to Setup โ†’ App Manager โ†’ New Connected App. Configure the following settings: enable OAuth, set the callback URL to your MCP server's auth endpoint (e.g., https://your-mcp-server.com/oauth/callback), and grant these OAuth scopes: api, refresh_token, offline_access. For server-to-server integration (non-interactive), use the JWT Bearer Flow instead โ€” this avoids per-user OAuth redirects and is the right choice for automated pipelines like meeting logging.

JWT Bearer Flow Setup

from simple_salesforce import Salesforce
import jwt, time, requests

def get_salesforce_token(
    consumer_key: str,
    private_key_path: str,
    salesforce_username: str,
    sandbox: bool = False
) -> str:
    """Get Salesforce access token via JWT Bearer Flow."""
    domain = "test.salesforce.com" if sandbox else "login.salesforce.com"

    # Build JWT
    with open(private_key_path, 'r') as f:
        private_key = f.read()

    claim = {
        "iss": consumer_key,
        "sub": salesforce_username,
        "aud": f"https://{domain}",
        "exp": int(time.time()) + 300
    }
    signed_jwt = jwt.encode(claim, private_key, algorithm="RS256")

    # Exchange for access token
    response = requests.post(
        f"https://{domain}/services/oauth2/token",
        data={
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "assertion": signed_jwt
        }
    )
    return response.json()["access_token"], response.json()["instance_url"]

# Connect to Salesforce
token, instance = get_salesforce_token(
    consumer_key=SF_CONSUMER_KEY,
    private_key_path="./salesforce_private.pem",
    salesforce_username=SF_USERNAME
)
sf = Salesforce(instance_url=instance, session_id=token)
Security note: Never use username/password authentication for server-to-server Salesforce integrations. The JWT Bearer Flow with a dedicated integration user (no login access, API-only permissions) is the correct approach for production systems.

Step 2: Building the Salesforce MCP Server

The MCP server is the layer that gives Claude structured access to Salesforce operations. Build it as a typed Python server exposing read and write tools for the Salesforce objects you need.

from mcp.server import Server
import mcp.types as types
from simple_salesforce import Salesforce

app = Server("salesforce-crm")
sf = connect_salesforce()  # Using JWT flow above

@app.list_tools()
async def handle_list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="search_accounts",
            description="Search Salesforce Accounts by name, industry, or ARR range",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "Account name or keyword"},
                    "industry": {"type": "string", "description": "Industry filter (optional)"},
                    "min_arr": {"type": "number", "description": "Minimum ARR in USD (optional)"}
                },
                "required": ["query"]
            }
        ),
        types.Tool(
            name="get_opportunity_detail",
            description="Get full opportunity detail including stage history and activities",
            inputSchema={
                "type": "object",
                "properties": {
                    "opportunity_id": {"type": "string"}
                },
                "required": ["opportunity_id"]
            }
        ),
        types.Tool(
            name="log_activity",
            description="Log a call, meeting, or email activity against a Salesforce record",
            inputSchema={
                "type": "object",
                "properties": {
                    "object_id": {"type": "string", "description": "Salesforce Account or Opportunity ID"},
                    "activity_type": {"type": "string", "enum": ["Call", "Meeting", "Email"]},
                    "subject": {"type": "string"},
                    "description": {"type": "string"},
                    "activity_date": {"type": "string", "description": "ISO 8601 date"}
                },
                "required": ["object_id", "activity_type", "subject", "description"]
            }
        ),
        types.Tool(
            name="update_opportunity_stage",
            description="Update the stage of a Salesforce Opportunity",
            inputSchema={
                "type": "object",
                "properties": {
                    "opportunity_id": {"type": "string"},
                    "new_stage": {"type": "string", "enum": [
                        "Prospecting", "Qualification", "Needs Analysis",
                        "Value Proposition", "Proposal/Price Quote",
                        "Negotiation/Review", "Closed Won", "Closed Lost"
                    ]},
                    "close_date": {"type": "string", "description": "Updated close date (ISO 8601)"}
                },
                "required": ["opportunity_id", "new_stage"]
            }
        )
    ]

@app.call_tool()
async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == "search_accounts":
        soql = f"SELECT Id, Name, Industry, AnnualRevenue, OwnerId FROM Account WHERE Name LIKE '%{arguments['query']}%'"
        if arguments.get("industry"):
            soql += f" AND Industry = '{arguments['industry']}'"
        results = sf.query(soql)
        return [types.TextContent(type="text", text=str(results["records"]))]

    elif name == "get_opportunity_detail":
        opp = sf.Opportunity.get(arguments["opportunity_id"])
        activities = sf.query(
            f"SELECT Id, Subject, Description, ActivityDate FROM Task "
            f"WHERE WhatId = '{arguments['opportunity_id']}' ORDER BY ActivityDate DESC LIMIT 20"
        )
        stage_history = sf.query(
            f"SELECT StageName, CreatedDate FROM OpportunityHistory "
            f"WHERE OpportunityId = '{arguments['opportunity_id']}' ORDER BY CreatedDate ASC"
        )
        return [types.TextContent(type="text", text=str({
            "opportunity": opp,
            "recent_activities": activities["records"],
            "stage_history": stage_history["records"]
        }))]

    elif name == "log_activity":
        task = sf.Task.create({
            "WhatId": arguments["object_id"],
            "Subject": arguments["subject"],
            "Description": arguments["description"],
            "Type": arguments["activity_type"],
            "Status": "Completed",
            "ActivityDate": arguments.get("activity_date", str(date.today()))
        })
        return [types.TextContent(type="text", text=f"Activity logged. Task ID: {task['id']}")]

    elif name == "update_opportunity_stage":
        update_data = {"StageName": arguments["new_stage"]}
        if arguments.get("close_date"):
            update_data["CloseDate"] = arguments["close_date"]
        sf.Opportunity.update(arguments["opportunity_id"], update_data)
        return [types.TextContent(type="text", text=f"Opportunity stage updated to {arguments['new_stage']}")]

For the complete MCP server setup and deployment instructions, see our MCP server Python tutorial. For connecting to additional data sources alongside Salesforce โ€” such as Jira, Slack, and your internal databases โ€” see our guide on MCP servers for Salesforce, Jira, and Slack.

Already have a Salesforce org to connect?

Our Claude API Integration service has delivered Salesforce + Claude integrations for enterprise sales teams. We handle the Connected App setup, MCP server build, and Claude workflow configuration โ€” production-ready in 3 weeks.

Book a Free Strategy Call โ†’

Step 3: Deal Intelligence Prompts

With the MCP server live, Claude can now answer complex questions about your pipeline. The real value comes from prompts that combine CRM data, activity history, and external context to produce actionable assessments.

Deal Risk Assessment

import anthropic

client = anthropic.Anthropic()

DEAL_INTELLIGENCE_SYSTEM = """You are an expert sales coach with deep knowledge of enterprise B2B sales.
Your job is to assess deal risk based on CRM data and activity patterns.

When assessing deals, look for these risk signals:
- No contact with champion or economic buyer in 14+ days
- Deal has been in same stage 3x longer than your average cycle
- Last activity was inbound from client (not outbound from rep) โ€” champion may be going cold
- No clear next step documented
- Competition mentioned in recent notes

Always output: RISK LEVEL (HIGH/MEDIUM/LOW), 3 specific risk signals from the data,
and 2 concrete recommended next actions with timeframes."""

def assess_deal_risk(opportunity_id: str) -> str:
    """Use Claude + MCP to assess deal risk."""
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        system=DEAL_INTELLIGENCE_SYSTEM,
        tools=[
            {
                "name": "get_opportunity_detail",
                "description": "Get full opportunity detail from Salesforce",
                "input_schema": {
                    "type": "object",
                    "properties": {"opportunity_id": {"type": "string"}},
                    "required": ["opportunity_id"]
                }
            }
        ],
        messages=[
            {
                "role": "user",
                "content": f"Assess the risk for opportunity {opportunity_id}. Pull the full detail and recent activity, then give me a risk assessment."
            }
        ]
    )
    return response.content[-1].text

Automated Activity Logging from Meeting Transcripts

This is the highest-volume automation: after every client meeting, Claude reads the transcript, identifies the relevant Salesforce Account and Opportunity, extracts the key discussion points and next steps, and logs the activity record. Your reps wake up the next morning with their Salesforce already updated. Combine this with the meeting summarisation pipeline for the complete workflow: transcript โ†’ summary โ†’ Salesforce activity log โ†’ Slack notification to rep.

async def log_meeting_to_salesforce(
    transcript: str,
    meeting_metadata: dict
) -> dict:
    """Auto-log a client meeting to Salesforce."""

    # First: use Claude to extract CRM-relevant data from transcript
    extraction_response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        system="""Extract CRM data from this meeting transcript. Return JSON with:
- account_name: company name of the client
- key_discussion_points: 3-5 bullet points of what was discussed
- next_steps: specific commitments made by either party
- competitor_mentions: any competitors mentioned
- deal_stage_signal: any signals about buying intent or urgency
- activity_subject: a 60-char meeting title for the CRM log""",
        messages=[{"role": "user", "content": transcript}]
    )

    import json
    crm_data = json.loads(extraction_response.content[0].text)

    # Then: use MCP to find the account and log the activity
    activity_description = f"""
Key Discussion: {chr(10).join(crm_data['key_discussion_points'])}

Next Steps: {crm_data['next_steps']}

Competitors Mentioned: {crm_data.get('competitor_mentions', 'None')}

Deal Signal: {crm_data.get('deal_stage_signal', 'No clear signal')}
"""

    # Search for the account in Salesforce
    accounts = sf.query(
        f"SELECT Id FROM Account WHERE Name LIKE '%{crm_data['account_name']}%' LIMIT 1"
    )
    if accounts["records"]:
        account_id = accounts["records"][0]["Id"]
        sf.Task.create({
            "WhatId": account_id,
            "Subject": crm_data["activity_subject"],
            "Description": activity_description,
            "Type": "Meeting",
            "Status": "Completed",
            "ActivityDate": meeting_metadata["date"]
        })
        return {"status": "logged", "account_id": account_id}
    return {"status": "account_not_found", "account_name": crm_data["account_name"]}

Step 4: AI-Drafted Outreach

Personalised outreach at scale is one of the highest-friction tasks in enterprise sales. Each message should reference the specific account context โ€” recent news, deal history, the specific problem you discussed โ€” but most reps write the same templated email because full personalisation takes too long.

With Claude reading the Account record, Claude produces a genuinely personalised draft in the rep's voice in under 3 seconds. The rep reads it, makes any changes, and sends. The key to doing this well is a strong system prompt that captures your outreach voice and firm guardrails on what not to include (competitor disparagement, pricing commitments, anything that requires legal review).

OUTREACH_SYSTEM = """You are drafting a sales follow-up email for {rep_name} at {company_name}.

Voice guidelines:
- Direct and specific โ€” reference the exact problem they mentioned
- Never say "leverage", "holistic", or "synergy"
- One clear ask per email โ€” not multiple questions
- Keep it under 150 words
- End with a specific proposed next step (e.g., "15-min call Thursday?" not "let me know your thoughts")

You will be given the Salesforce account record and last meeting notes.
Draft an email that builds on the specific context provided."""

def draft_follow_up_email(opportunity_id: str, rep_name: str) -> str:
    # Pull deal context via MCP / Salesforce API
    deal_data = get_opportunity_detail(opportunity_id)

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=512,
        system=OUTREACH_SYSTEM.format(rep_name=rep_name, company_name="ClaudeImplementations"),
        messages=[{
            "role": "user",
            "content": f"Draft a follow-up email based on this deal context:\n\n{deal_data}"
        }]
    )
    return response.content[0].text

Step 5: Claude Cowork + Salesforce

If your sales team uses Claude Cowork, the Salesforce MCP server connects directly to their desktop AI environment. Rather than running Claude integrations as background batch jobs, reps can interact with their CRM through natural language in Cowork: "Show me all deals over $100K that haven't had contact in two weeks" or "Draft a re-engagement email for the Accenture deal and log a task to send it Thursday."

This is fundamentally different from Salesforce Einstein โ€” instead of preset AI features within the CRM interface, your reps get a general-purpose AI assistant that can query Salesforce, read emails, access your internal knowledge base, and take actions across multiple systems in a single conversational turn. See our Cowork deployment service for enterprise rollout details and our case study on deploying Claude for a financial services sales team.

Step 6: Governance and Data Controls

Giving Claude write access to your Salesforce org carries real governance implications. Before deploying to a sales team, implement these controls.

Integration User with Scoped Permissions

Create a dedicated Salesforce integration user for Claude's MCP server. Apply a Permission Set that limits what the integration user can read and write. For a read-heavy use case (deal intelligence, outreach drafting), make the integration user read-only. Only grant write access for the specific objects and fields you've explicitly decided to automate โ€” Task creation yes, Opportunity deletion absolutely not.

Audit Logging

Log every Claude action that modifies Salesforce: which user triggered it, what data was read, what was written or updated, and the timestamp. Salesforce's Field Audit Trail handles on-object logging; supplement it with your own application log for the Claude decision layer. This is mandatory for financial services and legal organisations where audit trails are a compliance requirement.

Human-in-the-Loop for High-Stakes Actions

Stage changes on deals over $500K should require human confirmation before Claude executes them. Build a review queue: Claude proposes the action, routes it to the rep or manager for approval, and only then makes the Salesforce update. This is the right architecture for anything that affects forecast accuracy or revenue reporting. For a comprehensive governance framework, see our guide on Claude AI governance and our Claude Security & Governance service.

Common mistake: Deploying Claude with full Salesforce write access on day one. Start with read-only intelligence and logging, validate accuracy over 2โ€“4 weeks, then extend write access to low-risk operations. This prevents data quality issues from eroding sales team trust in the system.

Measuring ROI

Claude Salesforce integration has three measurable ROI vectors. First, time saved on CRM administration โ€” typical enterprise sales reps spend 20โ€“30% of their time on data entry and admin; automation to 5โ€“8% is achievable. Second, pipeline quality improvement โ€” when Claude flags at-risk deals weekly, conversion rates from late-stage opportunities improve because rep attention is focused on the right deals. Third, faster ramp time for new reps โ€” with deal intelligence and AI-drafted outreach, new reps achieve quota competency 4โ€“6 weeks faster because they can execute with the institutional knowledge of a senior rep from day one.

Before deploying, establish your baseline measurements: average CRM data entry time per rep per week, pipeline conversion rates by stage, and average ramp-to-quota timeline. Measure these again at 60 and 90 days post-deployment. For help designing the ROI measurement framework, see our guide on building the business case for Claude.

Ready to connect Claude to your Salesforce org? Our Claude API Integration team has delivered Salesforce integrations for enterprise sales organisations, including JWT setup, MCP server build, deal intelligence configuration, and the change management process for sales team adoption. Book a free strategy call to discuss your specific environment.

CI

ClaudeImplementation Team

Claude Certified Architects specialising in enterprise CRM integration, API development, and agentic sales automation. About us โ†’

Get Expert Help

Your Reps Should Be Selling, Not Doing Data Entry

Our Claude API Integration service delivers end-to-end Salesforce + Claude automation: activity logging, deal intelligence, AI outreach drafts, and risk scoring โ€” deployed in 3 weeks.