Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to Salesforce

Scalekit connector
Open markdown

The Salesforce connector lets your AI agent act in each user's Salesforce account. Each user signs in to Salesforce once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 74 tools.

Tools
74
What they doRead · write · destructive
35 · 28 · 1135 read28 write11 destructive
Users sign in with
OAuth app
Your own Salesforce app

What you can do

  • Read CRM records: retrieve accounts, contacts, leads, opportunities, and cases by ID or search query
  • Create and update records: open leads, close opportunities, update deal stages, and edit contacts
  • Log activities: create tasks and events linked to any CRM record
  • Run SOQL queries: execute arbitrary Salesforce Object Query Language queries for custom data retrieval
  • Search across objects: find records by name, email, phone, or any field value
  • Call the Metadata API: use SOAP proxy calls to inspect and modify Salesforce org metadata

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the Salesforce connection

    In AgentKit > Connections, create a Salesforce connection and copy its redirect URI. The name you give it is the connection_name your code passes. See Configure connections.

  4. Register an OAuth app

    Salesforce connections use your own OAuth app. Register one with Salesforce and add the redirect URI you copied.

    Then enter the app's Client ID and Client Secret on the Salesforce connection.

    Console steps with screenshots

    Register your Scalekit environment with the Salesforce connector using Salesforce’s External Client App (ECA) model. ECA is the recommended approach for distributing OAuth integrations to any Salesforce customer org. You need two orgs: a Partner Business Org for app creation and packaging, and a Developer Edition Org for namespace registration.

    1. Prepare your Salesforce orgs

      You need two orgs before starting:

      • Partner Business Org — sign up at the Salesforce Partner Community. This org acts as your Dev Hub and is where you create and package the External Client App.
      • Developer Edition Org — create one from your Partner Business Org. Use this org only to register a namespace.

      In your Partner Business Org, enable the Dev Hub:

      • Go to Setup > Quick Find: Dev Hub.
      • Enable Dev Hub and Unlocked Packages and Second-Generation Managed Packages.

      Dev Hub settings page in Partner Business Org with both options enabled

    2. Register a namespace

      Create the namespace in your Developer Edition Org

      • Log in to your Developer Edition Org and go to Setup > Apps > Packaging > Package Manager.
      • Click Edit next to the Namespace Prefix field.

      Package Manager page in Developer Edition Org showing the Namespace Prefix field with the Edit button

      • Enter your desired namespace identifier.
      • Click Check Availability to confirm the namespace is not already taken.
      • Click Review, then Save to finalize the namespace.

      Note the namespace string — you will add it to your project config in a later step.

      Namespace saved confirmation in Developer Edition Org Package Manager

      • Log in to your Partner Business Org and open the App Launcher.
      • Search for and open Namespace Registries.

      App Launcher search showing Namespace Registries result

      • Click Link Namespace.
      • When prompted, authenticate using the credentials of the Developer Edition Org where you created the namespace.
      • After logging in and granting access, your namespace appears in the registry list.

      Namespace Registries page showing the linked namespace in the list

    3. Copy the redirect URI from Scalekit

      • In the Scalekit dashboard, go to AgentKit > Connections > Create Connection.

      • Find Salesforce from the list of providers and click Create.

      • Copy the redirect URI. It looks like:

        https://<SCALEKIT_ENVIRONMENT_URL>/sso/v1/oauth/<CONNECTION_ID>/callback

      Scalekit dashboard — Salesforce connection page showing the redirect URI to copy

    4. Create the External Client App

      In your Partner Business Org, go to Setup > Platform Tools > Apps > External Client Apps and click New.

      Configure the app with the following settings:

      SettingValue
      Distribution StatePackaged
      API (OAuth)Enabled
      Callback URLRedirect URI copied from Scalekit
      OAuth ScopesAccess identity URL (id), manage user data via APIs (api), perform requests at any time (refresh_token)
      Authorization Code and Credentials FlowEnabled
      Require user credentials in POST body for Authorization Code FlowUnchecked
      Require Proof Key for Code Exchange (PKCE)Checked

      External Client App creation form in Salesforce with settings filled in

      Flow Enablement and Security sections showing Authorization Code Flow enabled and PKCE checked

      After saving, go to OAuth Settings > Consumer Key and Secret to retrieve your credentials:

      • Consumer Key
      • Consumer Secret — click Reveal to view and copy

      Consumer Key and Secret fields in External Client App OAuth Settings

    5. Package and distribute the app

      Use the Salesforce CLI to package the External Client App and generate an install URL. Complete these sub-steps in order.

      1. Install the Salesforce CLI

        Install the sf CLI from the Salesforce CLI setup guide, or via npm:

        Terminal window
        npm install -g @salesforce/cli

        Verify the installation:

        Terminal window
        sf --version
      2. Authenticate to your Dev Hub

        Log in to your Partner Business Org and set it as the default Dev Hub:

        Terminal window
        sf org login web --set-default-dev-hub --alias DevHub
      3. Create a DX project

        Terminal window
        sf project generate --name my-integration
        cd my-integration
      4. Set the namespace

        Open sfdx-project.json and add your namespace to the project definition:

        {
        "namespace": "<your-namespace>"
        }
      5. Retrieve the External Client App metadata

        Pull the ECA configuration from your Partner Business Org into the project:

        Terminal window
        sf project retrieve start --metadata ExternalClientApplication ExtlClntAppOauthSettings
      6. Create the managed package

        Run this once to create the package definition:

        Terminal window
        sf package create --name "Your App Display Name" --package-type Managed --path force-app
      7. Create a package version

        Terminal window
        sf package version create --package "Your App Display Name" --installation-key-bypass --wait 10

        Note the version ID (starts with 04t...) from the output.

      8. Promote the version to released

        Terminal window
        sf package version promote --package <VERSION_ID>
      9. Get the install URL

        Construct the install URL using the promoted version ID:

        https://login.salesforce.com/packaging/installPackage.apexp?p0=<VERSION_ID>
    6. Add credentials in Scalekit

      • In the Scalekit dashboard, go to AgentKit > Connections and open the connection you created.
      • Enter your credentials:
        • Client ID — Consumer Key from your External Client App
        • Client Secret — Consumer Secret from your External Client App
        • Package Install URL — the install URL from the previous step
        • Permissions — any additional OAuth scopes beyond the defaults (see Salesforce OAuth Scopes documentation)

      Scalekit dashboard — credentials form showing Client ID, Client Secret, and Package Install URL fields

      • Click Save.
    7. How users install the package

      A Salesforce admin must install the package in their org once before anyone in that org can connect. After the admin installs it, other users in the same org do not need to install it again — they can skip past the install screen.

      Scalekit always shows the Install package screen first when a user opens the magic link:

      • Admin (first time) — click Install to open the Salesforce package installation page and complete the installation.
      • Non-admin or subsequent users — click Copy link to send the install URL to the org’s Salesforce admin, or click I’ve installed the package directly if the admin has already installed it.

      Scalekit magic link — "Install package" screen showing the Install button, Copy link option, and the "I've installed the package" confirmation button

      After clicking Install, Salesforce opens the package installation page:

      Salesforce package installation page showing the install options

      Once installation is complete, return to the magic link and click I’ve installed the package. This takes the user to the Connect account screen where they complete the OAuth authorization:

      Scalekit magic link — Salesforce connector page showing the "Connect account" button to complete OAuth

  5. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'salesforce'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize Salesforce:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'salesforce_global_describe',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user signs in once. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • salesforce_account_getRetrieve details of a specific account from Salesforce by account ID.Read-only

    Get Account

    Retrieve details of a specific account from Salesforce by account ID. Returns account properties and associated data.

    Inputs

    account_idstringrequired
    ID of the account to retrieve
    fieldsstring
    Comma-separated list of fields to include in the response
  • salesforce_accounts_listRetrieve a list of accounts from Salesforce using a pre-built SOQL query.Read-only

    List Accounts

    Retrieve a list of accounts from Salesforce using a pre-built SOQL query. Returns basic account information.

    Inputs

    limitnumberrequired
    Number of results to return per page
  • salesforce_bulk_job_getGet the status and progress of a Bulk API 2.0 ingest job by ID — state (Open, UploadComplete, InProgress, JobComplete, Aborted, Failed), record counts, and object/operation metadata.Read-only

    Get Bulk Ingest Job

    Get the status and progress of a Bulk API 2.0 ingest job by ID — state (Open, UploadComplete, InProgress, JobComplete, Aborted, Failed), record counts, and object/operation metadata. Works for jobs created by any client (Data Loader, other integrations, or Salesforce Setup), not only ones created through this connector.

    Inputs

    job_idstringrequired
    ID of the Bulk API 2.0 ingest job to check.
  • salesforce_case_getRetrieve a Case record from Salesforce by ID.Read-only

    Get Case

    Retrieve a Case record from Salesforce by ID. Optionally specify which fields to return.

    Inputs

    case_idstringrequired
    ID of the case to retrieve
    fieldsstring
    Comma-separated list of fields to include in the response
  • salesforce_chatter_comments_listList all comments on a Salesforce Chatter post (feed element).Read-only

    List Chatter Comments

    List all comments on a Salesforce Chatter post (feed element).

    Inputs

    feed_element_idstringrequired
    The ID of the Chatter post to list comments for
    pagestring
    Page token for retrieving the next page of results
    page_sizenumber
    Number of comments to return per page (default: 25, max: 100)
  • salesforce_chatter_post_getRetrieve a specific Salesforce Chatter post (feed element) by its ID.Read-only

    Get Chatter Post

    Retrieve a specific Salesforce Chatter post (feed element) by its ID.

    Inputs

    feed_element_idstringrequired
    The ID of the Chatter feed element (post) to retrieve.
  • salesforce_chatter_user_feed_listRetrieve feed elements (posts) from a Salesforce user's Chatter news feed.Read-only

    List User Chatter Feed

    Retrieve feed elements (posts) from a Salesforce user's Chatter news feed. Use 'me' as the user ID to get the current user's feed.

    Inputs

    user_idstringrequired
    The ID of the user whose Chatter feed to retrieve. Use 'me' for the current user.
    pagestring
    Page token for retrieving the next page of results. Use the value from the previous response's nextPageToken.
    page_sizenumber
    Number of feed elements to return per page (default: 25, max: 100)
    sort_paramstring
    Sort order for feed elements. Options: LastModifiedDateDesc (default), CreatedDateDesc, MostRecentActivity

To rename a cloned dashboard, use a two-step pattern:

  1. Call salesforce_dashboard_clone with the source dashboard ID.
  2. Call salesforce_dashboard_update with the cloned dashboard ID and the new name.
{
"dashboard_id": "<cloned_dashboard_id>",
"name": "New dashboard name"
}

See the Salesforce Dashboard clone API for clone payload fields.

The Salesforce Metadata API is a SOAP-based API for reading and modifying your Salesforce org’s configuration, not its data. Use it to inspect or deploy custom objects, page layouts, validation rules, Apex classes, permission sets, profiles, and other org metadata.

Salesforce SOAP APIs only accept opaque access tokens, not JSON Web Token (JWT) access tokens. In your Salesforce Connected App, make sure Issue JSON Web Token (JWT)-based access tokens for named users is unchecked. If you disable this option after users have already authenticated, users must re-authenticate before SOAP proxy calls work.

Get the API version for the connected account

The Metadata API SOAP endpoint URL requires a version number. Retrieve the version from the connected account’s api_config.

15 collapsed lines
import os
import scalekit.client
from dotenv import load_dotenv
load_dotenv()
connection_name = "salesforce" # Connection name from the Scalekit dashboard
identifier = "6fe1c057-f684-4303-9555-3dd8807319b4" # Your user's identifier as registered in Scalekit
scalekit_client = scalekit.client.ScalekitClient(
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
)
actions = scalekit_client.actions
result = actions.get_connected_account(
connection_name=connection_name,
identifier=identifier,
)
raw_version = result.connected_account.api_config.get("version")
if not raw_version:
raise ValueError("Salesforce connected account is missing api_config.version")
api_version = raw_version.lstrip("v") # e.g. "66.0"
  1. Build the SOAP body

    Construct the SOAP envelope for the operation you want to call. Do not include a <SessionHeader> element. Scalekit injects the session header with the connected account’s access token.

    The soap_body string uses the api_version value from the previous section.

    soap_body = f"""<?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:met="http://soap.sforce.com/2006/04/metadata">
    <soapenv:Body>
    <met:describeMetadata>
    <met:asOfVersion>{api_version}</met:asOfVersion>
    </met:describeMetadata>
    </soapenv:Body>
    </soapenv:Envelope>"""
  2. Send the SOAP request through Scalekit

    Pass the SOAP body as raw_body. Set Content-Type to text/xml; charset=UTF-8 and SOAPAction to the operation name. Scalekit resolves the user’s Salesforce instance URL, so the request only needs the Metadata API path.

    try:
    response = actions.request(
    connection_name=connection_name,
    identifier=identifier,
    path=f"/services/Soap/m/{api_version}",
    method="POST",
    raw_body=soap_body,
    headers={
    "Content-Type": "text/xml; charset=UTF-8",
    "SOAPAction": "describeMetadata",
    },
    )
    except Exception as exc:
    raise RuntimeError("Salesforce Metadata API SOAP proxy request failed") from exc
    print(response.content)

Workflows

Make your first call

Once a user authorizes the connection, make a request to Salesforce through the Scalekit proxy. The example below retrieves the authenticated user’s profile — a useful sanity-check call.

const result = await actions.request({
connectionName: 'salesforce',
identifier: 'user_123',
method: 'GET',
path: '/chatter/users/me',
})
console.log(result)
Query records with SOQL

Retrieve this month’s open opportunities, sorted by deal size:

const result = await actions.request({
connectionName: 'salesforce',
identifier: 'user_123',
method: 'GET',
path: '/query',
params: {
q: 'SELECT Id, Name, Amount, StageName, CloseDate FROM Opportunity WHERE CloseDate = THIS_MONTH ORDER BY Amount DESC LIMIT 10',
},
})
console.log(result.records)
Log a sales call and advance a lead

Find a lead by email, update its status, then log a completed task — all in one agent turn.

This workflow uses Scalekit’s tool calling API (execute_tool), which takes the tool names listed under Tools on this page.

// 1. Search for the lead
const searchResult = await actions.request({
connectionName: 'salesforce',
identifier: 'user_123',
method: 'GET',
path: '/query',
params: { q: "SELECT Id, Status FROM Lead WHERE Email = 'jane@acme.com' LIMIT 1" },
})
const lead = searchResult.records[0]
// 2. Update the lead status
await actions.request({
connectionName: 'salesforce',
identifier: 'user_123',
method: 'PATCH',
path: `/sobjects/Lead/${lead.Id}`,
body: { Status: 'Working - Contacted' },
})
// 3. Log a completed task linked to the lead
await actions.request({
connectionName: 'salesforce',
identifier: 'user_123',
method: 'POST',
path: '/sobjects/Task',
body: {
WhoId: lead.Id,
Subject: 'Discovery call — follow up in 3 days',
Status: 'Completed',
ActivityDate: '2025-04-10',
},
})