To integrate Replit with Salesforce, create an OAuth 2.0 Connected App in Salesforce Setup, store the Client ID and Client Secret in Replit Secrets (lock icon in sidebar), then use the Salesforce REST API with the JWT Bearer or Username-Password flow to authenticate and run SOQL queries. Deploy on a Reserved VM for stable callback URLs required by Salesforce OAuth.
Why Connect Replit to Salesforce?
Salesforce holds the most valuable data in most enterprises — leads, opportunities, contacts, accounts, and the entire history of customer relationships. By connecting your Replit application to Salesforce, you can build custom integrations that sync data between Salesforce and other systems, automate business workflows that Salesforce's native tools cannot handle, surface CRM data in custom dashboards, or create lightweight apps for sales teams that interact with Salesforce without requiring a full Salesforce license.
The integration runs entirely through the Salesforce REST API, which supports full CRUD operations on any Salesforce object. You can query records using SOQL (Salesforce Object Query Language — a SQL-like syntax tuned for CRM data), create and update leads or opportunities programmatically, and subscribe to real-time changes via outbound messages. Common use cases include syncing form submissions directly into Salesforce leads, building a custom reporting dashboard that pulls from multiple Salesforce objects, or creating a webhook bridge that forwards Salesforce events to Slack or other services.
Salesforce's OAuth 2.0 setup is more involved than most APIs due to the Connected App concept, the instance-based API URLs, and the strict callback URL requirements. This tutorial walks through every step, from configuring the Connected App to making your first authenticated SOQL query, using the Username-Password OAuth flow suitable for server-to-server integrations where you control both the Replit app and the Salesforce credentials.
Integration method
The Replit-Salesforce integration works by configuring a Connected App in Salesforce that issues OAuth 2.0 tokens, then calling the Salesforce REST API from your Replit server-side code. Your credentials (Consumer Key, Consumer Secret) are stored as Replit Secrets and never exposed to the browser. All Salesforce API calls — querying records with SOQL, creating leads, updating opportunities — are made from your backend code running on Replit's infrastructure.
Prerequisites
- A Salesforce Developer Edition or production org (free developer org at developer.salesforce.com)
- System Administrator access in Salesforce to create Connected Apps
- A Replit account with a Node.js or Python Repl ready
- Basic familiarity with OAuth 2.0 flows and REST APIs
- A deployed Replit app URL (required for OAuth callback — use Autoscale or Reserved VM deployment)
Step-by-step guide
Create a Salesforce Connected App
Create a Salesforce Connected App
A Connected App is the Salesforce equivalent of an OAuth client registration — it issues the credentials your Replit app uses to authenticate. Navigate to Salesforce Setup by clicking the gear icon in the top right, then search for 'App Manager' in the Quick Find box. Click 'New Connected App' in the top right corner. Fill in the required fields: Connected App Name (e.g., 'Replit Integration'), API Name (auto-fills), and your contact Email. Under 'API (Enable OAuth Settings)', check the 'Enable OAuth Settings' checkbox. In the Callback URL field, enter your Replit deployment URL followed by /oauth/callback — for example https://your-app.replit.app/oauth/callback. For development, you can temporarily use https://login.salesforce.com/services/oauth2/success as a placeholder. Under 'Selected OAuth Scopes', add at minimum: 'Manage user data via APIs (api)' and 'Perform requests at any time (refresh_token, offline_access)'. If you plan to use the Username-Password flow, also add 'Full access (full)'. Click Save — Salesforce warns that changes take 2-10 minutes to propagate. After saving, click 'Continue' and then 'Manage Consumer Details' to reveal the Consumer Key (Client ID) and Consumer Secret (Client Secret). Copy both values immediately — you will need them for Replit Secrets in the next step.
Pro tip: For server-to-server integrations where a human does not log in interactively, the Username-Password OAuth flow is the simplest approach. If you need multi-user OAuth where each user authorizes access to their own Salesforce data, implement the full Authorization Code flow with the callback URL handler.
Expected result: A Connected App appears in App Manager with a Consumer Key and Consumer Secret visible under Manage Consumer Details.
Store Salesforce Credentials in Replit Secrets
Store Salesforce Credentials in Replit Secrets
Click the lock icon (🔒) in the Replit sidebar to open the Secrets panel. Add each credential as a separate secret so they are AES-256 encrypted at rest and injected as environment variables at runtime. Never paste these values directly into your code files — Replit's Secret Scanner will detect them and warn you, but the safest practice is to go directly to Secrets first. Add the following secrets: - SF_CLIENT_ID — your Connected App Consumer Key - SF_CLIENT_SECRET — your Connected App Consumer Secret - SF_USERNAME — the Salesforce username you will authenticate as (e.g., your admin email) - SF_PASSWORD — the Salesforce password for that user - SF_SECURITY_TOKEN — the Salesforce security token for that user (find it via Salesforce Settings > My Personal Information > Reset My Security Token; a new token is emailed to you) - SF_LOGIN_URL — use https://login.salesforce.com for production or https://test.salesforce.com for sandbox The security token is required in addition to the password for the Username-Password OAuth flow unless your Replit deployment IP is whitelisted in Salesforce's 'Trusted IP Ranges'. Because Replit uses dynamic IPs, the security token approach is more reliable. After adding all secrets, click anywhere outside the Secrets panel to close it. Your secrets are now available via process.env.SF_CLIENT_ID (Node.js) or os.environ['SF_CLIENT_ID'] (Python).
Pro tip: Salesforce security tokens reset whenever you change your password. If authentication suddenly stops working after a password change, generate a new security token and update your SF_SECURITY_TOKEN secret in Replit.
Expected result: All six Salesforce secrets appear in the Replit Secrets panel with values hidden.
Implement OAuth 2.0 Authentication and Token Management
Implement OAuth 2.0 Authentication and Token Management
The Username-Password OAuth flow sends a POST request to the Salesforce token endpoint with your credentials, receiving back an access_token and instance_url. The instance_url is critical — it is the base URL for all subsequent API calls and varies per org (e.g., https://yourcompany.my.salesforce.com). Access tokens expire after a session timeout period, so implement token caching and automatic refresh. The Node.js implementation below uses the axios library for HTTP requests and includes in-memory token caching. For production, consider storing the token in a database or Redis with the expiry timestamp so it survives server restarts. Install the required package: in the Replit Shell, run: npm install axios
1// salesforce-auth.js — OAuth token management2const axios = require('axios');34let tokenCache = null;5let tokenExpiry = 0;67async function getSalesforceToken() {8 // Return cached token if still valid (with 5-minute buffer)9 if (tokenCache && Date.now() < tokenExpiry - 300000) {10 return tokenCache;11 }1213 const loginUrl = process.env.SF_LOGIN_URL;14 const params = new URLSearchParams({15 grant_type: 'password',16 client_id: process.env.SF_CLIENT_ID,17 client_secret: process.env.SF_CLIENT_SECRET,18 username: process.env.SF_USERNAME,19 // Append security token directly to password (no space)20 password: process.env.SF_PASSWORD + process.env.SF_SECURITY_TOKEN21 });2223 try {24 const response = await axios.post(25 `${loginUrl}/services/oauth2/token`,26 params.toString(),27 { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }28 );2930 tokenCache = {31 accessToken: response.data.access_token,32 instanceUrl: response.data.instance_url33 };34 // Salesforce tokens typically valid for 2 hours; cache for 90 minutes35 tokenExpiry = Date.now() + 90 * 60 * 1000;3637 console.log('Salesforce token acquired, instance:', tokenCache.instanceUrl);38 return tokenCache;39 } catch (error) {40 const msg = error.response?.data?.error_description || error.message;41 throw new Error(`Salesforce auth failed: ${msg}`);42 }43}4445module.exports = { getSalesforceToken };Pro tip: If you see 'invalid_grant: authentication failure' during testing, verify that SF_PASSWORD + SF_SECURITY_TOKEN are concatenated without a space. Also confirm the Salesforce user has the 'API Enabled' permission under their Profile settings.
Expected result: Calling getSalesforceToken() returns an object with accessToken and instanceUrl without throwing errors.
Query Salesforce Data with SOQL
Query Salesforce Data with SOQL
With authentication working, you can now run SOQL queries against any Salesforce object. SOQL is similar to SQL but works only on Salesforce data — it supports SELECT, FROM, WHERE, ORDER BY, LIMIT, and relationship traversal (e.g., Account.Name from Opportunity). All queries are URL-encoded and sent to the /services/data/vXX.0/query endpoint. The code below builds a complete Express server with endpoints for querying leads, querying opportunities, and creating a new lead. The Salesforce API version is pinned to v59.0 — check Salesforce Release Notes for the latest version, but avoid using the very latest until it has been available for one release cycle. Install Express if not already present: npm install express
1// server.js — Salesforce REST API integration2const express = require('express');3const axios = require('axios');4const { getSalesforceToken } = require('./salesforce-auth');56const app = express();7app.use(express.json());89const SF_API_VERSION = 'v59.0';1011// Helper: make authenticated Salesforce API call12async function sfRequest(method, path, data = null) {13 const { accessToken, instanceUrl } = await getSalesforceToken();14 const url = `${instanceUrl}/services/data/${SF_API_VERSION}${path}`;15 const config = {16 method,17 url,18 headers: {19 'Authorization': `Bearer ${accessToken}`,20 'Content-Type': 'application/json'21 }22 };23 if (data) config.data = data;24 const response = await axios(config);25 return response.data;26}2728// GET /leads — query recent leads with SOQL29app.get('/leads', async (req, res) => {30 try {31 const soql = `SELECT Id, FirstName, LastName, Email, Company, Status,32 CreatedDate FROM Lead33 WHERE CreatedDate = LAST_N_DAYS:3034 ORDER BY CreatedDate DESC35 LIMIT 50`;36 const result = await sfRequest('GET',37 `/query?q=${encodeURIComponent(soql)}`);38 res.json({39 total: result.totalSize,40 leads: result.records41 });42 } catch (err) {43 res.status(500).json({ error: err.message });44 }45});4647// GET /opportunities — open pipeline48app.get('/opportunities', async (req, res) => {49 try {50 const soql = `SELECT Id, Name, StageName, Amount, CloseDate,51 Account.Name FROM Opportunity52 WHERE IsClosed = false53 ORDER BY CloseDate ASC54 LIMIT 100`;55 const result = await sfRequest('GET',56 `/query?q=${encodeURIComponent(soql)}`);57 res.json({ opportunities: result.records });58 } catch (err) {59 res.status(500).json({ error: err.message });60 }61});6263// POST /leads — create a new Lead64app.post('/leads', async (req, res) => {65 try {66 const { firstName, lastName, email, company, phone } = req.body;67 if (!lastName || !email || !company) {68 return res.status(400).json({69 error: 'lastName, email, and company are required'70 });71 }72 const lead = {73 FirstName: firstName || '',74 LastName: lastName,75 Email: email,76 Company: company,77 Phone: phone || '',78 LeadSource: 'Web'79 };80 const result = await sfRequest('POST', '/sobjects/Lead', lead);81 res.json({ id: result.id, success: result.success });82 } catch (err) {83 res.status(500).json({ error: err.message });84 }85});8687app.listen(3000, '0.0.0.0', () => {88 console.log('Salesforce integration server running on port 3000');89});Pro tip: Salesforce SOQL queries are case-insensitive for keywords but case-sensitive for field names. Always use the exact API Name of fields (e.g., LastName not lastname). Find field API names in Salesforce Setup > Object Manager > [Object] > Fields & Relationships.
Expected result: GET /leads returns a JSON array of recent leads from Salesforce. GET /opportunities returns open pipeline records with account names.
Deploy and Configure Webhook Outbound Messages
Deploy and Configure Webhook Outbound Messages
For production use, deploy your Replit app to ensure it has a stable URL. Go to your Repl and click the Deploy button (cloud icon in the top right), then choose 'Autoscale' for apps that handle intermittent requests or 'Reserved VM' if you need guaranteed always-on uptime for real-time webhook processing. Copy your deployment URL (e.g., https://your-app.replit.app). To receive real-time notifications when Salesforce records change, configure an Outbound Message in Salesforce. Navigate to Setup > Process Automation > Workflow Rules, create a new Workflow Rule on the object you want to monitor (e.g., Opportunity), set evaluation criteria (e.g., 'every time a record is created or edited'), then add an action of type 'New Outbound Message'. Set the Endpoint URL to your deployment URL + /salesforce-webhook. Select the fields to include in the message. Alternatively, use Salesforce Flow or Apex Triggers for more complex scenarios. The outbound message sends an XML (SOAP) POST to your endpoint. The Flask handler below parses the XML and returns the required acknowledgment: Install the required packages: pip install flask requests
1# webhook_handler.py — Salesforce outbound message receiver2import os3from flask import Flask, request4import xml.etree.ElementTree as ET56app = Flask(__name__)78@app.route('/salesforce-webhook', methods=['POST'])9def salesforce_webhook():10 """Receive Salesforce outbound message (SOAP XML)"""11 try:12 body = request.data.decode('utf-8')13 # Parse the SOAP envelope14 root = ET.fromstring(body)1516 # Salesforce uses SOAP namespaces17 ns = {18 'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/',19 'sf': 'urn:sobject.enterprise.soap.sforce.com'20 }2122 # Extract notifications from the Body23 notifications = root.findall(24 './/soapenv:Body//Notification', ns25 )2627 for notif in notifications:28 # Extract object fields — adjust element names to match29 # the fields you selected in the Outbound Message config30 sf_id = notif.findtext('sObject/Id', namespaces=ns)31 name = notif.findtext('sObject/Name', namespaces=ns)32 stage = notif.findtext('sObject/StageName', namespaces=ns)33 print(f'Salesforce update: {sf_id} | {name} | {stage}')3435 # Add your business logic here:36 # - Forward to Slack37 # - Update external database38 # - Trigger another workflow3940 # Return required SOAP acknowledgment41 ack = '''<?xml version="1.0" encoding="UTF-8"?>42<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">43 <soapenv:Body>44 <notificationsResponse>45 <Ack>true</Ack>46 </notificationsResponse>47 </soapenv:Body>48</soapenv:Envelope>'''49 return ack, 200, {'Content-Type': 'text/xml'}5051 except Exception as e:52 print(f'Webhook error: {e}')53 # Return false Ack to trigger Salesforce retry54 return '<Ack>false</Ack>', 5005556if __name__ == '__main__':57 app.run(host='0.0.0.0', port=3000)Pro tip: If Salesforce reports that outbound messages are failing, check your Replit deployment logs for incoming requests. Salesforce retries outbound messages up to 24 hours with exponential backoff, so temporary downtime will not permanently lose events.
Expected result: Salesforce successfully delivers test outbound messages to your Replit deployment URL, and the webhook handler logs the parsed record data.
Common use cases
Lead Capture Pipeline
Capture leads from a custom web form on your Replit app and push them directly into Salesforce as new Lead records, complete with source tracking, UTM parameters, and auto-assignment rules. New leads appear in Salesforce in real time without manual data entry.
Build an Express server with a /submit-lead POST endpoint. When called with name, email, company, and phone fields, it should authenticate with Salesforce using OAuth 2.0 and create a new Lead record via the Salesforce REST API. Store credentials in environment variables.
Copy this prompt to try it in Replit
Custom CRM Dashboard
Build an internal dashboard that runs SOQL queries against Salesforce to display open opportunities by stage, recent activities, and quota attainment — all in a format tailored to your team's workflow rather than the default Salesforce UI.
Create a Node.js backend with endpoints that run SOQL queries: one for open Opportunities grouped by StageName, one for today's Tasks, and one for Account details by ID. Return JSON responses to a React frontend that renders the data as cards and charts.
Copy this prompt to try it in Replit
Salesforce-to-Slack Webhook Bridge
Receive Salesforce outbound messages when records change (e.g., a deal moves to Closed Won) and forward a formatted notification to a Slack channel, keeping the sales team informed without polling Salesforce manually.
Build a Flask server that receives Salesforce outbound message XML POSTs at /salesforce-webhook, parses the SOAP envelope to extract the Opportunity name and Stage, then forwards a formatted message to a Slack webhook URL. Return a valid SOAP acknowledgment response.
Copy this prompt to try it in Replit
Troubleshooting
Error: 'invalid_grant: authentication failure' when requesting OAuth token
Cause: The most common causes are: the password and security token are not concatenated correctly (they must be joined with no space as password+token), the Salesforce user does not have API access enabled in their Profile, or the Connected App has not finished propagating (can take up to 10 minutes after creation).
Solution: Verify that SF_PASSWORD and SF_SECURITY_TOKEN are stored as separate secrets and that your code concatenates them as process.env.SF_PASSWORD + process.env.SF_SECURITY_TOKEN with no separator. Check the user's Profile in Salesforce Setup to ensure 'API Enabled' is checked. If recently created, wait 10 minutes and retry.
1// Correct concatenation — no space between password and token2password: process.env.SF_PASSWORD + process.env.SF_SECURITY_TOKENSOQL query returns 'INVALID_FIELD' or 'No such column' error
Cause: The field name in your SOQL query does not match the API Name of the field in Salesforce. Field API Names are case-sensitive and may differ from the label shown in the UI (e.g., 'Phone' is the API name but 'Business Phone' is the label, or custom fields end with __c like Custom_Field__c).
Solution: Find the exact API Name by going to Salesforce Setup > Object Manager > [Object Name] > Fields & Relationships. The 'Field Name' column shows the API name. Custom fields always end in __c. Update your SOQL query to use the exact API names.
Connected App callback URL mismatch error during OAuth authorization code flow
Cause: If you are implementing the full Authorization Code flow (for multi-user OAuth), the callback URL in the OAuth request must exactly match one of the callback URLs registered in the Connected App, including protocol (https), path, and trailing slash.
Solution: In Salesforce Setup > App Manager > your Connected App > Manage Consumer Details, verify the Callback URLs list includes your exact deployment URL. Update the Replit Deployment URL if you redeployed to a new URL. Remember Salesforce takes 2-10 minutes to propagate Connected App changes.
API requests return 401 after working previously
Cause: Salesforce access tokens expire based on the session timeout configured in your org (default 2 hours for most orgs, but can be set as short as 15 minutes). If your token cache does not handle expiry correctly, tokens will fail after the timeout period.
Solution: Implement token refresh logic that checks expiry before each request. The getSalesforceToken() function in this tutorial uses a 90-minute cache with a 5-minute buffer. Ensure your tokenExpiry timestamp is set correctly using Date.now() in milliseconds, not seconds.
1// Check if token expires within next 5 minutes (300,000 ms)2if (tokenCache && Date.now() < tokenExpiry - 300000) {3 return tokenCache; // Still valid4}5// Otherwise re-authenticateBest practices
- Always use the Username-Password OAuth flow for server-to-server integrations where a human does not need to interactively authorize — it is simpler and more reliable than maintaining authorization code flow state in a Replit deployment.
- Cache Salesforce access tokens with a TTL slightly shorter than the org's session timeout (default 2 hours). Re-authenticating on every request significantly slows your application and may hit Connected App rate limits.
- Use the Salesforce Composite API (/services/data/vXX.0/composite) to batch multiple API calls into a single HTTP request when creating or updating multiple related records — this reduces latency and stays within API call limits.
- Store the SF_LOGIN_URL secret as either https://login.salesforce.com (production) or https://test.salesforce.com (sandbox) so you can switch environments without changing code.
- Build and test all SOQL queries in the Salesforce Developer Console (Setup > Developer Console > Query Editor) before adding them to your Replit code — this lets you iterate on queries without deploying code changes.
- Deploy to a Reserved VM if processing Salesforce outbound messages — Autoscale deployments that scale to zero may miss messages during cold-start periods, and while Salesforce retries, delays can cause workflow issues.
- Monitor your Salesforce API call usage under Setup > Administration > Company Information > API Requests Used This Month — the default Developer Edition limit is 15,000 requests per 24 hours.
- Use Salesforce bulk API endpoints (/services/async/) when processing more than 2,000 records at once — the standard REST API has a 2,000-record limit per SOQL query result set.
Alternatives
HubSpot offers a free CRM tier with a simpler REST API and no OAuth Connected App setup, making it a better starting point for startups that do not already use Salesforce.
Zoho CRM is significantly cheaper than Salesforce and provides a similar REST API with OAuth 2.0, making it a cost-effective alternative for growing businesses.
Pipedrive focuses on sales pipeline management with a simpler API key authentication model that takes minutes to set up compared to Salesforce's Connected App process.
Dynamics 365 integrates natively with Microsoft Azure and Office 365, making it the better choice for organizations already deep in the Microsoft ecosystem.
Frequently asked questions
How do I connect Replit to Salesforce without exposing my credentials?
Store all Salesforce credentials — Client ID, Client Secret, username, password, and security token — in Replit Secrets (lock icon in the sidebar). Secrets are AES-256 encrypted, never appear in your code or Git history, and are injected as environment variables at runtime. Access them via process.env.SF_CLIENT_ID in Node.js or os.environ['SF_CLIENT_ID'] in Python.
Does Replit work with Salesforce sandbox environments?
Yes. Set your SF_LOGIN_URL secret to https://test.salesforce.com for sandbox environments instead of https://login.salesforce.com for production. All other configuration remains the same. This makes it easy to develop and test against sandbox data before pointing to production.
Why does my Salesforce API call fail with 'REQUEST_LIMIT_EXCEEDED'?
Salesforce enforces API call limits per 24-hour period — Developer Edition orgs get 15,000 calls/day, and limits scale with enterprise licenses. To avoid hitting limits, implement token caching (so you do not re-authenticate on every request), use the Bulk API for large data operations, and cache SOQL query results in your Replit app for data that does not change frequently.
Can I use Replit to receive Salesforce webhooks?
Yes, but you must use a deployed Replit app (not the development URL) because the development URL only works while your browser is open. Deploy using Autoscale or Reserved VM to get a stable https://your-app.replit.app URL. Configure Salesforce outbound messages or Flow to POST to that URL. Reserved VM is recommended for webhook processing that must never miss an event.
What is the Salesforce security token and do I always need it?
The security token is an extra credential required for the Username-Password OAuth flow when logging in from an IP address not in your org's 'Trusted IP Ranges'. Because Replit uses dynamic IP addresses that change on every restart, you will almost always need the security token. Generate it in Salesforce via Settings > My Personal Information > Reset My Security Token — it is emailed to your Salesforce login address.
Can I use Replit with Salesforce for free?
Salesforce offers a free Developer Edition org at developer.salesforce.com with full API access and 15,000 API calls per day. This is sufficient for development and testing. For production use, you need a paid Salesforce license. Replit's free tier works for development, but deploy on a paid plan for production webhook reliability.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation