Now part of ingestr
Reverse ETL
Your warehouse knows. Now your CRM does too.
Tested tables from your warehouse, synced into the tools your teams use.
| email varchar | churn_risk varchar | health_score int |
|---|---|---|
| [email protected] | high | 41 |
| [email protected] | high | 38 |
| [email protected] | medium | 58 |
| [email protected] | low | 77 |
| [email protected] | low | 82 |
Bruin · Reverse ETLnow
Synced to HubSpot
Churn risk and health score, from your warehouse.
Contacts 5 records
All contactsMy contactsAt risk
| Name | Churn risk | Health score | |
|---|---|---|---|
| AAna Ruiz | [email protected] | High | 41 |
| DDevon Park | [email protected] | High | 38 |
| LLina Haddad | [email protected] | Medium | 58 |
| SSam Okafor | [email protected] | Low | 77 |
| JJo Lindqvist | [email protected] | Low | 82 |
Trusted by forward-thinking teams
Use cases
What you model, where it gets used.
From your warehouse
Lands in
So that
churn_riskHubSpot · Companies
HubSpot · Companies
Customer success sees who is at risk before the renewal call.
lead_scoreSalesforce · Lead
Salesforce · Lead
Sales works the best leads first.
seats_usedAttio · Companies
Attio · Companies
Account owners see usage next to the deal.
lifetime_valueCleverTap · Profiles
CleverTap · Profiles
Campaigns reach your best customers, not everyone.
In your repo
One asset. Pointed the other way.
The same ingestr asset that loads your sources, with a CRM as the destination. It runs after the models it reads.
name: sync_contacts_to_hubspot
type: ingestr
depends:
- marts.customer_health
parameters:
source_connection: my-snowflake
source_table: 'marts.customer_health'
destination: hubspot
destination_connection: my-hubspot
destination_table: 'contacts?id_property=email'
incremental_strategy: merge
columns:
- name: email
primary_key: true$ bruin run
- marts.customer_health4.2s
- not_null · email
- accepted_values · churn_risk
- sync_contacts_to_hubspotmerge on email
Checks passed. HubSpot is up to date.
Destinations
Four destinations. Any source.
Anything ingestr reads can feed them: a warehouse, a database, even a CSV.
HubSpot
Contacts, companies, deals, custom objects and associations.
contacts?
HubSpot docs
Salesforce
Any standard or custom object, through the Bulk API.
Contact?
Salesforce docs
Attio
People, companies, deals and custom objects.
people?
Attio docs
CleverTap
User profiles and events.
profiles
CleverTap docs
Strategies
Five ways to write.
Pick one per asset. Each destination takes the ones its API supports.
merge
Update the match, or create it.
The usual choice.
update
Update matches only.
No match: rejected, never created.
append
Always create.
Scope each run to new rows.
delete
Remove the matches.
Archive or hard delete, per API.
replace
Mirror the source.
Removes what is not in it.
Guardrails
Bad data stays in the warehouse.
- marts.customer_health
- sync_contacts_to_hubspot
HubSpot
$ bruin run
- marts.customer_health4.2s
- accepted_values · churn_risk12 rows
- sync_contacts_to_hubspotheld
1 check failed. Nothing was sent to HubSpot.
Refused up front
A strategy the destination can't do is rejected before the run starts.
Rejects, reported
Rows the API won't take are listed. Fail at the end, stop at the first, or skip.
Nulls, your call
With write_nulls: false, a NULL leaves the stored value alone.
Safety guard
A run with zero source rows removes nothing.
The platform
Part of the Bruin platform.
Runs on ingestr, the engine that loads your sources, in the same repo as your models and checks.
Sources
DatabasesWarehousesApps & APIsFiles & storageStreams & webhooksWeb scrapingMove
Data IngestionFrequently asked
Questions about reverse ETL.
What is reverse ETL?
Reverse ETL moves data out of your warehouse and into the tools teams work in, such as a CRM. In Bruin it is ingestion in the other direction: the warehouse stays the source of truth, and each row becomes a record in the tool.
Which tools can Bruin reverse ETL write to?
HubSpot (contacts, companies, deals, custom objects and associations), Salesforce (any standard or custom object), Attio (people, companies, deals and custom objects) and CleverTap (user profiles and events).
Can Bruin sync Snowflake or BigQuery to HubSpot or Salesforce?
Yes. Any source ingestr reads can feed a reverse ETL destination, so Snowflake, BigQuery, Databricks, Postgres or even a CSV file can sync into HubSpot or Salesforce.
How does Bruin match warehouse rows to CRM records?
You name the field to match on in the destination table, such as email in HubSpot or an External ID field in Salesforce, and mark the source column that holds the value as the primary key.
Can it update records without creating new ones?
Yes, with the update strategy: matching records are updated and rows with no match are rejected, never created. merge updates or creates, and append always creates.
What happens when HubSpot or Salesforce rejects a row?
By default Bruin sends every valid row, then fails the run and lists the rejects. Set reject_mode to fail_fast to stop at the first bad row, or to skip to report the rejects and still succeed.
Will a NULL in the warehouse wipe a field in the CRM?
By default, yes: a NULL is written through and clears the field. Set write_nulls to false and Bruin leaves the stored value untouched.
Can a sync delete records in the CRM?
Only if you choose a strategy that does. delete removes the records you match; replace removes every record not in the source, including ones created by hand, so use it only when the source is complete. A run with zero source rows removes nothing.
Are reverse ETL writes transactional?
No. The destination API decides what is possible, so a run that fails partway may already have written some records, and Bruin cannot roll them back. It reports the rows the API rejected.
Do the fields need to exist in the CRM first?
In HubSpot, yes: properties must already exist. Salesforce maps source columns to fields by API name, and Attio expects columns to match existing attributes.
How is Bruin reverse ETL different from a separate reverse ETL tool?
It is not a second tool: a sync is an ingestr asset in the same repo and pipeline as your ingestion, SQL models and quality checks. It runs after the models it depends on, so a failing check holds the sync.
Is it open source, or do I need Bruin Cloud?
Reverse ETL ships in ingestr, which is source-available under the Functional Source License, and runs from the open-source Bruin CLI anywhere a binary runs. Bruin Cloud adds schedules, run history and alerts.
How fast does it write?
Rows go out in bulk through each API's batch endpoints, within its rate limits, and Salesforce uses the Bulk API by default. The destination sets the pace: Attio, for example, allows about 25 writes per second.
From the warehouse to the CRM.
$100 in credits and 50 AI tasks. No credit card.
A demo walks through your own data.
