> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rippit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Salesforce data integration

> Technical reference for the Rippit Salesforce data integration: authorization, clouds, object selection, what a case becomes in Rippit, API endpoints, sync behavior, and rate limiting.

A read-only OAuth integration that syncs Salesforce cases, and any other Salesforce objects you select, into Rippit on a schedule. Salesforce ingestion is available for PLG and hybrid workspaces. For what applies to every integration, see [how data integrations work](https://docs.rippit.com/plg/data-integrations.md). For the security model, see [Data integration security](https://docs.rippit.com/security/data-integrations.md).

This page covers importing Salesforce data into Rippit tables. To let the Rippit agent query Salesforce live instead, see the [Salesforce MCP server](https://docs.rippit.com/agentic-chat/connectors/salesforce.md).

## Connection summary

| | |
| - | - |
| Authorization | OAuth 2.0 authorization code grant with PKCE (`S256`) |
| Writes to Salesforce | None to your data. The only non-`GET` calls are the OAuth token exchange and token refresh |
| Credentials shared with Rippit | None. No password, no security token. Rippit stores the OAuth access and refresh tokens Salesforce issues |
| Required Salesforce access | Rippit asks for a Salesforce Admin to authorize. See [Before you connect](#before-you-connect) for what is checked |
| Connection identity | Your Salesforce organization ID. One connection per org |
| Required Rippit role | Workspace admin |
| API base | Your org's instance URL, under `/services/data/v58.0` |
| Sync method | Scheduled polling, incremental per object |
| Attachments | Downloaded and stored by Rippit |
| Maximum historical backfill | For Case and its child objects: 90 days of case updates by default, or 1 year. All time when the whole history holds up to 100,000 cases. Larger orgs choose up to the most recent days of case updates that hold 100,000 cases. Every other object imports its full history |
| Selection limits | 150 Case fields at setup, plus Rippit's standard columns. 50 objects per import |

## Before you connect

**Choose your clouds.** Enable Service Cloud, Sales Cloud, or both. Service Cloud is for cases: it preselects Case in the object picker. Sales Cloud is for opportunities: it preselects Opportunity. Each one also decides which access check runs when you connect. Rippit will not start authorization with neither selected.

**Authorize as a Salesforce Admin.** If you are not an Admin, Rippit creates a link you can share with one, and they complete the connection for you. Rippit can read only what the authorizing user can read, so use a dedicated integration user where you can. Two checks run against that user:

* When you connect, the user must be able to describe `Case` if Service Cloud is enabled, and `Opportunity` if Sales Cloud is enabled. If either fails, no connection is created.
* Connection checks pass when the user holds a permission set with **View All Data**, or can read every object Rippit collects.

**Sandbox orgs** authorize through `test.salesforce.com` instead of `login.salesforce.com`. The sandbox option appears only when Rippit has turned it on for your workspace.

## Choosing objects

After connecting, you pick the Salesforce objects to import from the list of objects your org reports as queryable, with the objects for your clouds preselected. A few objects that cannot be read by time window, such as `ContentDocumentLink` and `Vote`, are not offered. You can add up to 50 at a time, and come back later to add more.

| Object | What it becomes | History imported |
| - | - | - |
| Case | Conversations in Rippit, with the content described below | The import window you choose |
| A child object of Case (any object with a lookup to Case) | Its own table | The import window you choose |
| Any other object | Its own table | Its full history, whatever window you choose |

Object tables carry every field on the object. Rippit reads the child objects of Case from your org's Case describe. If that describe fails, every selected object uses the import window, so a temporary error cannot start an unbounded import.

## Data handling

| Question | Answer |
| - | - |
| Is case content ingested, or only metadata? | Full content: the case, its emails, case comments, feed posts, messaging transcripts, and files |
| Are internal comments ingested? | Yes. Each message keeps the visibility Salesforce gives it |
| Are attachments stored? | Yes, downloaded and stored rather than linked |
| Can Rippit modify Salesforce? | No. The integration only reads |
| Is data used to train models? | No. Rippit does not train any AI models. Your data is used for inference only |

### What a case becomes in Rippit

Each Salesforce case becomes one conversation in Rippit.

| Rippit record | Content |
| - | - |
| Subject | The case `Subject` |
| Status | The case `Status` |
| Start and end | The case `CreatedDate` and `LastModifiedDate` |
| Customer | The case contact (`ContactId`) |
| Default selected fields | Status, subject, case number, case record type, last assignee, and all assignees |
| Other fields | Any other Case field you select. Case is read with every field, so custom fields are available |
| Emails | Each `EmailMessage` on the case. An incoming email is attributed to the sender's name, an outgoing one to the user who created it. Visibility follows `IsExternallyVisible` |
| Case comments | Each `CaseComment`, attributed to the user who created it. Public when `IsPublished` is true |
| Feed posts | Each `CaseFeed` post with a body. Public when its visibility is `AllUsers` |
| Messaging transcripts | The entries of each messaging session linked to the case, read through the Connect API when your org and the authorizing user have access to it |
| Attachments | `Attachment` records, files linked through `ContentDocumentLink`, files shared in messaging sessions, and inline images in emails and case comments |
| People | Salesforce users as agents, leaving out customer portal user types. Contacts referenced by a case or task as customers |

Last assignee is the case owner. All assignees adds every user the case history shows as a past or present owner.

## API endpoints

Data requests go to your org's instance URL with the OAuth access token as a bearer token. Paths below are under `/services/data/v58.0` unless shown in full.

### Authentication

| Purpose | Endpoint | Method |
| - | - | - |
| Authorize | `https://login.salesforce.com/services/oauth2/authorize` | Browser redirect |
| Token exchange and token refresh | `https://login.salesforce.com/services/oauth2/token` | POST |
| Permission check when you connect | `/services/data/v39.0/sobjects/Case/describe`, `/services/data/v39.0/sobjects/Opportunity/describe` | GET |
| Connection check: the authorizing user | `https://login.salesforce.com/services/oauth2/userinfo` | GET |
| Connection check: that user's permissions | `/query?q=` on `PermissionSetAssignment`, `PermissionSet`, and `ObjectPermissions` | GET |
| Connection check, fallback: one record of each collected object | `/query?q=SELECT Id FROM <object> ... LIMIT 1` | GET |

Where Salesforce rotates refresh tokens, a token presented twice locks the connection out. Rippit refreshes under a lock so that two workers never present the same one.

### Cases and objects

Everything is read with SOQL through the query endpoint.

| Object | Endpoint | Method |
| - | - | - |
| Records changed in a time window, per object | `/query?q=SELECT <fields> FROM <object> WHERE <timestamp> >= <from> AND <timestamp> < <to>` | GET |
| Records by ID, up to 100 per request | `/query?q=SELECT <fields> FROM <object> WHERE Id IN (...)` | GET |
| Objects read this way for cases | `Case`, `CaseComment`, `EmailMessage`, `CaseFeed`, `CaseHistory`, `Task`, `Attachment`, `MessagingSession`, `RecordType`, `User`, `Contact` | GET |
| Files linked to a record | `/query?q=SELECT <fields> FROM ContentDocumentLink WHERE LinkedEntityId IN (...)` | GET |
| Messaging transcript entries | `/connect/conversation/<conversation identifier>/entries?recordLimit=1000` | GET |
| File content | `/sobjects/ContentVersion/<id>/VersionData`, or the `Body` URL on an `Attachment` | GET |
| Object list, for the object picker | `/sobjects` | GET |
| Field list for an object | `/sobjects/<object>/describe` | GET |
| Backfill sizing for cases | `/query?q=SELECT COUNT() FROM Case WHERE SystemModstamp >= <date>` and the earliest case by `CreatedDate` | GET |
| Sizing for a full-history object | `/query?q=SELECT MIN(<timestamp>), COUNT(<timestamp>) FROM <object>` | GET |

SOQL has no `SELECT *`, so Rippit describes each object and names its fields in the query.

Contacts are not swept. Rippit fetches the contacts that cases and tasks reference, by ID. Selecting Contact as an object turns on a sweep of all contacts.

## Sync behavior

| | |
| - | - |
| Discovery | Per object, by time window on `SystemModstamp`. `CaseHistory` and `EmailMessage` use `CreatedDate`. An object without `SystemModstamp` uses `LastModifiedDate`, then `CreatedDate` |
| Window size | Up to 100,000 records. A window holding more is split and read again |
| Trailing buffer | Each sync closes 500 seconds behind the current time |
| Initial lookback | Case and its child objects: the backfill chosen at setup. Other objects: from their earliest record |
| Refetch by ID | 100 records per request, 5 concurrent |
| Messaging transcripts | Fetched per conversation when a messaging session linked to a case changes, 1000 entries per page |
| Attachments | Queued when the record that carries them syncs, then downloaded and stored |
| Update handling | A changed record falls in a later window and is read again |
| Resumption | A high water mark per object. Changed records only, no full rescan |

Rippit selects cases by last update, not creation. A case created before the backfill window but updated inside it is synced with its original created time.

## Rate limiting

Salesforce counts API requests against your org's daily allocation and reports usage on every response in the `Sforce-Limit-Info` header. Rippit reads that header and slows down as usage climbs: the larger the share of the allocation already used, the more often it pauses between requests, and the pause scales with how long the previous request took. It does not reserve a fixed share for other apps.

Full-history imports of large objects are where most requests go. Select only the objects you need.

## Removing the connection

**Remove** stops syncing and deletes the tokens Rippit stored. It does not delete conversations or tables already imported, and it does not call Salesforce. To end the grant on the Salesforce side as well, revoke Rippit's access in your Salesforce OAuth settings.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.