Reference
Salesforce
Set up Salesforce OAuth for CRM and sales automation
Overview
Section titled “Overview”Connect your users to Salesforce for CRM data, opportunity management, contact records, and sales automation.
| Property | Value |
|---|---|
| Provider ID | salesforce |
| Category | CRM |
| PKCE | Supported (configurable; recommended) |
| Token refresh | Automatic |
| Redirect URI | Shown in Developer Portal |
Step 1: Create a Salesforce External Client App
Section titled “Step 1: Create a Salesforce External Client App”Log into your Salesforce org
Navigate to login.salesforce.com and sign in.
Open External Client App Manager
Go to Setup. In the Quick Find box, search for External Client App Manager and open it. Click New External Client App.
Enter basic information
Fill in:
- External Client App Name and API Name (auto-populated)
- Contact Email
- Distribution State — Local for single-org use, Packaged for distribution across orgs
Enable OAuth settings
Enable OAuth Settings and add the Alter callback URL from the Developer Portal to the Callback URL field. Select the OAuth scopes the application needs (e.g., api, and refresh_token to enable refresh tokens).
Configure OAuth policies
After creating the app, open the Policies tab and click Edit. In the OAuth Policies section, enable the OAuth flows the application will use (Authorization Code is required for the standard web flow) and save.
Get credentials
Open Settings → OAuth Settings to copy the Consumer Key (Client ID) and Consumer Secret (Client Secret). Salesforce may require email verification before revealing the secret.
Step 2: Add to Alter Vault
Section titled “Step 2: Add to Alter Vault”Open the Developer Portal
Go to portal.alterauth.com and navigate to the application.
Add Salesforce provider
Go to OAuth Providers > Add Provider > Salesforce.
Enter credentials
- Client ID: Paste your Salesforce Consumer Key
- Client Secret: Paste your Salesforce Consumer Secret
Select scopes
Choose the scopes the application needs.
Save
Click Save. The provider is now active.
Available Scopes
Section titled “Available Scopes”| Scope | Description |
|---|---|
api | Access to Salesforce REST API |
refresh_token | Obtain a refresh token for offline access |
full | Full access to all Salesforce data |
id | Access to user identity information |
chatter_api | Access to Chatter feeds and groups |
web | Access via a web browser session |
Additional scopes
Section titled “Additional scopes”| Scope | Description |
|---|---|
cdp_api | Access all Salesforce Data Cloud (CDP) APIs |
cdp_calculated_insight_api | Access the Data Cloud Calculated Insights API |
cdp_identityresolution_api | Access the Data Cloud Identity Resolution API |
cdp_ingest_api | Access the Data Cloud Ingestion API |
cdp_profile_api | Access the Data Cloud Profile API |
cdp_query_api | Access the Data Cloud Query API |
cdp_segment_api | Access the Data Cloud Segmentation API |
chatbot_api | Access Einstein Bots API services |
content | Access Salesforce CRM Content and Files resources |
custom_permissions | Return the custom permissions the user has in the connected app |
data_cloud_user_claims | Return Data Cloud user claims in the token response |
eclair_api | Access the Salesforce Analytics (Eclair) geodata API |
einstein_gpt_api | Access the Salesforce Einstein / models (Einstein GPT) API |
forgot_password | Access the headless Forgot Password API |
interaction_api | Access the Salesforce Interaction (flow orchestration) API |
lightning | Access Lightning Experience and Lightning apps |
mcp_api | Access the Salesforce MCP (Model Context Protocol) API |
pardot_api | Access the Marketing Cloud Account Engagement (Pardot) API |
pwdless_login_api | Access the headless Passwordless Login API |
scrt_api | Access the Salesforce Service Cloud real-time Messaging (SCRT) API |
sfap_api | Access the Einstein AI platform APIs (Models, Agent) |
user_registration_api | Access the headless User Registration API |
visualforce | Access customer-created Visualforce pages |
wave_api | Access the Salesforce CRM Analytics (Tableau CRM/Wave) REST API |
Org instance URL
Section titled “Org instance URL”Every Salesforce org has its own API host, such as https://acme.my.salesforce.com. Salesforce returns it as instance_url when the user authorizes; Alter checks that it is an https Salesforce host and returns it on the grant:
| Grant field (Python / TypeScript) | Value |
|---|---|
api_base_url / apiBaseUrl | The org’s instance URL, for example https://acme.my.salesforce.com |
Build REST URLs from it. Right after the Connect flow, the same value is on the returned ConnectResult as api_base_url / apiBaseUrl, so code that has just connected the org does not need to list grants; code that runs later reads it from list_grants() / listGrants(), as below. An Agent reads the same field from its own list_grants() / listGrants() rows. Operators see the same value as API host in the developer portal’s grant details and in the API HOST column of alter grants list.
page = await app.list_grants(end_user_token=end_user_token, provider_id="salesforce")grants = [g for g in page.grants if g.grant_kind == "oauth"]if not grants: raise RuntimeError("No Salesforce connection yet — run the Connect flow first.")
sf = grants[0]if sf.api_base_url is None: raise RuntimeError("Instance URL not captured — ask the user to reconnect.")
response = await app.request( "GET", f"{sf.api_base_url}/services/data/v59.0/limits", grant_id=sf.grant_id,)const page = await app.listGrants({ endUserToken, providerId: "salesforce" });const sf = page.grants.find((g) => g.grantKind === "oauth");if (!sf || sf.grantKind !== "oauth") { throw new Error("No Salesforce connection yet — run the Connect flow first.");}if (sf.apiBaseUrl === null) { throw new Error("Instance URL not captured — ask the user to reconnect.");}
const response = await app.request( "GET", `${sf.apiBaseUrl}/services/data/v59.0/limits`, { grantId: sf.grantId },);api_base_url is null for a connection made before Alter recorded the instance URL, or when Salesforce returned a URL that is not an https Salesforce host. Reconnecting from the wallet captures it again. There is no need to inspect tokens or configure the instance URL by hand.
- Always include
refresh_tokento enable long-lived access. - Salesforce uses org-specific API URLs. Alter returns the org’s instance URL on the grant as
api_base_url/apiBaseUrl(see Org instance URL). - See the Salesforce OAuth web server flow documentation for more details.
Policy-cataloged operations
Section titled “Policy-cataloged operations”Alter policy rules can target these attested operations and families for operation-level and parameter-aware controls.
| Operation ID | Families | Method | Provider path |
|---|---|---|---|
bulk/jobs/create | delete, write | POST | /services/data/v59.0/jobs/ingest |
bulk/jobs/get | read | GET | /services/data/v59.0/jobs/ingest/{jobId} |
composite | delete, read, write | POST | /services/data/v59.0/composite |
composite/batch | delete, read, write | POST | /services/data/v59.0/composite/batch |
composite/tree | write | POST | /services/data/v59.0/composite/tree/{sObject} |
limits/get | read | GET | /services/data/v59.0/limits |
oauth/userinfo | read | GET | /services/oauth2/userinfo |
query | read | GET | /services/data/v59.0/query |
query-all | read | GET | /services/data/v59.0/queryAll |
recent/list | read | GET | /services/data/v59.0/recent |
search | read | GET | /services/data/v59.0/search |
sobjects/create | write | POST | /services/data/v59.0/sobjects/{sObject} |
sobjects/delete | delete | DELETE | /services/data/v59.0/sobjects/{sObject}/{id} |
sobjects/describe | read | GET | /services/data/v59.0/sobjects/{sObject}/describe |
sobjects/get | read | GET | /services/data/v59.0/sobjects/{sObject}/{id} |
sobjects/list | read | GET | /services/data/v59.0/sobjects |
sobjects/update | write | PATCH | /services/data/v59.0/sobjects/{sObject}/{id} |
versions/list | read | GET | /services/data |