Reverse ETL: Snowflake → Salesforce¶
lht retl runs a Snowflake SELECT and writes the rows to a Salesforce object through a Bulk API 2.0 ingest job. Use it to push lead scores, enrichment, cleaned addresses, account hierarchies, or any model output back to where your sales team works.
lht retl upsert --sobject Account --match-field External_Id__c \
--sql "SELECT External_Id__c, Rating, AnnualRevenue FROM ANALYTICS.ACCOUNT_SCORES"
Operations¶
| Operation | Required columns | Notes |
|---|---|---|
upsert |
The --match-field column (an External ID field, or Id) plus the fields to set |
Creates records whose match value isn't found. A blank match value always creates a record. |
update |
Id plus the fields to set |
|
insert |
Fields to set | |
delete |
Id |
Soft delete: records go to the Recycle Bin |
Column names in the query must be Salesforce field API names (Industry, My_Field__c). Snowflake folds unquoted names to uppercase; Salesforce matches field names case-insensitively, so SELECT industry works.
NULL handling¶
By default a NULL becomes an empty CSV cell, which Salesforce treats as "don't change this field". To blank a field, pass --clear-nulls: lht then sends #N/A, which clears it. The match field itself is never turned into #N/A.
Batching¶
upsert reads the query once and sends it in batches of --batch-size rows (default 25,000). Each batch is its own Bulk API ingest job.
Results and logging¶
lht get-job-results JOB_IDdownloads the successful, failed and unprocessed CSVs for any ingest job.--log-resultsrecords job metadata in Snowflake tables in aLOGSschema (LOGS.RETL_HISTORYand friends), created on first use. Logging is best-effort: a logging failure never aborts the load.
Tips¶
- Keep reverse-ETL queries deterministic, and include only the rows that actually changed. That saves API usage and avoids noisy field history.
- Try new jobs in a sandbox first. Salesforce validation rules, triggers and flows all run on Bulk API writes.
- Use
--sql-filefor anything longer than a line, and keep those files in version control next to your dbt models.