> For the complete documentation index, see [llms.txt](https://docs.omnata.com/omnata-product-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.omnata.com/omnata-product-documentation/omnata-sync-for-snowflake/apps/salesforce.md).

# Salesforce

## Prerequisites

The Omnata Salesforce Plugin connects to the core Salesforce CRM APIs, aka Sales and Service Cloud, and does not require any add-on products on the Salesforce side to operate. It also supports industry clouds, such as Health Cloud, that are built on the core CRM but have slightly different object schemas.

## Authentication methods

### OAuth (Client Credentials)

Omnata does not provide a centralized OAuth app for all customers to use. Instead, you will create a External Client App within your Salesforce instance.

{% hint style="info" %}
Currently, the Authorization Code flow is not available due to incompatibilities with Snowflake OAuth secrets.
{% endhint %}

#### Create the External Client App

1. Navigate to the App Manager and click "New External Client App"\
   ![](/files/Feuj8QuKF54JoCnS53Z0)
2. Under Basic Information, use the following settings:
   1. Connected App Name: `Omnata Sync`
   2. Contact email: Chose any internal email\
      ![](/files/tYdTNX0qY1zALD5cvOGf)
3. Under API (Enable OAuth Settings), check Enable OAuth\
   ![](/files/qQ5yEKPl7YkQFuNqrx9t)
4. Under App Settings:
   1. Put `https://localhost` as the URL, this will not actually be used since we aren't using Authorization code flows
   2. Select OAuth Scopes:
      * Manage user data via APIs (api)
      * Perform requests at any time (refresh\_token, offline\_access)\
        ![](/files/8YAtdqHWibIxVx1V5MZC)
5. Under Flow Enablement, check Enable Client Credentials Flow\
   ![](/files/gVepkAHZUZQZHSbHf9Qb)
6. Under Security, uncheck Require Proof Key for Code Exchange (PKCE) Extension for Supported Authorization Flows:\
   ![](/files/cRmXKyd2agHQshzlO1yF)
7. Click Create:\
   ![](/files/8NBtSQcRM5Wx1opjPXPE)

{% hint style="info" %}
If you see this error in a Sandbox or Scratch org:\
\
**Failed to create an External Client App**\
\&#xNAN;**\[The Org Scoped External Client App must be in the format DeveloperOrganizationId:ExternalClientAppDeveloperName]**\
\
Then your environment may have been created during an awkward window in early 2026 where Salesforce had deprecated Connected Apps but not yet added support for External Client Apps.\
Orgs on Spring '26 or later should not experience this issue.
{% endhint %}

8. After the external client app has been created, click the Edit button under the policies tab:\
   ![](/files/82Hz7N4XmooWsgtHto8s)
9. Check Enable Client Credentials Flow and enter the username of the Salesforce user you wish to assume the privileges of from Snowflake. Also choose "Refresh token is valid until revoked" as the Refresh Token Policy:\
   ![](/files/BkRq3ixVRGR4jUXcM3kT)
10. Scroll down and click Save\
    ![](/files/SWnpDcDQ7MKb2aisaPw9)
11. Next, go to the Settings tab, expand OAuth Settings, and click the "Consumer Key and Secret" button:\
    ![](/files/hz0ITF6wCwwpSCm3DyR0)
12. You will be taken to Salesforce Classic in a new tab, and will be provided with the Consumer Key and Consumer Secret you need to provide in Omnata:\
    ![](/files/iHuJPfD14amUgVHUGOmC)
13. During the connection configuration process, you will also be asked for your Salesforce domain. You can find this under the "My Domain" section in Salesforce setup.

### Credentials

{% hint style="danger" %}
Salesforce deprecated Credential-based login in 2025 as described [here](https://help.salesforce.com/s/articleView?id=005132110\&type=1).

Omnata will continue to support existing Credential-based connections, but functionality will become increasingly limited and we strongly recommend migrating to OAuth.
{% endhint %}

Use your username, password, and security token to authenticate as a user.

If you don't know your security token, see the [Salesforce docs](https://help.salesforce.com/s/articleView?id=sf.user_security_token.htm\&type=5) for instructions on how to reset it.

## Inbound Syncs

Each Salesforce object you sync becomes a separate stream and lands as a separate table in Snowflake. The plugin uses the Salesforce **Bulk API** for most objects (with PK Chunking where supported) and falls back to the **REST API** for objects that don't work with bulk. See [How we use the Salesforce APIs](/omnata-product-documentation/omnata-sync-for-snowflake/apps/salesforce/how-we-use-the-salesforce-apis.md) for the full API-by-object breakdown.

#### Supported sync strategies

Omnata's Automatic mode will use incremental where possible, falling back to full-refresh if not. The cursor field is automatically determined by the plugin, in most cases for Salesforce it's the `SystemModstamp`

The specific sync strategies per object can be specified as such:

| Strategy              | When it applies                                                                                                        |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Incremental — Merge   | Pulls only modified records and merges them into the destination table on Salesforce `Id`. The most common choice.     |
| Incremental — Append  | Pulls only records modified since the last sync (using a cursor field) and appends them. Best for high-volume objects. |
| Full Refresh — Merge  | Replaces the destination table on every sync. Use for small reference objects or where history is not required.        |
| Full Refresh — Append | Appends a full snapshot of the object on every sync. Useful as a building block for SCD Type 2 modelling downstream.   |

### Supported streams

Available streams are discovered at sync-configuration time via Salesforce's global `describe()` call. The plugin filters the result down to objects that:

* Have the `queryable` property set to `true`, and
* Are accessible to the user the connection is authenticated as (i.e. inside the user's role + permission set + sharing rules).

See Salesforce permissions needed for the minimum set of permissions the connection's user needs.

**Supported Object types**

* **Standard Objects** — see the [Salesforce standard object reference](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_list.htm).
* **Custom Objects** (`*__c`) — all custom objects in the org.
* **Custom Metadata Types** (`*__mdt`).
* **History objects** (`*History`, `*__History`).
* **Share objects** (`*__Share`).
* **Feed objects** (`*__Feed`).
* Industry-cloud objects (Health Cloud, Financial Services Cloud, etc.) — exposed identically to standard objects.

**Unsupported Objects types**

The following are filtered out of the stream list because they can't be queried in a way that produces useful inbound rows:

* **Change Data Capture objects** — anything ending in `ChangeEvent` (these are real-time event streams, not queryable).
* **Platform Events** — anything ending in `__e`.
* **External Objects** — anything ending in `__x` (these resolve to data outside Salesforce).
* `PaymentIntentEvent` and certain other event-like objects.
* A small allowlist of objects that the Salesforce query API refuses or that only return useful results when fetched as nested fields on a parent object.

{% hint style="info" %}
If an object you expect to see is missing from the stream list, contact <support@omnata.com>
{% endhint %}

#### Inbound sync configuration

Each inbound sync exposes three optional parameters on the configuration form. These control inbound behaviour at the sync level — for connection-wide tuning levers (Bulk API batching, retry behaviour, etc.) see [Managing Performance](#managing-sync-performance).

<table><thead><tr><th>Parameter</th><th>Notes</th><th data-hidden>Type</th><th data-hidden>Default</th></tr></thead><tbody><tr><td>Calculate Formula Field values in Snowflake</td><td>Computes formula fields locally in Snowflake rather than fetching them from Salesforce. Requires the <code>CustomField</code> stream to be selected on the same sync (so the plugin has formula definitions available). Detailed in <a href="/pages/0BZ2PqoA90Wmlve1HFCR">Formula fields</a>.</td><td>checkbox</td><td>off</td></tr><tr><td>Incremental Sync Timestamp Offset</td><td>Seconds to subtract from the last-seen cursor value when requesting the next batch. Use this to catch records whose <code>SystemModstamp</code> was set at the start of a long-running Apex transaction but only committed after the previous sync finished. Cap at <strong>600 seconds</strong> (Salesforce's maximum Apex transaction length).</td><td>number</td><td><code>0</code> s</td></tr><tr><td>Apply rounding to currency/percent fields</td><td>Affects only the normalized Snowflake views the plugin creates. By default those views use <code>NUMERIC(38,19)</code> to preserve Salesforce's full 18-digit precision; enabling this rounds the values to the field's configured scale. Other numeric field types are not affected.</td><td>checkbox</td><td>off</td></tr></tbody></table>

### Delete handling

The plugin captures **soft deletes** automatically.&#x20;

Each record is staged in Snowflake with an `IS_DELETED` column that mirrors Salesforce's `IsDeleted` field. How this surfaces in your destination tables depends on the sync strategy:

**Limitations of soft-delete detection**

* **Salesforce retains soft-deleted records in the Recycle Bin for 15 days**, after which they are permanently purged. Records deleted more than 15 days before the next sync cannot be detected by this plugin and will silently disappear from incremental updates.
* **Some objects don't expose an `IsDeleted` field** (most system, metadata, and stats objects). For those, the `IS_DELETED` flag on every row will be `FALSE` regardless of Salesforce-side state. This is rare for business objects.
* **Hard-deleted records** (records deleted with the `Hard Delete` permission, bypassing the Recycle Bin) are never detectable — they leave no trace in any Salesforce API.

If you need stronger delete guarantees than 15-day Recycle Bin coverage, schedule incremental syncs to run at least every 14 days for any object where complete delete history matters.

## Outbound Syncs

### Sync Types

* Standard Objects
* Custom Objects
* Record Merge

### Sync Strategies

Standard and Custom Objects

* Upsert (requires an [External ID field](https://help.salesforce.com/s/articleView?id=000325076\&type=1) also marked as Unique)
* Update (requires the Salesforce 18 character system ID as an identifier)
* Mirror (requires an [External ID field](https://help.salesforce.com/s/articleView?id=000325076\&type=1) also marked as Unique)
* Create
* Delete

Record Merge

* Create - Calls the [merge() ](https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_merge.htm)operation to merge a source record (recordToMergeIds) into a target record (masterRecordId).
* Supports Lead, Contact, Account, Person Account, and Individual objects.

#### Outbound sync parameters

<table><thead><tr><th>Parameter</th><th>Notes</th><th data-hidden>Default</th></tr></thead><tbody><tr><td>Use Serial Mode</td><td>Processes batches one at a time instead of in parallel. Increases sync runtime but eliminates parallel-write contention entirely. Use this when reducing Batch Size and Batch Parallelism still doesn't avoid the contention errors.</td><td>off</td></tr></tbody></table>

## Managing sync performance

Every Salesforce instance responds differently to bulk extraction or ingestion, depending on the size of objects and other activity occurring in the instance. The plugin exposes tuning parameters separately for inbound and outbound syncs. Tuning parameters are set at sensible defaults, but can be changed on a live sync without triggering a full sync edit workflow.&#x20;

Additionally, sync parameters set sync behaviours in the sync configuration but are changed by editing the sync.&#x20;

### Tuning parameters

#### Inbound tuning parameters

<table><thead><tr><th>Parameter</th><th>Default</th><th>Notes</th><th data-hidden>Type</th><th data-hidden>Range</th></tr></thead><tbody><tr><td>Initial Bulk Query Limit</td><td><code>5,000,000</code></td><td>Maximum number of records to retrieve in a single Bulk API query. If Salesforce returns a resource-related error (e.g. <code>QUERY_TIMEOUT</code>, <code>EXCEEDED_ID_LIMIT</code>), the plugin automatically halves this value and retries. Only applies to objects that <strong>don't</strong> support PK Chunking — for PK-Chunked objects, Salesforce decides the chunk size.</td><td>text</td><td>positive integer</td></tr></tbody></table>

#### Outbound tuning parameters

Outbound syncs write to Salesforce in batches via the Bulk API and (where required) the Apex queue. The three sliders below control how those batches are sized, parallelised, and waited on.

<table><thead><tr><th>Parameter</th><th>Default</th><th>Range</th><th>Notes</th><th data-hidden>Type</th></tr></thead><tbody><tr><td>Batch Size</td><td><code>4,000</code></td><td>1 – 20,000</td><td>Records per batch. Salesforce processes each batch as a single transaction, so larger batches reduce overhead but increase the chance of trigger or workflow contention. <strong>Reduce this number</strong> if you're seeing <code>UNABLE_TO_LOCK_ROW</code> or similar contention errors in your sync logs.</td><td>slider</td></tr><tr><td>Batch Parallelism</td><td><code>4</code></td><td>1 – 16</td><td>Number of batches written to the Apex queue concurrently. Increase to speed up large syncs on Salesforce orgs with the capacity to handle parallel writes; decrease if you're hitting org-wide Apex governor limits.</td><td>slider</td></tr><tr><td>Batch processing wait minutes</td><td><code>60</code></td><td>1 – 120</td><td>How long the plugin waits for Apex processing to complete before timing out the batch. Increase if your Salesforce org has long-running validation rules, triggers, or workflows that occasionally exceed an hour per batch.</td><td>slider</td></tr></tbody></table>

## Functions

### SOQL\_QUERY

Executes a SOQL query and returns the results.

Parameters:

* CONNECTION\_SLUG (VARCHAR): The slug of the connection to query
* QUERY (VARCHAR): The SOQL query
* USE\_BULK\_API (BOOLEAN): Set to true to use the Salesforce Bulk API, recommended if you expect a large number of results.

Examples:

```sql
select RECORD
from table(OMNATA_SALESFORCE_PLUGIN.UDFS.SOQL_QUERY(
                'my-salesforce-connection',
                'select Id, Name from Account',
                true));
```

### FETCH\_SOBJECTS

Fetches a list of all objects in the Org by using [global describe](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_describeGlobal.htm).

Parameters:

* CONNECTION\_SLUG (VARCHAR): The slug of the connection to query

Examples:

```sql
select *
from table(OMNATA_SALESFORCE_PLUGIN.UDFS.FETCH_SOBJECTS(
                'my-salesforce-connection'));
```

### FETCH\_SOBJECT\_FIELDS

Fetches a list of all field metadata for a given object in the Org.

Parameters:

* CONNECTION\_SLUG (VARCHAR): The slug of the connection to query
* SOBJECT\_NAME: Type of object to search for

Examples:

```sql
select *
from table(OMNATA_SALESFORCE_PLUGIN.UDFS.FETCH_SOBJECT_FIELDS(
                'my-salesforce-connection','Account'));
```

### FETCH\_PICKLIST\_VALUES

Fetches all picklist values for all fields.

Parameters:

* CONNECTION\_SLUG (VARCHAR): The slug of the connection to query

Examples:

```sql
select *
from table(OMNATA_SALESFORCE_PLUGIN.UDFS.FETCH_PICKLIST_VALUES(
                'my-salesforce-connection'));
```

### LIST\_STANDARD\_ACTIONS

Lists all available standard Salesforce actions.

{% hint style="info" %}
Some actions return errors in some environments when attempting to query the full details. If you see NULL values next to certain actions, check the Snowflake event table for warning messages to see the API error.
{% endhint %}

Parameters:

* CONNECTION\_SLUG (VARCHAR): The slug of the connection to query

Examples:

```sql
select *
from table(OMNATA_SALESFORCE_PLUGIN.UDFS.LIST_STANDARD_ACTIONS(
                'my-salesforce-connection'));
```

### INVOKE\_STANDARD\_ACTION

Invokes standard Salesforce actions. The list of available actions and required inputs can be retrieved via LIST\_STANDARD\_ACTIONS

Parameters:

* CONNECTION\_SLUG (VARCHAR): The slug of the connection to query
* ACTION\_NAME (VARCHAR): The name of the action
* INPUTS (OBJECT): Inputs to the action

This function returns a variant due to the variety of possible return values the different actions can return.

Examples:

```sql
select OMNATA_SALESFORCE_PLUGIN.UDFS.INVOKE_STANDARD_ACTION(
    'my-salesforce-connection',
    'emailSimple',
    {
        'emailAddresses':'james.weakley@omnata.com',
        'emailSubject': 'Hello via Standard Action',
        'emailBody': 'This email was sent via a Snowflake UDF!'
    });

select OMNATA_SALESFORCE_PLUGIN.UDFS.INVOKE_STANDARD_ACTION(
    'my-salesforce-connection',
    'findMatchingIndividuals',
    {
        'searchTerm':'john',
        'searchObject': 'Contact',
        'searchFields': 'All'
    });
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.omnata.com/omnata-product-documentation/omnata-sync-for-snowflake/apps/salesforce.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
