How to Integrate Salesforce with Your Headless CMS
Connect Salesforce to structured content so sales teams, campaigns, product pages, and customer-facing experiences stay in sync without copy-paste work.
What is Salesforce?
Salesforce is a CRM platform used by sales, service, marketing, and operations teams to track accounts, contacts, leads, opportunities, campaigns, and customer activity. Its core strength is keeping customer and revenue data in one place, with APIs that let teams connect CRM records to websites, apps, sales workflows, and reporting tools. It’s one of the most widely used CRM platforms for companies that need configurable sales processes, automation, and integrations across large teams.
Why integrate Salesforce with a headless CMS?
Salesforce often knows who the customer is, what stage they’re in, which products they care about, and which campaign touched them last. Your content system usually knows the approved product copy, campaign messaging, sales enablement assets, localized descriptions, and publish status. When those two systems don’t talk, sales reps paste outdated copy into opportunities, campaign teams re-enter launch details, and customer experiences drift away from what’s approved.
Architecture overview
A typical Salesforce integration starts when an editor publishes content in Sanity Studio. A webhook fires on the publish mutation, or a Sanity Function runs server-side on the same content event. The handler receives the document ID, uses @sanity/client to run a GROQ query against the Content Lake, and projects only the fields Salesforce needs, such as title, slug, campaign code, audience, product SKU, publish URL, and Salesforce external ID. The handler then authenticates to Salesforce with a Connected App and calls the Salesforce REST API through jsforce. For one record, you might upsert a Campaign, Product2, or custom object using an external ID field like Sanity_Id__c. For larger backfills, Bulk API 2.0 is a better fit. After Salesforce is updated, sales reps see current content metadata on the related CRM record, marketers can report on content-linked campaigns, and your frontend can still read the same approved content directly from Sanity for web, mobile, and AI agent experiences.
Common use cases
Campaign sync
Create or update Salesforce Campaign records when launch pages, gated assets, or nurture content are published in Sanity.
Product content for Sales Cloud
Sync approved product names, SKUs, descriptions, and launch status from Sanity to Salesforce Product2 or custom product objects.
Sales enablement on opportunities
Attach current case studies, one-pagers, and pitch assets to Salesforce accounts or opportunities based on industry, region, or product interest.
Content-attributed pipeline reporting
Send content IDs, campaign codes, and asset URLs into Salesforce so teams can report on which content is tied to leads, contacts, and opportunities.
Step-by-step integration
- 1
Set up Salesforce API access
Create a Salesforce Connected App in Setup, enable OAuth settings, and add scopes such as api and refresh_token, offline_access. Copy the Consumer Key and Consumer Secret. For server-side scripts, use OAuth refresh tokens, JWT bearer flow, or username plus security token depending on your company’s security policy.
- 2
Create Salesforce fields for Sanity IDs
Add an external ID field such as Sanity_Id__c to the target Salesforce object, for example Campaign, Product2, or a custom Content_Asset__c object. This lets your integration upsert records instead of creating duplicates on every publish.
- 3
Model the content in Sanity Studio
Define schema fields that match the CRM use case. For a campaign, include title, slug, campaignCode, audience, launchDate, status, heroAsset, relatedProducts, and salesforceCampaignId or Sanity_Id__c. Keep CRM-owned fields, such as pipeline stage or campaign member count, out of Sanity.
- 4
Create the sync trigger
Use a Sanity webhook filtered to published campaign, product, or asset documents, or use a Sanity Function to run server-side logic on content mutations. Configure the trigger to send the document ID and dataset so your handler can fetch the latest published document.
- 5
Call the Salesforce API
Install jsforce with npm install jsforce @sanity/client. In the handler, authenticate to Salesforce, fetch the Sanity document with GROQ, map fields to the Salesforce object, and call upsert with the external ID field. Use Composite API when one publish needs to update related records, and use Bulk API 2.0 for large migrations.
- 6
Test the end-to-end experience
Publish a draft in Sanity Studio, confirm the webhook or Function ran, inspect the Salesforce record, and verify that the frontend still renders from Sanity. Test updates, deletes, unpublished documents, required Salesforce fields, and permission failures before giving the workflow to editors.
Code example
Minimal TypeScript webhook handler that receives a Sanity publish event, fetches the document with GROQ, and upserts a Salesforce Campaign with jsforce.
import {createClient} from '@sanity/client'
import {Connection} from 'jsforce'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_READ_TOKEN!,
useCdn: false
})
export async function POST(req: Request) {
const event = await req.json()
const id = event.ids?.created?.[0] || event.ids?.updated?.[0]
if (!id) return Response.json({ok: true, skipped: true})
const campaign = await sanity.fetch(`
*[_id == $id][0]{
_id,
title,
"url": "https://example.com/campaigns/" + slug.current,
campaignCode,
launchDate,
audience
}
`, {id})
if (!campaign) return Response.json({ok: false}, {status: 404})
const sf = new Connection({loginUrl: process.env.SF_LOGIN_URL})
await sf.login(
process.env.SF_USERNAME!,
process.env.SF_PASSWORD! + process.env.SF_SECURITY_TOKEN!
)
const result = await sf.sobject('Campaign').upsert({
Sanity_Id__c: campaign._id,
Name: campaign.title,
Campaign_Code__c: campaign.campaignCode,
Landing_Page_URL__c: campaign.url,
Audience__c: campaign.audience,
StartDate: campaign.launchDate,
Status: 'In Progress'
}, 'Sanity_Id__c')
return Response.json({ok: result.success, id: result.id})
}How Sanity + Salesforce works
Build your Salesforce integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect approved content with Salesforce records and sales workflows.
Start building free →CMS approaches to Salesforce
| Capability | Traditional CMS | Sanity |
|---|---|---|
| CRM-ready content structure | Content is often page-shaped, so Salesforce syncs may need HTML parsing, manual field mapping, or extra cleanup. | Content is typed JSON in the Content Lake, with schema-as-code for fields like campaignCode, SKU, audience, region, and Salesforce external ID. |
| Real-time Salesforce updates | Teams often rely on scheduled exports, plugins, or manual updates after publish. | Webhooks trigger on content mutations, and Functions can run sync logic without separate infrastructure. |
| Field-level control for Salesforce payloads | Integrations may receive full pages or plugin-defined payloads, which adds mapping work. | GROQ can select exactly the fields Salesforce needs and join referenced products, industries, assets, and locales in one query. |
| Sales campaign operations | Campaign content and Salesforce Campaign records are often updated in separate workflows. | Editors can publish campaign content in Sanity Studio, then a webhook or Function can upsert the matching Salesforce Campaign using Sanity_Id__c. |
| Multi-channel content reuse | Content is commonly tied to web pages, which makes reuse in Salesforce, mobile apps, and AI agents harder. | One structured back end can feed websites, apps, Salesforce, and AI agents through APIs, GROQ, and Agent Context. |
| Operational trade-offs | Lower setup effort if you only need a plugin and can accept limited data control. | Best when you want schema control, real-time events, and custom workflows. You’ll still need to design ownership rules between Sanity and Salesforce. |
Keep building
Explore related integrations to complete your content stack.
Sanity + HubSpot CRM
Connect structured campaign and content data with HubSpot contacts, lists, forms, and nurture workflows.
Sanity + Gong
Tie approved messaging, battlecards, and enablement content to call insights and sales coaching workflows.
Sanity + Outreach
Keep sales sequences connected to approved product copy, case studies, and persona-specific messaging.