For the complete documentation index, see llms.txt. This page is also available as Markdown.

Salesforce

Access configuration details to connect Soda to a Salesforce data source.

Soda works with Salesforce in two roles. You can verify a contract against a Salesforce object, and you can name a Salesforce dataset as the source of a reconciliation contract to compare warehouse data against the Salesforce records it was loaded from.

Salesforce answers SOQL rather than SQL, so support in both roles is partial. A subset of contract checks and reconciliation checks cannot run against it, and the values it returns are compared with a tolerance. A check Soda cannot run reports NOT_EVALUATED naming the reason, never a pass it did not measure. See the caveats and limitations of this connection.

To request access to the Salesforce data source for your organization, contact support@soda.io with your organization identifier.

Connection configuration reference

Install the following package:

pip install soda-salesforce

Contract verification against Salesforce needs soda-salesforce 4.22.0 or later. Earlier versions support reconciliation only.

Create the config file:

soda data-source create -f ds_config.yml

The data source configuration YAML should look like the following:

ds_config.yml
type: salesforce
name: my_salesforce
connection:
  authentication: client_credentials
  domain: https://acme.my.salesforce.com
  consumer_key: ${env.SF_CONSUMER_KEY}
  consumer_secret: ${env.SF_CONSUMER_SECRET}
  api_version: "60.0"
  timeout_seconds: 120

Connection properties

Property
Required
Type
Default
Description

type

yes

string

Identify the type of data source for Soda. In this case, must be salesforce.

authentication

no

string

client_credentials

The authentication flow. client_credentials is the only supported value.

domain

yes

string

The My Domain host of your Salesforce org, for example acme.my.salesforce.com. Soda adds https:// when you omit it and tolerates a trailing slash. An explicit http:// is rejected, because the token request carries the consumer secret.

consumer_key

yes

string

The consumer key of the Connected App. Consider using an environment variable to retrieve this value securely, for example ${env.SF_CONSUMER_KEY}.

consumer_secret

yes

string

The consumer secret of the Connected App. Consider using an environment variable to retrieve this value securely, for example ${env.SF_CONSUMER_SECRET}.

api_version

no

string

60.0

The Salesforce REST API version to pin, in major.minor form. A value with no minor component, such as 60, is rejected. Quote the value so YAML reads it as a string.

timeout_seconds

no

integer

300

The request timeout in seconds.

Salesforce prerequisites

Provision a Connected App with the OAuth 2.0 client credentials flow enabled. No other flow is supported.

Designate a Run As user for the Connected App, with API access and read permission on the objects and fields you want to read. Every query Soda issues runs as that user, so the Run As user's object-level and field-level security bounds what Soda can read. Grant nothing beyond read.

Soda reads and never writes. Against your org it issues SOQL SELECT queries and metadata calls, nothing else.

Connection test

Test the data source connection:

Object and field names

Refer to objects and fields by their Salesforce API names, in the casing Salesforce reports.

  • A dataset is <data_source_name>/<ObjectApiName>. Standard objects carry no suffix, as in my_salesforce/Account; custom objects end in __c, as in my_salesforce/Invoice__c.

  • A column is a field API name. Standard fields are PascalCase, such as Id, Name or CreatedDate; custom fields end in __c, such as Region__c.

  • Id is the natural key of every object. It is always present, always sortable, and always 18 characters.

To find an object's field API names, go to Setup > Object Manager in Salesforce.

Contract verification support

Write the contract against the Salesforce dataset and verify it the same way you verify any other contract. There is no Salesforce-specific contract syntax.

Supported contract checks

Check
Salesforce
Notes

row_count

Supported

schema

Supported

Set allow_extra_columns and allow_other_column_order. See Schema checks.

missing

Supported

Both missing_count and missing_percent.

invalid

Partly supported

valid_values, invalid_values, valid_min and valid_max are supported. Regex, format, length and valid_reference_data are not.

duplicate

Column level only

A dataset-level duplicate check identifies rows by a row hash, which SOQL cannot express.

aggregate

Partly supported

sum, min, max and avg are supported. The length aggregates are not.

freshness

Supported

metric

Supported

Both the expression form and query. Write the query in SOQL.

failed_rows

Supported

expression, query and keys_query all evaluate. Write query and keys_query in SOQL.

group_by

Not supported

The check runs a grouped query the org cannot answer.

Writing checks against Salesforce

SOQL is strict about literals where SQL is not. A quoted value against a numeric field and a bare number against a text field are each rejected outright, where a warehouse would cast silently. Soda reads the object's metadata and writes each literal the way the field's type requires, so you can use whichever YAML form is natural:

A Date field and a DateTime field take different literals, and SOQL accepts neither in the other's place. A Date field takes 2026-01-01; a DateTime field takes 2026-01-01T00:00:00Z and must carry a timezone. Writing a bare date against a DateTime field reports NOT_EVALUATED naming the form the field needs, rather than assuming a time and a zone on your behalf.

Filters become a SOQL WHERE clause, so they must use predicates SOQL can express on field API names: the comparison operators, AND, OR, LIKE, IN over literals, and IS NULL.

A metric or failed_rows check written with query sends your query to the org exactly as you wrote it. Write it in SOQL, and name the object rather than the Soda dataset:

A filter on a metric check does not narrow what the metric measures, on Salesforce or on any other data source. Put the condition in the expression or the query.

Schema checks

A schema check compares names, missing columns, type names, string length, and numeric precision and scale. Three things about Salesforce metadata shape how you write one:

  • Salesforce reports the standard fields on every object, among them Id, Name, CreatedDate, IsDeleted and SystemModstamp. Either declare them or set allow_extra_columns: true.

  • Column order is Salesforce's, not the contract's, so set allow_other_column_order: true.

  • Every Number field reports as a double whatever its precision and scale, and no field reports a datetime precision. Declaring datetime_precision is rejected when the contract is parsed.

Checks that cannot run

A check Soda cannot run against Salesforce reports NOT_EVALUATED with the reason, never a result it did not measure. Where the refusal happens decides what else it costs.

  • Refused when the contract is parsed: a check type Salesforce does not support, and a dataset-level duplicate. No query is built and the rest of the contract runs normally.

  • Refused when the query runs: a regex, a format, or a length validity rule. Soda batches aggregation checks into shared queries, so the refusal fails the whole batch and every check batched with it reports NOT_EVALUATED, not only the offending one. Keep these rules out of a contract you want partial results from.

SOQL has no length function, so the length aggregates min_length, max_length and avg_length cannot be measured either. Leave them out of a Salesforce contract.

Failing rows

Soda stores the rows behind a failing check in a Diagnostics Warehouse. Salesforce cannot host one, because that needs write access to the org, so point the warehouse at a separate Postgres or Databricks data source and Soda extracts the rows across.

Rows are keyed by the Salesforce record Id rather than by a hash of the check's columns.

Two checks evaluate normally but have no rows to store:

  • A column-level duplicate check. Identifying which records duplicated needs a grouped query joined back to the source rows, which SOQL cannot express in a single statement.

  • A failed_rows check written with keys_query. Soda wraps the query in a common table expression, which SOQL has no form for.

A failed_rows check written with query stores the result of your query in its own table, where every column is typed as text.

Without a Diagnostics Warehouse, a failing check on Salesforce reports its count and no rows.

Reconciliation support

Define the contract on the warehouse dataset and name the Salesforce dataset as the source. Reconciliation runs through the same engine it uses for every other data source. There is no Salesforce-specific check logic.

For the syntax, see the reconciliation checks reference.

Supported checks

Check
Salesforce
Notes

row_count_diff

Supported

COUNT is pushed down to SOQL.

aggregate_diff with sum, min, max

Supported

Values compare exactly.

aggregate_diff with avg

Supported

Set a tolerance threshold. See Comparison tolerance.

metric_diff

Supported

Both the expression form and source_query.

freshness_diff

Supported

rows_diff

Supported

Requires explicit source_columns and target_columns.

reference_diff

Supported

Requires explicit columns on both sides.

duplicate_diff

Not supported

SOQL has no per-row hash.

Key columns and ordering

Row-level reconciliation reads each side ordered by key, then merges the two streams. The key columns have to be orderable in SOQL, which constrains what you can key on.

Ordering is ascending only. Soda rejects a descending order. SOQL also cannot order by a long text field, so Soda validates the key columns before it opens a cursor and names any column it cannot use, rather than failing part way through a read.

SOQL sorts NULL first. Keep reconciliation keys non-null.

Id is the safest key. It is always present, always sortable, and immune to the text-ordering problem described under Comparison tolerance.

Column names

A Salesforce custom field carries an API name with a __c suffix. Soda does not map those onto warehouse column names for you, so name the columns explicitly on each side of every check.

The same asymmetry affects filters. A check-level filter applies one predicate to both sides, and one predicate cannot name region__c and region at once. Put the Salesforce predicate in a source-side filter instead. Any two data sources with differing column names have this problem; Salesforce just guarantees it.

Query behavior

Every reconciliation metric compiles to SOQL. Aggregates are evaluated inside your org and only the resulting value comes back. Row-level checks read both sides ordered by key and stream the results in pages, so a large comparison is not bound by the SOQL offset ceiling.

Where several metrics cover one object and share a filter, Soda compiles them into as few SOQL statements as it can. Thirty metrics on one object cost a handful of calls.

A transient failure mid-stream, such as a rate limit or a gateway error, is retried on the same page.

Availability in Soda Cloud

Onboard a Salesforce data source and its datasets the same way you onboard any other, then author and schedule contracts against them.

These parts of Soda Cloud are unavailable for a Salesforce data source:

  • Dataset profiling. Onboard with profiling turned off.

  • Metric Monitoring, and the anomaly detection built on it. Onboard with monitoring turned off.

  • Contract Autopilot, which generates a contract from profiled statistics.

  • Data standards. Salesforce datasets are left out of a standard's scope rather than failing when the standard runs.

  • Sample data, which rests on the row sampling Salesforce refuses.

Caveats and limitations

Unsupported reconciliation checks and filters

  • duplicate_diff always reports NOT_EVALUATED, because SOQL cannot compute a per-row hash.

  • A rows_diff with no value columns reports NOT_EVALUATED. SOQL has no SELECT *, so set source_columns and target_columns. A reference_diff that omits columns, or uses the shorthand form, does the same, since Salesforce field names do not resolve to warehouse column names automatically.

  • Two kinds of filter also report NOT_EVALUATED: a predicate SOQL cannot express, such as a bare literal like 1 = 1 or a SQL-only function, and a datetime literal with no timezone. SOQL datetime literals must carry a timezone, and Soda does not guess one.

  • Any reconciliation check that asks for row sampling reports NOT_EVALUATED. SOQL has no equivalent of TABLESAMPLE.

Comparison tolerance

  • Salesforce returns numbers over JSON and datetimes as timezone-aware values. Soda therefore compares values from a Salesforce source with a tolerance rather than testing exact equality.

  • Fractional numbers that differ below a relative tolerance of 1e-12 compare as equal. Integral values compare exactly. Datetimes compare to microsecond precision, and anything finer is truncated. Two warehouse data sources would flag differences that a Salesforce reconciliation treats as equal.

  • This is why aggregate_diff on avg needs a tolerance threshold. An exact must_be: 0 can fail on representation differences alone, with no discrepancy in the underlying data.

  • Text is compared case-sensitively, so a difference in case is a real difference and Soda reports it as one. Ordering is the complication: SOQL sorts text case-insensitively while a warehouse sorts by codepoint, so the two streams can disagree on order. For rows_diff this matters only when one dataset holds two keys differing solely in case. reference_diff is more exposed, because any mixed-case text can push its count high and report added and removed rows for two sets that match. Non-ASCII text keys carry the same risk in both checks.

  • Uniform-case keys are exact. Id keys are exact.

API request limits

Your org's API request limits apply to every call Soda makes. A large contract or reconciliation costs a metadata call per object, plus a call per page of rows and per aggregate, so it can take a real share of the daily quota.

Filter each check on an indexed field such as LastModifiedDate or SystemModstamp to scope the comparison to a recent window instead of the whole object. Schedule scans outside your peak hours.


You are not logged in to Soda and are viewing the default public documentation. Learn more about Licensing & documentation access.

If you do have a Soda license, make sure to log in to Soda Cloud in this same browser.

Last updated

Was this helpful?