Intermediate~15 min setupProductivity & CRMVerified April 2026
Google Sheets logo
Salesforce logo

How to Bi-directional contact sync with Power Automate

Automatically sync contact updates between Google Sheets and Salesforce in real-time whenever either system changes.

Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for

Teams that maintain contact lists in both Sheets and Salesforce and need instant sync without manual copying.

Not ideal for

One-time imports or bulk migrations where Salesforce Data Loader would be faster and more reliable.

Sync type

real-time

Use case type

sync

Real-World Example

💡

A 25-person sales team keeps prospect research in Google Sheets while managing deals in Salesforce. When marketing updates contact details in Sheets, sales needs those changes in Salesforce instantly. Before automation, data got stale for days and reps called wrong phone numbers during outreach.

What Will This Cost?

Drag the slider to your expected monthly volume.

/mo
505005K50K

Each platform counts differently — Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.

Prices shown for annual billing. Based on published pricing as of April 2026.

Estimated ROI

1000

min saved/mo

$583

labor value/mo

Free

no platform cost

Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.

Implementation

Skip the setup

Import this workflow directly into Power Automate

Copy the pre-built Power Automate blueprint and paste it straight into Power Automate. All modules, filters, and field mappings are already configured — you just need to connect your accounts.

Before You Start

Make sure you have everything ready.

Google account with edit access to the target spreadsheet
Salesforce user account with Create, Read, Edit permissions on Contact object
Power Automate premium license for unlimited flows and Salesforce connector
Structured Google Sheet with Email column as unique identifier

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
First NameFirstName
Last NameLastName
EmailEmail
6 optional fields▸ show
PhonePhone
CompanyAccount.Name
Job TitleTitle
Lead SourceLeadSource
Mailing StreetMailingStreet
Mailing CityMailingCity

Step-by-Step Setup

1

My flows > New flow > Automated cloud flow

Create Flow for Sheets to Salesforce sync

Go to make.powerautomate.com and click 'New flow' then 'Automated cloud flow'. Name it 'Sheets to Salesforce Contact Sync'. Search for 'Google Sheets' in the connector picker and select 'When a row is added, modified or deleted'. This triggers whenever your contact spreadsheet changes.

  1. 1Click 'New flow' in the top menu bar
  2. 2Select 'Automated cloud flow' from the dropdown
  3. 3Name the flow 'Sheets to Salesforce Contact Sync'
  4. 4Search 'Google Sheets' in the connector search
  5. 5Choose 'When a row is added, modified or deleted'
What you should see: You should see the Google Sheets trigger block with connection fields ready to configure.
Common mistake — The trigger fires on ANY row change including header modifications, so structure your sheet carefully.
2

Sheets Trigger > Connection Settings

Configure Google Sheets connection

Sign in to your Google account when prompted. Select your contact spreadsheet from the dropdown and choose the specific worksheet tab. Set 'Change type' to 'Updated' to catch contact edits. Power Automate will create a webhook that Google notifies when rows change.

  1. 1Click 'Sign in' and authenticate your Google account
  2. 2Select your contact spreadsheet from the File dropdown
  3. 3Choose the worksheet tab containing contacts
  4. 4Set Change type to 'Updated'
  5. 5Leave Key Column as default (Row number)
What you should see: Green checkmark appears next to Google Sheets with your spreadsheet name displayed.
Common mistake — If your spreadsheet has merged cells or complex formatting, the trigger may fail silently.
3

New step > Control > Condition

Add condition to filter contact rows

Click 'New step' and search for 'Condition' under Control. This prevents the flow from processing header rows or empty cells. Set the condition to check if the Email column is not empty, since contacts need email addresses in Salesforce.

  1. 1Click '+ New step' below the Sheets trigger
  2. 2Search 'Condition' and select it from Control
  3. 3Click the left value field and select 'Email' from dynamic content
  4. 4Set operator to 'is not equal to'
  5. 5Leave right value empty to check for non-empty emails
What you should see: Condition block shows with 'Yes' and 'No' branches for next steps.
Common mistake — Filters are the most common place setups break. Double-check the field name and value exactly match what your app sends — a single capital letter difference will block everything.
Google Sheets
GO
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Salesforce
SA
notified
4

Yes branch > New step > Salesforce > Get records

Check if contact exists in Salesforce

In the 'Yes' branch, add 'Get records' from Salesforce connector. This searches for existing contacts by email to avoid duplicates. Set Object Type to 'Contact' and Filter Query to 'Email = [Email from Sheets]'. Power Automate will authenticate to Salesforce using OAuth.

  1. 1Click 'Add an action' in the Yes branch
  2. 2Search 'Salesforce' and select 'Get records'
  3. 3Sign in to Salesforce when prompted
  4. 4Set Object Type to 'Contact'
  5. 5Set Filter Query to Email eq 'EMAIL_DYNAMIC_CONTENT'
What you should see: Salesforce connector shows connected with your org name and the query field populated.
Common mistake — Filter Query uses OData syntax - 'eq' not '=' and single quotes around dynamic values.
5

Yes branch > New step > Condition

Add condition for update vs create

Add another Condition after the Salesforce query. Check if the 'Get records' step returned any results using the 'value' array length. If length is greater than 0, update the existing contact. If 0, create a new contact record.

  1. 1Add Condition after Get records step
  2. 2Click left value and go to Expression tab
  3. 3Type: length(outputs('Get_records')?['body/value'])
  4. 4Set operator to 'is greater than'
  5. 5Set right value to 0
What you should see: Second condition appears with Yes (update) and No (create) paths.
Common mistake — The expression is case-sensitive and must match your step name exactly.
6

Inner Yes > Salesforce > Update record

Configure update contact action

In the inner 'Yes' branch, add 'Update record' from Salesforce. Set Object Type to Contact and Item ID to the ID from the Get records result. Map fields from your Google Sheets columns to Salesforce fields like FirstName, LastName, Email, Phone, Company.

  1. 1Add action in the inner Yes branch
  2. 2Select Salesforce 'Update record'
  3. 3Set Object Type to Contact
  4. 4Set Item ID from Get records dynamic content
  5. 5Map Google Sheets columns to Salesforce fields
What you should see: Update form shows with your mapped fields and dynamic content tokens.
Common mistake — Required fields in Salesforce (like LastName) must have values from Sheets or the update fails.
7

Inner No > Salesforce > Create record

Configure create contact action

In the 'No' branch, add 'Create record' from Salesforce for new contacts. Set Object Type to Contact and map the same fields as the update step. Include at minimum FirstName, LastName, and Email since these are typically required in most Salesforce orgs.

  1. 1Add action in the inner No branch
  2. 2Select Salesforce 'Create record'
  3. 3Set Object Type to Contact
  4. 4Map FirstName from Sheets to FirstName
  5. 5Map other required fields like LastName and Email
What you should see: Create form populated with field mappings and required fields marked.
8

My flows > New flow > Automated cloud flow

Create reverse flow for Salesforce to Sheets

Create a second automated flow named 'Salesforce to Sheets Contact Sync'. Use the Salesforce trigger 'When a record is created or modified' with Object Type set to Contact. This catches changes made directly in Salesforce that need to sync back to Sheets.

  1. 1Create new Automated cloud flow
  2. 2Name it 'Salesforce to Sheets Contact Sync'
  3. 3Search Salesforce connectors
  4. 4Select 'When a record is created or modified'
  5. 5Set Object Type to Contact
What you should see: Salesforce trigger configured and waiting for the next step.
Common mistake — This trigger has a 5-minute delay minimum - not truly instant like the Sheets trigger.
9

Salesforce trigger > Google Sheets > Get rows

Find matching row in Google Sheets

Add 'Get rows' from Google Sheets connector to find the matching contact by email. Use Filter Query with the email from the Salesforce trigger. This identifies which row needs updating in your spreadsheet.

  1. 1Add Google Sheets 'Get rows' action
  2. 2Select same spreadsheet and worksheet
  3. 3Set Filter Query to Email eq 'EMAIL_FROM_SALESFORCE'
  4. 4Leave other settings default
What you should see: Sheets connector shows configured with filter query populated.
Common mistake — If multiple rows have the same email, only the first match gets updated.
10

Condition > Yes/No branches > Sheets actions

Update or insert row in Sheets

Add a Condition to check if the Get rows found a match. If yes, use 'Update row' with the Row number from results. If no match, use 'Add row' to append the new contact. Map Salesforce fields back to your Sheets columns in the appropriate action.

  1. 1Add Condition checking length of Get rows results
  2. 2In Yes branch: add 'Update row' with Row number from results
  3. 3In No branch: add 'Add row' to append new contact
  4. 4Map Salesforce fields to Sheets columns in both actions
What you should see: Both flows are complete with bidirectional sync logic configured.
Common mistake — Test with dummy data first - bidirectional sync can create infinite loops if both triggers fire simultaneously.
11

Flow details > Save > Test

Test and enable both flows

Save both flows and test with a sample contact edit in each system. Monitor the run history for errors and check that changes sync correctly without creating duplicates or loops. Enable both flows only after successful testing.

  1. 1Click Save on both flows
  2. 2Test by editing a contact in Sheets first
  3. 3Verify change appears in Salesforce
  4. 4Test editing same contact in Salesforce
  5. 5Check both run histories for success
What you should see: Both flows show green success status and contact data matches between systems.
Common mistake — Rapid edits in both systems within 5 minutes can cause conflicts due to Salesforce trigger delay.

Add this expression in a Compose action to prevent sync loops by checking if the last modification came from Power Automate. Paste this in the Expression editor when configuring your condition logic.

JavaScript — Code Stepif(
▸ Show code
if(
  equals(
    outputs('Get_records')?['body/value']?[0]?['Description'],

... expand to see full code

if(
  equals(
    outputs('Get_records')?['body/value']?[0]?['Description'],
    'Updated by PowerAutomate'
  ),
  'skip',
  concat(
    outputs('Get_records')?['body/value']?[0]?['FirstName'],
    ' ',
    outputs('Get_records')?['body/value']?[0]?['LastName']
  )
)
Power Automate
▶ Test flow
executed
Google Sheets
Salesforce
Salesforce
🔔 notification
received

Going live

Production Checklist

Before you turn this on for real, confirm each item.

Troubleshooting

Common errors and how to fix them.

Frequently Asked Questions

Common questions about this workflow.

Analysis

VerdictWhy n8n for this workflow

Use Power Automate for this if you're already in the Microsoft ecosystem and need simple bidirectional sync without complex transformations. The Salesforce connector is solid and the Google Sheets integration handles webhooks properly. Skip this platform if you need sub-minute sync in both directions - Salesforce polling has a 5-minute minimum that kills true real-time sync.

Cost

Costs add up fast. Each contact edit burns 2 runs (check + update), so 500 contact changes monthly hits 1,000 runs. Power Automate Premium costs $15/month for 5,000 runs. Compare that to Make at $9/month for 10,000 operations - Make wins on pure cost.

Tradeoffs

Make handles the same workflow with better error handling and faster Salesforce sync using webhooks instead of polling. Zapier offers cleaner field mapping UI and built-in deduplication. n8n gives you custom JavaScript for complex contact matching logic that Power Automate's expressions can't handle. Pipedream provides better debugging with full request/response logs when things break. But Power Automate wins if you already have Office 365 licenses and want everything in one Microsoft stack.

You'll hit the Salesforce API governor limits with heavy sync volume - 24 hour limits reset at midnight PST, not rolling windows. Required field errors are cryptic and don't tell you which field failed. The Google Sheets trigger fires multiple times for single row edits, burning extra runs and potentially creating duplicates if you don't add deduplication logic.

Ideas for what to build next

  • Add Account syncExtend the workflow to sync company records between Sheets and Salesforce Account objects for complete data alignment.
  • Build error dashboardCreate a Power BI dashboard tracking sync failures, duplicate prevention hits, and daily sync volume metrics.
  • Add field validationImplement data quality checks like email format validation and phone number formatting before syncing to either system.

Related guides

Was this guide helpful?
Google Sheets + Salesforce overviewPower Automate profile →