Skip to main content

Salesforce

Connect your Gravity Rail workspace to Salesforce CRM. The integration links workspace members to Salesforce Contacts and Leads, and gives your AI assistants the ability to query, create, and update Salesforce records during conversations.

This is useful when your team manages relationships in Salesforce and uses Gravity Rail for automated outreach, inbound scheduling, or AI-assisted conversations. Instead of switching between systems, your assistants can pull up a contact's record, log an interaction, or update a field -- all within the conversation flow.

What the integration does

Once connected, the Salesforce integration provides three capabilities:

  1. Member reconciliation -- Match workspace members to Salesforce Contacts or Leads by email or phone number. When a member is linked, their Salesforce record ID is stored so assistants and workflows can reference it.

  2. AI assistant tools -- Six tools become available to any AI assistant in the workspace. Assistants can run SOQL queries, search across records, look up a specific Contact or Account, create new records, and update existing ones. All from within a conversation.

  3. Schema introspection -- Assistants can describe any Salesforce object type at runtime, so they understand what fields are available before querying or updating records. This means they can work with custom objects and custom fields without hardcoded configuration.

The integration authenticates with OAuth 2.0 -- your choice of the server-to-server Client Credentials Flow (recommended) or the user-consent Authorization Code flow -- with encrypted token storage. One Salesforce org connects to one workspace.

Prerequisites

Before connecting, you need a Salesforce app (either a Connected App or an External Client App) configured for OAuth. If you don't have admin access to Salesforce, you'll need someone on your Salesforce team to set this up.

Salesforce edition

Any Salesforce edition with API access works -- Enterprise, Unlimited, Developer Edition, or a free Developer Edition org. If you're testing, a Developer Edition org is free and never expires.

Choose an auth method

The connection can authenticate with Salesforce two ways. You pick one with the Connection type radio in the credentials dialog; both use the same Client ID and Client Secret from your Salesforce app.

Server-to-server (recommended)User authorization
OAuth flowClient Credentials FlowAuthorization Code with PKCE
Who authorizesNobody -- tokens are minted directly from the stored credentialsA Salesforce user grants consent in a browser
Long-term stabilityNever needs re-authorization -- every API session mints a fresh tokenRelies on a refresh token, which org policies can expire or revoke
Extra Salesforce setupEnable the Client Credentials Flow on the app and assign a run-as userNone
Instance URLMust be your org's My Domain URL (e.g. https://myorg.my.salesforce.com)Any login URL, including https://login.salesforce.com

Use server-to-server unless you specifically need API calls attributed to the authorizing user, or your Salesforce admin can't enable the Client Credentials Flow on the app. Connections that authenticate as a user stop working when the refresh token is revoked (session policies, password resets, user deactivation) and must be manually re-authorized; server-to-server connections don't have that failure mode.

Create a Salesforce app

You need a Client ID (Consumer Key) and Client Secret (Consumer Secret) from a Salesforce app. There are two ways to create one -- both produce the same credentials.

External Client Apps are Salesforce's newer approach, available on orgs running Spring '24 or later. They separate developer settings from admin policies.

  1. In Salesforce Setup, search for App Manager and click New External Client App

  2. Fill in the basics:

    • Name: Gravity Rail
    • Contact Email: your admin email
    • Distribution State: Local
  3. Expand Enable OAuth and check Enable OAuth

  4. Set the Callback URL to the OAuth callback URL shown in step 1 of the guided setup on the Salesforce connection page in Gravity Rail (see Connection setup below)

  5. Add these OAuth Scopes:

    • api -- Manage user data via APIs
    • refresh_token, offline_access -- Perform requests at any time
    • openid -- Access unique user identifiers
    • id, profile, email, address, phone -- Access the identity URL service
  6. Select Require Secret for the Web Server Flow and Require Secret for Refresh Token Flow

  7. Click Save

  8. Note the Consumer Key (this is your Client ID) and Consumer Secret (this is your Client Secret)

  9. Under the app's Policies tab, set Permitted Users to Admin approved users are pre-authorized and assign the app to the relevant Profiles or Permission Sets

  10. For server-to-server connections: enable the Client Credentials Flow. External Client Apps have two separate "Enable Client Credentials Flow" checkboxes, and both must be on:

    • On the Settings tab, under the OAuth section, check Enable Client Credentials Flow (this exposes the flow on the app).

    • On the Policies tab, expand OAuth Policies → OAuth Flows and External Client App Enhancements, click Edit, and check Enable Client Credentials Flow there too. This per-org policy is what actually authorizes the grant, and checking it reveals the Run As field -- set it to an API-enabled user whose profile can access the objects the integration touches (Contacts, Leads, Tasks). All API calls run as this user.

      The Run As field matches on Salesforce username, which is not the same as the user's email address. On Developer Edition and sandbox orgs the username usually carries an org-specific suffix (e.g. jane.doe@example.com.mysandbox or dan+sf-canary.e18c505cd9b7@agentforce.com). Copy the exact Username from Setup → Users rather than typing the email.

    If only the Settings checkbox is on, Salesforce still rejects the grant with "Salesforce rejected the client-credentials grant" -- the Policies checkbox and Run As user are the missing piece.

External Client Apps may take a few minutes to become active after creation, and policy changes can take 2-10 minutes to propagate.

Option B: Connected App (works on all editions)

  1. In Salesforce Setup, search for App Manager and click New Connected App
  2. Fill in the basics:
    • Connected App Name: Gravity Rail
    • Contact Email: your admin email
  3. Under API (Enable OAuth Settings), check Enable OAuth Settings
  4. Set the Callback URL to the OAuth callback URL from Gravity Rail
  5. Add the same four OAuth scopes listed above
  6. Click Save, then Continue
  7. Note the Consumer Key and Consumer Secret
  8. For server-to-server connections: edit the app and check Enable Client Credentials Flow under the OAuth settings, then in Manage → Edit Policies, set the Run As user under Client Credentials Flow. As with External Client Apps, the Run As field matches on Salesforce username (not email) — copy the exact Username from Setup → Users, which on Developer/sandbox orgs carries an org-specific suffix.

Connected Apps are available to all org users by default and may take 2-10 minutes to become active.

Instance URL

You'll need the correct login URL for your Salesforce org:

EnvironmentInstance URL
Productionhttps://login.salesforce.com
Sandboxhttps://test.salesforce.com
Developer EditionYour org's login URL (e.g. https://myorg.my.salesforce.com)
Custom domainYour custom domain login URL

Using the wrong instance URL is the most common cause of invalid_client_id errors during connection.

Server-to-server connections: the generic hosts above won't work -- Salesforce only serves client-credentials grants on your org's My Domain host. Find it in Salesforce Setup under My Domain (it looks like https://yourorg.my.salesforce.com, or https://yourorg--sandboxname.sandbox.my.salesforce.com for sandboxes). The credentials dialog rejects the generic login hosts when server-to-server is selected.

Connection setup

Setup is guided. After you install Salesforce, the connection page walks you through the remaining steps and shows where you are in the process. You don't need any credentials to begin -- the OAuth callback URL you must register in Salesforce is shown in step 1 of the guide, before you enter anything. (There's no chicken-and-egg: the callback URL depends only on your workspace, not on the Salesforce app you haven't created yet.)

Step 1: Install Salesforce

In Apps & Connections (or Channels in the sidebar), find Salesforce and click Install. This creates the connection and opens its setup page with a Setup in progress badge and a three-step guide. If Salesforce isn't listed, the crm_salesforce feature flag may not be enabled on your workspace -- contact your Gravity Rail administrator.

Step 2: Register the callback URL in Salesforce

The guide's first step shows your OAuth Callback URL:

https://api.gravityrail.com/api/v2/w/{your-workspace-uuid}/app-connections/salesforce/oauth/callback

Copy it (there's a copy button) and set it as the Callback URL on your Salesforce Connected App or External Client App, along with the OAuth scopes listed in the same step (see Create a Salesforce app). The URL must match exactly, including the protocol and path. Salesforce then gives you a Consumer Key (your Client ID) and Consumer Secret (your Client Secret).

Step 3: Enter your credentials

Click Enter credentials in the guide's second step. In the dialog, enter:

  • Connection type -- Server-to-server (recommended) or User authorization (see Choose an auth method)
  • Client ID -- the Consumer Key from your Salesforce app
  • Client Secret -- the Consumer Secret from your Salesforce app
  • Instance URL -- the login URL for your Salesforce org (defaults to https://login.salesforce.com; server-to-server requires your My Domain URL instead)

Click Create to save. For server-to-server, Gravity Rail verifies the connection immediately -- if the credentials and app configuration are right, the status goes straight to Connected and you're done (skip step 4). For User authorization, the second step is checked off and the guide moves to authorization.

Step 4: Authorize with Salesforce (user authorization only)

Server-to-server connections are already Connected after step 3 -- this step doesn't apply to them.

Click Connect with Salesforce in the guide's third step. A new browser window opens to the Salesforce login page.

  1. Log in with a Salesforce user account that has API access
  2. Review the permissions and click Allow
  3. The window redirects back to Gravity Rail and closes

The connection status changes to Connected. Your Salesforce instance URL and API version appear on the Overview tab, where the callback URL and scopes remain available under Setup & Auth for future reference.

Set up from the command line (optional)

The same steps are available through the Gravity Rail CLI -- handy for scripted setup or when a workspace agent performs the configuration. Run gr salesforce install --help for the full sequence.

# 1. Create the connection. The response includes the OAuth "callbackUrl".
gr salesforce install -w <workspace-uuid> --allow-write

# 2. Register that callbackUrl + scopes on your Salesforce app, then save credentials.
# For a server-to-server connection, pass "authMethod":"client_credentials" with the
# org's My Domain URL — the connection is verified immediately and step 3 isn't needed.
gr salesforce configure -w <workspace-uuid> --allow-write \
-d '{"clientId":"...","clientSecret":"...","instanceUrl":"https://login.salesforce.com"}'

# 3. Authorize (user authorization only). Open the returned "authorizeUrl" in a browser.
gr salesforce authorize -w <workspace-uuid> --allow-write

# Check progress at any time (shows configured / connected + the callback URL).
gr salesforce status -w <workspace-uuid>

Connection states

StateBadgeMeaning
Setup in progressSecondaryInstalled, but credentials haven't been entered yet -- the guided setup is showing
Awaiting OAuthSecondaryCredentials are saved but OAuth authorization hasn't been completed yet
ConnectedGreenOAuth is complete and the API connection is active
Needs re-authorizationRedThe org revoked the connection's refresh token (user authorization) or rejected the client-credentials grant (server-to-server) -- see Troubleshooting
ErrorRedSomething went wrong (see Troubleshooting)
Not ConnectedSecondaryNo connection has been configured

Member reconciliation

Member reconciliation links your workspace members to their corresponding Salesforce Contacts or Leads. When a member is linked, their Salesforce record ID is stored as a member field (salesforce:id), which assistants can use to look up the right record during conversations.

How matching works

The system searches Salesforce for records that match each unlinked member by:

  1. Email (checked first, higher confidence) -- exact match on the member's email address against Contact and Lead records
  2. Phone (checked second) -- exact match on phone number against Contact and Lead records

Members without an email or phone number won't produce matches. The search checks Contacts first, then Leads, and returns the first match found.

Finding and linking matches

  1. Go to the Members tab on the Salesforce connection page
  2. Click Find Matching Records
  3. In the dialog, click Find Matching Salesforce Records to start the search
  4. Review the results -- each match card shows the workspace member on the left and the Salesforce record on the right, with the match type (email or phone) and object type (Contact or Lead)
  5. Click Link on individual matches to link them, or Link All to link every match at once

Managing linked members

The Members tab shows all currently linked members with their Salesforce record IDs. To remove a link, click Unlink next to the member. Unlinking removes the salesforce:id member field but does not delete or modify any Salesforce data.

Member fields

When a member is linked, the Salesforce connection manages these member fields:

Field KeyDescription
salesforce:idThe 18-character Salesforce record ID for the linked Contact or Lead
salesforce:account_idThe Salesforce Account ID associated with the record
salesforce:owner_idThe Salesforce Owner (user) ID assigned to the record

These fields are available in templates and can be read by AI assistants during conversations.

AI assistant tools

When the Salesforce connection is active, six tools are automatically available to all AI assistants in the workspace. These tools let assistants interact with Salesforce data during conversations without any additional configuration.

Available tools

ToolWhat it doesExample use
salesforce_queryRun a SOQL query and return matching recordsLook up all Contacts associated with an Account
salesforce_searchFull-text search across Contacts, Leads, Accounts, and OpportunitiesFind a record when you only know a name or partial information
salesforce_get_recordGet a specific record by object type and IDPull up a Contact's details using their salesforce:id
salesforce_create_recordCreate a new record in any object typeLog a new Lead after an inbound call
salesforce_update_recordUpdate fields on an existing recordMark a Contact's status or update a phone number
salesforce_describe_objectGet the schema for an object type, including all fields and picklist valuesUnderstand what fields exist on a custom object before querying it

Query safety

SOQL queries from assistants are capped at 200 rows per query. If an assistant writes a query with a higher limit (or no limit), the system enforces the cap automatically. SOQL is read-only by design -- assistants cannot delete records through queries.

Search terms are escaped to prevent injection. All query results may contain customer data and are never logged.

Using tools effectively

Assistants work best with Salesforce when they have context about your data model. Consider including guidance in your assistant's identity prompt, such as:

  • Which object types are relevant (e.g. "Our Salesforce org uses Contacts for patients and custom object Care_Plan__c for treatment plans")
  • Key fields to query or update (e.g. "Always check the Status__c field on Contact records")
  • What the salesforce:id member field means and when to use it

The salesforce_describe_object tool helps assistants discover fields at runtime, but providing context up front reduces unnecessary API calls and improves response quality.

Settings reference

Overview tab

FieldDescription
StatusCurrent connection state (Connected, Awaiting OAuth, Error, or Not Connected)
Instance URLYour Salesforce org's instance URL (e.g. https://na1.salesforce.com)
API VersionSalesforce REST API version in use (default: v59.0)
ErrorError message if the connection is in an error state

Setup Details (collapsible on Overview)

FieldDescription
OAuth Callback URLThe URL to register in your Salesforce app as the Callback URL
Required OAuth ScopesThe four scopes your Salesforce app must include
Instance URL guidanceWhich URL to use for production, sandbox, or developer orgs

Configuration dialog

FieldDescription
Connection typeServer-to-server (recommended) (Client Credentials Flow, never needs re-authorization) or User authorization (classic OAuth consent with a refresh token)
Client IDConsumer Key from your Salesforce Connected App or External Client App
Client SecretConsumer Secret from your Salesforce app
Instance URLLogin URL for your Salesforce org (server-to-server requires the org's My Domain URL)

Disconnecting and reconnecting

To disconnect: click Disconnect on the Overview tab. This clears the OAuth tokens but preserves your client credentials and all member links. The connection returns to the Awaiting OAuth state and you can reconnect by clicking Connect with Salesforce again.

Reconnecting a server-to-server connection never opens a browser window -- Reconnect (or Connect with Salesforce) mints a fresh token directly from the stored credentials and reports success or failure on the spot.

To reconfigure with different credentials: click Edit in the page header and enter new Client ID, Client Secret, and Instance URL values. User-authorization connections then need the OAuth consent flow again; server-to-server connections are verified as part of saving.

Troubleshooting

Connection stuck on "Awaiting OAuth"

The OAuth flow didn't complete. This can happen if the browser window was closed before Salesforce redirected back, or if the OAuth state expired (it's valid for 10 minutes). Click Connect with Salesforce to start a new OAuth flow.

redirect_uri_mismatch error during OAuth

The callback URL registered in your Salesforce app doesn't match the one Gravity Rail is using. Copy the OAuth Callback URL from the guided setup (step 1) or, once connected, from the Setup & Auth card on the Overview tab, and update your Salesforce app to use that exact URL. It must match character-for-character, including the protocol and any trailing characters.

invalid_client_id error

The Instance URL doesn't match your Salesforce org. Double-check which URL to use -- production orgs use https://login.salesforce.com, sandboxes use https://test.salesforce.com, and Developer Edition orgs often use a custom domain like https://myorg.my.salesforce.com. Update the Instance URL in the configuration dialog and try again.

Connection shows "Error" state

The access token may have been revoked. This happens when a Salesforce admin ends the user's session, the Connected App is deleted or deactivated, or the authorizing user's account is deactivated. Click Disconnect, then Connect with Salesforce to reauthorize. Server-to-server connections recover automatically on the next token mint unless the app configuration itself changed.

Server-to-server: "Salesforce rejected the client-credentials grant"

The Client Credentials Flow isn't fully enabled on your Salesforce app. The most common cause on External Client Apps is that only one of the two "Enable Client Credentials Flow" checkboxes is on -- the one on the Settings tab exposes the flow, but the one under Policies → OAuth Policies → OAuth Flows and External Client App Enhancements is what authorizes the grant, and it's where the Run As user is set. Open the app and confirm that (1) both Enable Client Credentials Flow checkboxes are on, (2) a Run As user is assigned under the Policies checkbox, and (3) the Client ID and Secret are correct. Policy changes take a few minutes to propagate; then click Reconnect (or re-save the credentials dialog) to retry.

Server-to-server: "requires a Salesforce-hosted My Domain instance URL"

https://login.salesforce.com and https://test.salesforce.com don't serve client-credentials grants, and non-Salesforce hosts are rejected outright. Update the Instance URL to your org's My Domain host (Salesforce Setup → My Domain), e.g. https://yourorg.my.salesforce.com.

Member reconciliation returns no matches

  • Verify that workspace members have email or phone data populated
  • Verify that matching Contacts or Leads exist in Salesforce with the same email or phone values
  • Confirm the Salesforce user who authorized the connection has permission to query Contact and Lead objects
  • For External Client Apps: confirm the user's profile has been granted access to the app

AI assistant tools not working

The tools are only available when the connection is in the Connected state with a valid access token. Check the Salesforce connection status on the Overview tab. If it shows an error, disconnect and reconnect.

"Salesforce API request failed" errors

The Salesforce REST API returned an error. Common causes include: the Connected App was deactivated, API rate limits were exceeded (Enterprise edition allows 15,000 API requests per day), or the authorizing user lost permissions. Check the Salesforce app status in Salesforce Setup.

Data Sync

The Salesforce integration supports bidirectional data sync using the same multi-entity sync framework as HubSpot. You can push member data to Salesforce records (outbound) and pull Salesforce record changes back into Gravity Rail (inbound).

Entity Types

Sync is configured per entity type. Each entity type has its own sync direction toggles, field mapping rules, match strategy, and optional filters. Salesforce currently supports the member entity type for syncing workspace members with Salesforce Contacts/Leads.

Configuring Sync

  1. Go to the Sync tab on the Salesforce connection page
  2. For each entity type, enable outbound sync, inbound sync, or both
  3. Configure sync rules that define how fields map between Gravity Rail and Salesforce
  4. Optionally set outbound filter or inbound filter CEL expressions to limit which entities are synced (e.g., source.email != '' to only sync members with an email)
  5. Click Save to apply

Sync rules use CEL expressions for flexible field transforms -- the same system used by HubSpot sync. See the HubSpot Sync guide for detailed documentation on sync rules, expressions, transform presets, and filters, which work identically for Salesforce.

Salesforce-Specific Settings

SettingDescription
Target ObjectWhich Salesforce object to sync to (default: Contact)
Upsert FieldExternal ID field for matching (default: Email)
Record TypeOptional RecordType ID for new records

These settings are stored in entity_params on the entity's sync config or in the Salesforce-specific SalesforceSyncSettings.

Running a Sync

Click Run Sync on the Sync tab to start a batch sync. Progress streams live to your browser via SSE. The sync uses the same Temporal workflow as HubSpot, with batched member processing and automatic checkpoint tracking per entity type.

Each member row shows the outcome per direction (outbound ↑ / inbound ↓): Synced, Skipped, or Failed. A failed row's detail names the reason as a stable code. For a remote rejection it also appends Salesforce's own error code — e.g. remote_api_error:DUPLICATES_DETECTED or remote_api_error:REQUIRED_FIELD_MISSING — so you can see why Salesforce refused the write without exposing any record content. Common ones:

Salesforce error codeWhat it meansFix
DUPLICATES_DETECTEDAn active Salesforce duplicate rule blocked creating a Contact/Lead that resembles an existing recordAdjust or deactivate the duplicate rule in Salesforce Setup → Duplicate Rules, or link the member to the existing record on the Members tab
REQUIRED_FIELD_MISSING / FIELD_INTEGRITY_EXCEPTIONA field Salesforce requires on the target object wasn't provided by the sync mappingAdd the missing field to the outbound mapping, or make it non-required in Salesforce
FIELD_CUSTOM_VALIDATION_EXCEPTIONA Salesforce validation rule rejected the valueReview the validation rule on the target object
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITYThe connection's user (or the server-to-server Run As user) lacks permission on the objectGrant the user create/update access to the target object

Automatic Sync

When outbound sync is enabled for an entity type, member updates automatically trigger a sync for the changed member via managed event rules.

Chat-Triggered Tasks

The Chat-Triggered Tasks feature automatically logs each chat conversation as a completed Salesforce Task on the member's linked Contact or Lead record. This gives your Salesforce team a full activity history without any manual data entry.

Enabling Chat-Triggered Tasks

  1. Go to the Sync tab on the Salesforce connection page
  2. Toggle on Chat-Triggered Tasks
  3. Click Save

Gravity Rail creates a managed event rule that fires whenever a new chat is created. Members without a linked Salesforce record are skipped automatically.

What it does

Each chat creates a Salesforce Task with:

  • Status: Completed (the conversation already happened)
  • WhoId: the Salesforce Contact or Lead linked to the member
  • Subject and body: rendered from configurable templates using chat variables

The Task appears in the Salesforce activity timeline for the linked Contact or Lead, the same as if someone had used Salesforce's "Log a Call" action.

Template configuration

The subject and body of the Salesforce Task are rendered from templates. Available variables include:

VariableDescription
chat.urlDirect link to the conversation in Gravity Rail
chat.created_atTimestamp when the chat started
workflow.nameName of the workflow the member was in (if any)

You can customize these templates on the Sync tab under Chat-Triggered Tasks.

Filter options

Two optional filters let you control which chats trigger a Task:

FilterDescription
Exclude test chatsSkip chats created through test or staging workflows
Workflow filterOnly log chats from specific workflows (leave blank for all workflows)

Current limitations

  • One Salesforce org per workspace -- Each workspace supports a single Salesforce connection. If you need to connect multiple orgs, use separate workspaces.
  • Query row limits -- AI assistant SOQL queries are capped at 200 rows per query. For larger data sets, use more specific WHERE clauses or paginate with OFFSET.
  • Inbound polling interval -- Inbound sync polls Salesforce via SOQL queries rather than real-time streaming. Change Data Capture support is planned.
  • Channels -- Overview of all communication channels and integrations
  • Members -- Understanding workspace members and member fields
  • Workflows -- Build the workflows that use Salesforce data