Documentation
How do I enable the plugin?
After purchasing, you’ll need to install the plugin on your WordPress website. Here’s how.
Step 1: Installation
Log in to your WordPress site and navigate to Plugins → Add Plugin → Upload Plugin. Upload the zip file and activate it.
Step 2: Register the Plugin
On the left-hand navigation, navigate to Forms → Settings → Salesforce. In the License section click Manage License, enter your license key and click Activate License.
Step 3: Connect Salesforce
Once licensed, scroll to the Salesforce Connection section on the same settings page (it is titled Add Salesforce Account once an account exists) and connect your first Salesforce account (full instructions below)
That’s it! You’re ready to start mapping forms to Salesforce.
How do I connect a Salesforce account with OAuth?
Step 1: Understand the App Types
In Salesforce you’ll create either an External Client App (the modern option, and the only one available for new orgs created since Salesforce’s Spring ’26 release) or a Connected App (the legacy option, still supported on existing orgs that allow it). The plugin works identically with both.
Navigate to one of the following in Salesforce Setup:
- Setup → External Client App Manager → New External Client App (recommended)
or legacy connected app (disabled by default on newer orgs)
- Setup → App Manager → New Connected App
Before you connect: the Salesforce user who authorizes the connection needs Create (and Edit, for feeds that update) on every object you’ll sync to. The plugin only lists objects that user can actually create.
Step 2: Create a new External Client App
NOTE: In our screenshots below, settings left unchecked or empty are meant to be left alone, so leave them as is.
After logging into salesforce, on the top right of your screen, click the cog wheel:

On the left side of your screen, in the search box that says “quick find”, search for “external client app” and then select “External Client App Manager”:

On the right hand side of your screen click the button that says “New External Client App”:

Under “Basic Information” enter the following:
- External Client App Name (the api name will auto generate, leave that alone)
- Contact Email
- Leave the Distribution State as “Local”

Under “API (Enable OAuth Settings)” do the following:
- Enable OAuth
- Enter your Callback URL (this is located in the plugin settings under Forms > Settings > Salesforce > OAuth Callback URL)
- Under OAuth Scopes, select the following:
- “Manage user data via APIs (api)”
- “Perform requests at any time (refresh_token, offline_access)”

Leave these settings below alone:

Leave all remaining settings below alone too:

Click create app and you’re ready to move to step 3:

Step 3: Edit the App Authorization Policies
After the app is created, we need to edit the app policies. Click back into “External Client App Manager”

Then select your newly created app and click the “Edit” button like you see below:

Then open the “OAuth Policies” dropdown and set the refresh token policy to expire “after specific time” then set it to 365 days or more. The plugin refreshes the short-lived access token on its own, but once the refresh token itself expires Salesforce rejects the connection and you have to reconnect the account under Forms → Settings → Salesforce. A longer policy means you do that once a year at most instead of every 30 days.
We recommend setting a reminder to check the connection just in case.

Step 4: Connect the new External Client App to Gravity Forms
The salesforce setup side is complete, now we need to connect Gravity Forms to the app. In salesforce go back to “External Client App Manager” and select your app.

Click “Settings” and scroll down to “OAuth Settings” and open it. You’ll see a button that says “Consumer Key and Secret” click that button.

You’ll most likely be presented with an identity verification screen and a code will have been sent to you. Enter the code and click “Verify”

After verifying, you’ll be presented with the Consumer Key and Consumer Secret. Keep that tab open and in a new tab go to our plugin settings under Forms > Settings > Salesforce and add those keys like the screenshot below describes. The button appears once both fields are filled: click Connect Account (it reads Add Account once you already have an account connected).

You’ll see Gravity Forms is connected to Salesforce now and we can move to Step 5 (final step):

Step 5: Add a Salesforce Feed to your form
Go to the settings for the Gravity Form you wish to connect to salesforce and add a new feed:

And finally, configure your feed settings the way you wish:

Now you’re all set! If you’d like to connect additional salesforce accounts and create feeds for other forms, repeat those 5 steps above.
How do I connect with Org ID? (Web-to-Lead / Web-to-Case)
If your Salesforce org doesn’t have API access (some Essentials/Professional editions), you can fall back to the legacy Web-to-Lead / Web-to-Case endpoint.
Step 1: In Salesforce, find your Organization ID under Setup → Company Information.
Step 2: In WordPress, on the Salesforce settings page, switch the Connection Method dropdown to “Org ID (Web-to-Lead/Case)”.
Step 3: Paste your Org ID and click Save Org ID Account. As of 1.5.0 this reads the values typed into the form, so there’s no separate settings save first.
Heads-up: Org ID mode only supports Lead and Case. Updates, deletes, file uploads, prefill, and picklist mapping require an OAuth account.
How do I set up a feed?
Step 1: Edit a form → Form Settings → Salesforce → Add New.
Step 2: Pick the Salesforce Account, Salesforce Object (Lead, Contact, Account, custom object…), and Sync Mode (the default, Create or update if a record exists, is right for most cases).
Step 3: Map your Gravity Forms fields to Salesforce fields. Required Salesforce fields are marked with an asterisk so you can’t miss them.
Step 4 (optional): Under Duplicate Matching, tick Enable primary key duplicate matching to update existing records matched on Email, an External ID, or any other filterable field.
Step 5 (optional): Configure conditional logic, the conflict check, owner assignment, file uploads, notes, or campaign membership.
Step 6: Save the feed and submit a test entry. Check the entry detail screen for the Salesforce sidebar. Because syncing runs in the background you may briefly see a blue Pending status, then the new record’s ID and a direct link to it in Salesforce.
Which sync behaviours do I need to switch on?
Creating records works as soon as a feed is saved. The lifecycle behaviours are opt-in, under Forms → Settings → Salesforce → Sync Settings, and all four are off until you tick them:
- Sync updates when entries are edited pushes entry edits to the linked Salesforce record.
- Delete Salesforce records when entries are permanently deleted removes the record when you empty the trash. Moving an entry to trash never touches Salesforce.
- Resync entry data when entries are restored from trash/spam pushes any edits made while the entry was trashed, and creates a replacement if the record was deleted in Salesforce meanwhile.
- Send Gravity Forms entry notes to Salesforce as Note records mirrors notes an admin adds to an entry, and deletes the Salesforce copy when the note is deleted.
Feature Details
Connection Methods
The plugin supports two ways to connect Gravity Forms to Salesforce. Pick the one that matches your org’s API access level.
- OAuth 2.0 with PKCE (recommended). Modern Salesforce authentication using a Connected App or External Client App (the Spring ’26+ replacement). No static API keys live on your server, the plugin refreshes access tokens automatically, and the connection stays alive indefinitely as long as the app’s refresh token policy doesn’t expire it. Required scopes:
apiandrefresh_token/offline_access. Works with all Salesforce editions that have API access (Enterprise, Unlimited, Performance, Developer, or Professional with the API access add-on). - Web-to-Lead / Web-to-Case (Org ID). Legacy submission endpoint that uses just your Salesforce Organization ID. Useful for orgs without API access (some Essentials and Professional editions). Limitations: Lead and Case objects only, no updates, no deletes, no file uploads, no prefill, no picklist mapping.
Both modes can be used on the same WordPress site. You can have an OAuth account for your main org and an Org ID account for a side org, and each feed independently picks which one it syncs to.
Multi-Account Support
Connect as many Salesforce accounts as you want to one WordPress site. Common setups:
- Sandbox and production: test feeds against sandbox, then switch to production with one dropdown change.
- Agency: each client org gets its own connection; each feed picks which client it syncs to.
- Parent and subsidiaries: different forms route to different orgs in your corporate structure.
One account is always marked as the default so simple sites never have to think about which account to use. It’s also the fallback: a feed with no account set, or whose account you later remove, syncs to the default account. Removing an account warns you about that before it goes ahead, so point affected feeds at another account first if the default org isn’t where they should write.
Each account also carries its own connection health. The plugin records when each account last refreshed, last succeeded, and last failed, and the Accounts list reports Connected with a last-verified time, Needs attention, or Reconnect required, so a broken connection is visible before submissions start failing. Each account keeps the Salesforce API version it was connected with (new connections default to v63.0); remove and reconnect an account to move it to a newer version. Removing an account revokes its tokens with Salesforce rather than just forgetting them locally.
Salesforce Objects Supported
You can sync to any Salesforce object the connected user can access, standard or custom. The Object dropdown is built dynamically from your org’s actual schema, so you’ll see every standard object Salesforce ships with (Account, Contact, Lead, Opportunity, Case, Campaign, Task, Event, Note, User, and many more) plus every custom object you’ve defined.
For custom objects, choose “Custom (type below)” and enter the API name (e.g., My_Object__c). Picklist values, lookup relationships, file attachments, and prefill all work the same way against custom objects as standard ones.
Linking Records Across Feeds
One submission can create several related records. Add a feed per object, then drag the rows on the feed list into the order you want. Feeds run in that order, one after another, in the background. In a later feed, map the lookup field (AccountId, ContactId, or any custom lookup) to Custom Value and enter {salesforce_id:FEED_ID}, where FEED_ID is the earlier feed’s ID. The tag resolves to the record that feed created or updated for this entry, so the new record is linked to it. The feed ID is the fid= value in the address bar while you edit the feed.
A typical chain is an Account feed first, then a Contact feed with AccountId set to {salesforce_id:12} (12 being the Account feed’s ID), then an Opportunity feed with AccountId set the same way. Use the same Salesforce account on every feed in the chain, because a record ID only exists in the org that issued it. If the earlier feed created no record, because its conditional logic didn’t match or the conflict check stopped it, the tag is empty and the lookup stays unset instead of taking another feed’s ID.
Field-Level Picklist Mapping
For Drop Down, Multi Select, Radio Button, and Checkbox fields, you can pull choices directly from a Salesforce picklist instead of typing them manually. Two modes per field:
- Populate choices (editable) copies the picklist values into the Gravity Forms field once. The admin can edit or remove choices afterwards. Good for “I want a starting point but might prune the list.”
- Live sync (always match Salesforce) refreshes the choices on every form render. Choices are locked in the form editor to prevent drift. Use this when Salesforce is the single source of truth (status fields, lead sources, State/Country picklists, etc.).
Salesforce labels are shown to form users; Salesforce values are submitted. So the form shows “California” but the entry submits CA, exactly what Salesforce State/Country picklists expect, and you don’t need a mapping table.
Record Type is a picklist here too. For any object with selectable record types, Record Type appears alongside the real picklists in the same field-mapping UI. The form shows the record type name to users (e.g., “Donor”); the submitted value is the Salesforce Record Type ID. Map that field to RecordTypeId in your feed and the chosen record type is set on the created or updated record. Only record types your connected Salesforce user can actually assign are listed (the implicit Master type and unavailable types are filtered out), and both Populate and Live Sync modes are supported.
The Salesforce Object dropdown in the field-mapping sidebar pins the eight most-used standard objects (Account, Contact, Lead, Opportunity, Case, Campaign, Task, Note) at the top of the list, so you don’t have to scroll past every custom object in the org to find them. On the feed settings screen, change the object or the account and the field map, match field and object pickers reload from Salesforce right away rather than waiting for a save.
Primary Key Matching (no duplicates)
Most Salesforce admins don’t want every form submission to create a new Lead. Enable Primary Key matching to update existing records when there’s a match.
- Pick any filterable, idLookup, or externalId field as the match key (Email is the most common).
- If a matching record is found, it’s updated. If not, a new record is created (when Sync Mode is set to “Create or update if a record exists”).
- Use a custom external ID (
Customer_Reference__c) for matching across systems. - Built-in protection against the “matched record was deleted in Salesforce” race condition. If Salesforce returns 404 for a stored ID, the plugin transparently creates a fresh record.
Conflict Check
Primary Key matching updates a record when it finds one. Sometimes you’d rather the feed stopped instead. The conflict check looks for an existing Salesforce record before the feed creates anything, and halts if it finds one.
Where duplicate matching works on a single key, the conflict check combines several fields into one lookup. Map First Name and Last Name together and you’ll catch the same person submitting under a different email address, which a match on Email alone would let straight through. Every mapped row has to match the same record for the feed to stop, so a partial overlap doesn’t trigger it.
- Map as many Salesforce fields to Gravity Forms fields as you need, custom values included.
- Set your own conflict message. It’s logged against the entry and sent in the failure email, so you find out why the record wasn’t created.
- Only applies when the feed would create a record. A record found by duplicate matching is still updated as normal.
- Per feed, and available on OAuth accounts.
File Uploads
Gravity Forms file upload fields can be attached to the Salesforce record as ContentVersion / ContentDocumentLink, so they show up in the record’s Files related list. Configurable max file size, 1 to 25 MB per file, defaulting to 10.
To attach files, map each File Upload field to the Files — attach to Salesforce record row in the feed’s Salesforce Field Map. Add the row once per upload field. Every mapped field is attached, and a multi-file field sends every file it holds. Files go up the first time a feed syncs an entry, and Re-send to Salesforce sends them again after a failure.
Notes Sync
Push form data into a Salesforce Note attached to the parent record. Both the note title and the note body support merge tags, so you can format them however you like. Leave the body blank and it uses {all_fields}.
Optionally, mirror admin-added Gravity Forms entry notes to Salesforce as Notes too, which keeps the conversation history in one place. Delete the note in Gravity Forms and the Salesforce copy goes with it.
Campaign Membership
Add the Lead or Contact created by the form to a Salesforce Campaign. Tick the box, give it a Campaign ID (a merge tag works, so a hidden field can pick the campaign), and the plugin creates a CampaignMember record linking the new Lead or Contact to the Campaign. Set your own member status, or leave it and the plugin uses Responded.
Owner Assignment
Per-feed:
- Set a custom OwnerId (supports merge tags, so you can route based on form values).
- Toggle the
Sforce-Auto-Assign: FALSEheader to bypass standard assignment rules when needed.
Prefill from Salesforce
Send returning users a personalized form, prefilled with their existing Salesforce data. Renewal forms, account update forms, profile completion flows.
- Map any Salesforce field to any Gravity Forms field for prefill. The prefill object defaults to the one the feed writes to, but you can read from a different one, for example prefill from the Contact while the feed creates a Case.
- Prefill works for logged-in admins as soon as it’s enabled. To use it on a public form, tick Allow public prefill requests on front-end forms as well.
- HMAC-signed URLs with TTL prevent record-ID enumeration on public forms, and signing is on by default with a 15 minute expiry. Without signatures, anyone could iterate
?sfid=001000...and pull data they shouldn’t have. - The Signed Prefill Link Generator is built into feed settings. Paste a record ID, get a signed URL with an expiration. Use it in marketing emails, customer portals, or self-service flows.
- Logged-in admins can use raw record IDs without signatures for testing.
- Per-feed query parameter (default
?sfid=) keeps URLs clean.
Entry Lifecycle Sync
Creating records is automatic once a feed is saved. The rest of the lifecycle is yours to switch on, under Forms → Settings → Salesforce → Sync Settings, so an integration only ever writes back as much as you asked it to.
- Edit an entry → the Salesforce record is updated, once “Sync updates when entries are edited” is enabled (
gform_after_update_entry). - Trash an entry → the Salesforce record is left untouched, always. Trash is reversible in WordPress, so the record stays put.
- Restore from trash → with “Resync entry data when entries are restored from trash/spam” enabled, any edits made while the entry was trashed are pushed to the record. If the record was manually deleted in Salesforce meanwhile and the feed’s Sync Mode is “Create or update if a record exists” (the default), a fresh replacement is created.
- Permanently delete an entry (Empty Trash, or “Delete Permanently”) → the Salesforce record is deleted, if you’ve enabled “Delete Salesforce records when entries are permanently deleted”.
Background Sync
Salesforce API calls don’t run while the visitor waits. Feeds process in the background after the submission completes, using Gravity Forms’ own asynchronous feed processing, so a slow Salesforce response never turns into a slow thank-you page.
Once the background job picks up the feed, the entry gets a blue Pending status, which is replaced by the final success or error result as soon as Salesforce answers. Open a test entry straight after submitting and Pending may be the first thing you see. If a feed needs to finish before the thank-you page, for example to show {salesforce_id} on it, tick Process this feed during submission on that feed.
Payment Add-on Aware
If your form takes a payment via a Gravity Forms payment add-on (Stripe, PayPal Standard, PayPal Commerce, Authorize.Net, Square, 2Checkout, Mollie, and the rest), enable “Process Salesforce feed only when payment is received.” under Post Payment Actions in the payment add-on’s feed settings (the option appears once the form has a Salesforce feed) to defer the Salesforce sync until the payment confirms successfully, so abandoned checkouts never leave half-baked Leads behind.
At submission, the Salesforce sync is held back if the entry has a pending payment. When the payment add-on confirms the transaction (via Gravity Forms’ standard delayed-feeds framework), the Salesforce sync runs as normal. If the payment fails or is abandoned, the sync simply never runs.
Works with any payment add-on built on Gravity Forms’ standard payment add-on framework, which is all the official ones.
Logs & CSV Export
Every Salesforce sync, successful or failed, is logged with the sync status and the exact message Salesforce returned. The Salesforce Logs page (Forms → Salesforce Logs) shows:
- Entry ID, form, status, record ID, message, and sync time
- Per-form filter, and pagination at 50 rows a page
- CSV export of the current view, so filtering by form exports that form
Turn on Gravity Forms logging and you also get the outgoing request payload and the error message Salesforce returned written to the Gravity Forms log, which is usually the fastest way to see exactly what Salesforce objected to.
If you’d rather not keep sync history at all, “Disable storing Salesforce entry logs (status/messages/sync history)” stops it being recorded and keeps wp_gf_entry_meta lean. Worth knowing before you tick it: the Salesforce Logs page reads from that history, so it goes quiet and the entry sidebar drops to the record link and the Re-send button, and existing history is cleared when you enable the setting. Record IDs stay on the entry, so merge tags and record links keep working.
Email Notifications on Sync Failure
Get pinged the moment Salesforce rejects a payload, with the entry ID and the exact error message Salesforce returned. Tick “Send error notification emails” under Error Reporting in the plugin’s Salesforce settings, then set the recipient once in Notification Email (it defaults to your WordPress admin email if left blank).
Useful for catching:
- A required Salesforce field that suddenly went un-mapped (someone edited the form)
- A picklist value that no longer exists in Salesforce
- API limits, validation rules, or workflow rules rejecting the record
- A Connected App that needs re-authorization
Hitting the Salesforce API request limit is reported as rate limiting rather than a mysterious submission failure, so you know to wait rather than to start debugging your field map.
Fix the issue, hit Re-send to Salesforce on the affected entries, and the records go in. The alternative is finding out two weeks later that 30 leads never reached the CRM.
Per-Entry Salesforce Sidebar
Every Gravity Forms entry detail screen shows a dedicated Salesforce sidebar box with the live status of the sync:
- Status, color-coded: green Success, red Error, blue Pending, grey Skipped
- Object, what Salesforce object was created (Lead, Contact, custom object, etc.)
- Record ID, clickable, opens directly in Salesforce in a new tab
- Account, which connected Salesforce account handled the sync (useful when you have multiple)
- Synced, timestamp of the most recent sync attempt
- Error message, the exact error Salesforce returned, when applicable
- Feed Results, a per-feed breakdown when multiple feeds run on the same form
It’s the fastest way to confirm a form is actually pushing data into Salesforce, and to spot the exact entry that failed when something goes wrong.
Re-send to Salesforce
Entries carry a Re-send to Salesforce button on the entry detail screen, for users who can manage the plugin’s settings. It does more than resync the main record: it clears the side-effect deduplication flags so notes, files, and campaign memberships retry too. Use it when:
- Salesforce was down when the original sync ran
- You fixed a feed mapping after a batch of failed submissions
- You want to push test data to a sandbox without re-submitting the form
Merge Tags
{salesforce_id} is the Salesforce record ID created or updated by the most recent feed for this entry. Feeds that use Org ID mode (Web-to-Lead / Web-to-Case) post to an endpoint that returns no record ID, so they never set this tag or its per-feed variant.
{salesforce_id:FEED_ID} is the Salesforce record ID created by a specific feed. Use this when multiple feeds run on the same form.
Feed-specific tags also work inside feed settings. Map a lookup field to Custom Value and enter {salesforce_id:FEED_ID} to link the record to one an earlier feed created. Feeds run in feed list order, and the feed ID is the fid= value in the feed’s edit URL. A tag for a feed that created no record resolves to empty.
{salesforce_link} is a direct link to the Salesforce record, built from that account’s instance URL and the record ID, for example https://yourorg.my.salesforce.com/00Q5g00000ABCDE. Salesforce sends that on to the Lightning record page.
{salesforce_link:FEED_ID} is a direct link to the record created by a specific feed. Useful when a form has multiple feeds writing to different Salesforce orgs, one to sandbox and one to production say, because each variant uses that feed’s own account instance URL.
Both tags work anywhere Gravity Forms evaluates merge tags, with one timing rule. Feeds run in the background after the submission completes, so the confirmation message and any notification sent on form submission render before the record exists, and the tags come out empty there. They fill in anywhere Gravity Forms evaluates merge tags after the sync has finished. To have them on the confirmation page and in submission notifications, tick Process this feed during submission on the feed; the visitor then waits for Salesforce to respond. Common uses:
- Link records across feeds: on a later feed, set a lookup such as AccountId or ContactId to the Custom Value
{salesforce_id:FEED_ID}of the feed that created the parent record. - Notifications sent later: “New lead in Salesforce → {salesforce_link}” fills in when you use Gravity Forms’ Resend Notifications on the entry after the sync has run.
- Any add-on or custom code that evaluates merge tags after the sync, for example to pass
{salesforce_id}to a downstream system.
Translations
This plugin is translation-ready. You can translate it into any language using the free Loco Translate plugin or any other WordPress translation tool.
Default language: English (en_US)
FAQs
Does this require Gravity Forms?
Yes. Gravity Forms 2.7 or later must be installed and active. This is an add-on for Gravity Forms, so it doesn’t work on its own.
What WordPress and PHP versions are supported?
WordPress 6.5+ and PHP 8.0+. Tested up to WordPress 7.0.
Can I connect to a Salesforce Sandbox?
Yes. Use https://test.salesforce.com as your Login URL when connecting the account. You can have a sandbox account and a production account connected to the same WordPress site at the same time.
Does this support multiple Salesforce accounts on one site?
Yes. Connect as many accounts as you want. Each feed picks which account it syncs to. Common use cases: sandbox and production, agency client orgs, or parent and subsidiary corporate structures.
Can I sync to custom Salesforce objects?
Yes. In the feed’s Salesforce Object dropdown, choose “Custom (type below)” and enter the API name of your custom object (e.g., My_Object__c). Picklists, file uploads, lookups, and prefill all work the same way against custom objects. Custom objects need an OAuth account; Org ID mode is limited to Lead and Case.
Can one form create an Account, a Contact and an Opportunity that are linked together?
Yes. Add one feed per object and drag them into order on the feed list: Account first, then Contact, then Opportunity. Feeds run in that order. In the Contact and Opportunity feeds, map AccountId to Custom Value and enter {salesforce_id:FEED_ID} with the Account feed’s ID, which is the fid= value in its edit URL. Each record arrives with its lookup already pointing at the Account from the same submission. The same works for any lookup field, standard or custom, as long as the feeds use the same Salesforce account.
Why is my Salesforce object missing from the dropdown?
The object list comes from Salesforce and only includes objects the connected user can create. If Lead, Contact or a custom object is missing, the authorizing user is missing Create permission on it, commonly an API-only integration licence with no permission set attached. Fix it in Salesforce, then wait for the plugin’s six-hour object cache to refresh; reconnecting the account does not clear it. “Custom (type below)” works as an immediate workaround.
Does this work with Salesforce Pardot (Account Engagement)?
Not directly. The plugin writes standard Salesforce records such as Lead and Contact through the Salesforce REST API and has no Pardot-specific code. If your org runs the Salesforce-Pardot Connector, Pardot picks those records up the same way it does any other Salesforce record. What this plugin does not do: write directly to Pardot Prospects or Pardot Lists, bypass Salesforce, or interact with Pardot-specific objects (Custom Redirects, Pardot Forms, etc.).
How does picklist mapping handle State and Country fields with codes?
The plugin shows the Salesforce label to the form user (e.g., “California”) but submits the picklist value (e.g., CA), exactly what Salesforce State/Country picklists require, so you don’t need a translation table on your end.
What’s the difference between Populate and Live Sync mode for picklists?
- Populate copies the Salesforce picklist values into your form field once. After that, you can edit, prune, or rearrange the choices. Good when you want a starting point but might curate the list.
- Live Sync re-applies the Salesforce choices on every form render, so changes in Salesforce appear on the form on their own once the plugin’s six-hour field cache refreshes. The form editor locks the choices to prevent accidental drift. Good when Salesforce is the single source of truth (status fields, lead sources, country lists, etc.).
Can I let form users choose a Salesforce Record Type?
Yes. Open the Drop Down / Multi Select / Radio / Checkbox field, enable Salesforce Field Mapping, pick the object, and choose Record Type from the field list. The form shows the record type name (e.g., “Donor”) to users; the submitted value is the Salesforce Record Type ID. Then map that Gravity Forms field to RecordTypeId in your feed mapping so the chosen record type is set on the created or updated record. Only record types available to your connected Salesforce user are listed.
Can I avoid creating duplicate records?
Yes, in two ways. Enable Primary Key matching in feed settings and pick a unique field (Email is the most common, or any custom external ID). The feed will update an existing record when there’s a match, and create a new one when there isn’t.
If you’d rather the feed stopped than wrote anything, use the conflict check instead. It combines several fields into one lookup, so mapping First Name and Last Name together catches the same person submitting under a different email address. When every mapped row matches the same record, the feed stops and logs your conflict message against the entry.
What’s the difference between primary key matching and the conflict check?
Primary key matching finds a record and updates it. The conflict check finds a record and stops. Use matching when a repeat submission should keep one record current; use the conflict check when a second submission is a problem you want to know about rather than absorb. They work together: the conflict check only fires when the feed was about to create a record, so a record found by duplicate matching is still updated as normal.
Can I prefill a form with existing Salesforce data?
Yes. Enable Prefill from Salesforce in a feed and map the Salesforce fields you want pulled in. For a public form, also tick Allow public prefill requests on front-end forms, otherwise prefill only runs for logged-in admins. Then send users a URL like ?sfid=001XXXXXXXXXXXX. Signed requests are required by default with a 15 minute expiry; use the built-in Signed Prefill Link Generator to create those URLs for email campaigns or self-service flows.
Does editing an entry update the Salesforce record?
Once you switch it on, yes. Entry updates, permanent deletes, restore-from-trash resyncs and entry-note syncing are each their own checkbox under Forms → Settings → Salesforce → Sync Settings, and all of them start off. Creating records needs no setup beyond saving a feed; anything that writes back to an existing record is opt-in on purpose, so the plugin never touches your org in ways you didn’t ask for.
What happens if I trash an entry, then restore it?
The Salesforce record is preserved through the trash/restore cycle. Only permanent deletion removes the Salesforce record, and only if you’ve enabled that option in plugin settings. This matches WordPress’s native behavior: trash is reversible, delete-forever is not.
Why does my entry show a blue “Pending” status?
That’s normal, and it’s usually gone by the time you refresh. Feeds process in the background so Salesforce API calls never hold up form submission. Just before it calls Salesforce, the background task marks the entry Pending, then overwrites that with success or an error as soon as Salesforce answers. A status stuck on Pending means the task started but never finished, usually a PHP timeout or fatal error mid-request, so enable Gravity Forms logging and check the add-on log. If a feed should have run but the entry shows no Salesforce status at all, the queue never ran it, which usually means the site blocks loopback requests to admin-ajax.php and WP-Cron, the fallback, is disabled.
Can I retry a failed sync?
Yes. Open the entry detail screen and click Re-send to Salesforce. It clears the side-effect dedup flags too, so notes, files, and campaign memberships retry along with the main record.
Does this work with payment add-ons (Stripe, PayPal, etc.)?
Yes. The payment add-on’s feed (Stripe, PayPal, and so on) gets a Post Payment Actions section with a “Process Salesforce feed only when payment is received.” checkbox that defers the Salesforce sync until the payment add-on confirms a successful transaction, so abandoned checkouts never leave half-baked Leads behind.
Does this support file uploads from Gravity Forms?
Yes. Gravity Forms file upload fields can be attached to the Salesforce record as ContentVersion / ContentDocumentLink, so they show up in the record’s Files related list. Configurable max file size, 1 to 25 MB per file, defaulting to 10.
Can I stop the plugin storing sync history?
Yes. Tick “Disable storing Salesforce entry logs (status/messages/sync history)” in plugin settings and no status, message or sync timestamp is written to wp_gf_entry_meta. Two things to know first: the Salesforce Logs page is built from that history, so it stops showing results and the entry sidebar drops to the record link and the Re-send button, and existing history is cleared when you enable the setting. Record IDs and account IDs are still stored, so merge tags, record links and Re-send keep working.
How do I customize the Salesforce payload programmatically?
Use the pb_gfsf_payload filter:
add_filter( 'pb_gfsf_payload', function ( $payload, $context ) {
// $context['feed'], ['entry'], ['form'], ['object'], ['account_id'], ['method']
return $payload;
}, 10, 2 );The full feed, entry, form, target object, account ID, and connection method (oauth or org_id) are available in $context so you can make decisions based on any of them.
What if my Salesforce org doesn’t have API access?
Use Org ID mode. It uses Salesforce’s legacy Web-to-Lead / Web-to-Case endpoint instead of the REST API. Limitations: only Lead and Case objects are supported, and you don’t get updates, deletes, file uploads, prefill, or picklist mapping. For most production setups, OAuth (the default) is the right choice.
What Salesforce OAuth policies does the plugin support?
The plugin uses OAuth 2.0 Authorization Code flow with PKCE and supports Refresh Token Rotation, so you can enable both on your Connected App or External Client App without breaking the connection. The only Refresh Token Policy that’s incompatible is “Immediately expire refresh token”, which forces a full re-authorization on every access token expiry and isn’t compatible with any unattended integration.
How does the OAuth connection stay logged in?
The plugin uses OAuth 2.0 with PKCE and refreshes the access token automatically using the refresh token. As long as the Connected App or External Client App’s refresh token policy doesn’t expire it (‘valid until revoked’, or a long fixed expiry such as the 365 days in our setup guide), the connection stays alive. When the refresh token does expire, you reconnect the account once. If it does drop, see the next question. It’s almost always a Connected App policy or scope setting. Refresh Token Rotation is supported, so security-conscious admins can enable that policy too.
The Salesforce connection keeps dropping and I have to reconnect. Why?
Almost always one of three things on the Salesforce side. The Connected App’s Refresh Token Policy is set to expire rather than “valid until revoked”. The app was scoped to “Full access (full)”, which never issues a refresh token. Or login IP ranges are blocking your server, in which case Salesforce refuses every refresh and reconnecting from the same server won’t help. From version 1.3.0 the Accounts screen names which one it is instead of showing a generic session error, and records when each account last refreshed, last succeeded and last failed.
Is the data deleted when I uninstall the plugin?
Only if you opt in. Enable “Delete all plugin data when uninstalling” in plugin settings before uninstalling. By default, your sync logs and connection settings are preserved (so you don’t lose history if you reactivate later).
Does it work with caching/CDNs?
Yes. The plugin runs entirely server-side at form submission, so caching and CDN setups don’t interfere with Salesforce syncing. The form-editor JS only loads inside the WordPress admin (where caching is typically disabled anyway).
Does this work with a WordPress Multisite network?
TL;DR
- Unlimited license: enable Network Mode once; one key can manage every subsite’s settings + support.
- Network Mode on: every subsite inherits the network key, so all of them can run Salesforce feeds and receive updates right away. Network-activating the plugin on its own does not license subsites.
- Limited plan / no subsite license: that subsite’s Salesforce settings are locked and its feeds log “Salesforce sync is unavailable until the plugin license is active” instead of syncing.
- Support follows the license: sites covered by the active key are eligible.
If you have an Unlimited license, you can enable Network Mode from the main site and push that single key to every subsite in one batch (with live progress). Those subsites inherit the license-managed settings and support automatically.
Hooks
Filters
pb_gfsf_payload
Filter the payload that’s about to be sent to Salesforce. This runs for every create and update across every feed, with the full context of the sync (feed config, entry, form, target object, account, and connection method). It fires after the field map and owner ID have been applied but before the API call, and returning an empty array is treated the same as a feed with no mapped fields.
Parameters:
$payload(array): the associative array of Salesforce field name → value about to be sent.$context(array): sync context. Keys:feed,entry,form,object,account_id,method(oauthfor an OAuth connection,org_idfor Web-to-Lead/Case).
Example: set a default LeadSource on every Lead.
add_filter( 'pb_gfsf_payload', function ( $payload, $context ) {
if ( $context['object'] === 'Lead' && ! isset( $payload['LeadSource'] ) ) {
$payload['LeadSource'] = 'WordPress Form';
}
return $payload;
}, 10, 2 );Example: route different feeds to different RecordTypes.
add_filter( 'pb_gfsf_payload', function ( $payload, $context ) {
$feed_id = (int) ( $context['feed']['id'] ?? 0 );
if ( $feed_id === 12 ) {
$payload['RecordTypeId'] = '0125g000000XYZAAA';
}
return $payload;
}, 10, 2 );Example: rewrite the LeadSource based on the form ID.
add_filter( 'pb_gfsf_payload', function ( $payload, $context ) {
$form_id = (int) ( $context['form']['id'] ?? 0 );
$payload['LeadSource'] = match ( $form_id ) {
7 => 'Webinar Signup',
12 => 'Demo Request',
default => 'Website',
};
return $payload;
}, 10, 2 );pb_gfsf_multi_value_delimiter
Change the separator used to join a Multi Select or Checkboxes field’s selections. Defaults to the semicolon that Salesforce multi-select picklists require. Override it when one of those fields is mapped to a plain text field that wants something else.
Parameters:
$delimiter(string): separator,;by default.$sf_field(string): Salesforce field API name being written.$raw_value(string): mapped Gravity Forms field ID or merge tag.$feed(array): Gravity Forms feed object.
add_filter( 'pb_gfsf_multi_value_delimiter', function ( $delimiter, $sf_field ) {
return 'Interests__c' === $sf_field ? ', ' : $delimiter;
}, 10, 2 );pb_gfsf_allowed_salesforce_domains
The add-on checks every login URL, instance URL and Web-to-Lead URL against Salesforce’s own domains before sending credentials or entry data to them. The default list is salesforce.com, force.com, cloudforce.com and salesforce.mil, matched on a dot boundary. Add to it if your org is reached on a custom Salesforce domain.
Parameters:
$domains(array): registrable suffixes, matched on a dot boundary.$url(string): the URL being checked.
add_filter( 'pb_gfsf_allowed_salesforce_domains', function ( $domains ) {
$domains[] = 'example-crm.com';
return $domains;
} );pb_gfsf_http_timeout
Change the timeout, in seconds, applied to every request to Salesforce. Defaults to 20. Sites on slow networks or sending large file uploads may need more; anything under a second or two will break normal operation.
add_filter( 'pb_gfsf_http_timeout', function () {
return 45;
} );







