We build custom applications 5x faster and cheaper 🚀
Book a Free Consultation
Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.
Integrate Salesforce with OpenClaw by: (1) creating a Salesforce Connected App (OAuth client) with the right scopes or a JWT certificate for server-to-server; (2) installing/configuring an OpenClaw skill in ClawHub and storing the Salesforce credentials (client id/secret, private key, refresh token, integration username, instance URL) in secrets/environment variables; (3) implementing the skill’s runtime code to perform OAuth token exchange (JWT or authorization-code/refresh flow), call Salesforce REST APIs using the returned access token, and handle errors/refreshes; and (4) routing Salesforce push events (Outbound Messages, Platform Events, Change Data Capture/Streaming) to an external, durable webhook or worker (not inside ephemeral agent runtime) that validates requests and invokes the OpenClaw skill through its configured entrypoint. Follow least-privilege permissions, use a dedicated integration user or scoped Connected App, and debug by inspecting token/API responses, ClawHub and skill logs, and Salesforce connected-app settings and user permissions.
api, refresh\_token or equivalent). Use a dedicated integration user when appropriate.
SF_CLIENT_ID, SF_CLIENT_SECRET (if using auth code), SF_PRIVATE_KEY (for JWT), SF_USERNAME (for JWT subject), and optionally SF_INSTANCE\_URL if you need to override sandbox vs production.
// Dependencies: npm install jsonwebtoken node-fetch
const jwt = require('jsonwebtoken');
const fetch = require('node-fetch');
const SF_CLIENT_ID = process.env.SF_CLIENT_ID; // Connected App Consumer Key
const SF_USERNAME = process.env.SF_USERNAME; // Salesforce integration username (sub)
const SF_PRIVATE_KEY = process.env.SF_PRIVATE_KEY; // PEM string, keep in secret storage
const SF_LOGIN_AUD = 'https://login.salesforce.com'; // or https://test.salesforce.com for sandbox
const SF_TOKEN_URL = `${SF_LOGIN_AUD}/services/oauth2/token`;
async function getAccessTokenWithJWT() {
const now = Math.floor(Date.now() / 1000);
const payload = {
iss: SF_CLIENT_ID, // issuer = client id
sub: SF_USERNAME, // subject = username of integration user
aud: SF_LOGIN_AUD, // audience = login URL
exp: now + 3 * 60 // short expiry, e.g., 3 minutes
};
// // Sign the JWT with RS256 using your private key
const assertion = jwt.sign(payload, SF_PRIVATE_KEY, { algorithm: 'RS256' });
// // Exchange assertion for token
const params = new URLSearchParams();
params.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
params.append('assertion', assertion);
const res = await fetch(SF_TOKEN_URL, { method: 'POST', body: params });
if (!res.ok) {
const text = await res.text();
throw new Error(`Token endpoint error: ${res.status} ${text}`);
}
const tokenResponse = await res.json();
// tokenResponse.access_token and tokenResponse.instance_url are returned
return tokenResponse;
}
// // Use instance_url returned from token response and the access_token
async function queryAccounts(accessToken, instanceUrl) {
// // Replace vXX.X with the API version you target, or use the latest supported
const apiVersion = 'vXX.X';
const q = encodeURIComponent('SELECT Id, Name FROM Account LIMIT 5');
const url = `${instanceUrl}/services/data/${apiVersion}/query?q=${q}`;
const res = await fetch(url, {
headers: { 'Authorization': `Bearer ${accessToken}`, 'Accept': 'application/json' }
});
if (!res.ok) {
const body = await res.text();
throw new Error(`API error: ${res.status} ${body}`);
}
return res.json();
}
instance\_url for API calls. If you hardcode production vs sandbox endpoints, you can call the wrong host.login.salesforce.com and the Salesforce instance host; check proxies and firewall rules.
Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.
1
The short answer: invalid_grant means Salesforce rejected the authorization code grant—most commonly because the code expired or was already used, the redirect_uri in the token request doesn’t exactly match the one used during authorization, client credentials are wrong, or you hit the wrong Salesforce token endpoint (sandbox vs production).
// Exchange code for token (Node fetch)
const res = await fetch('https://login.salesforce.com/services/oauth2/token', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: AUTH_CODE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI
})
});
const body = await res.json(); // inspect body.error_description for details
2
OpenClaw Sync Jobs hit REQUEST_LIMIT_EXCEEDED or API limit errors because bulk upserts generate more API calls or concurrent work than Salesforce allows — either per-minute REST calls, concurrent Bulk API jobs, or org-wide daily limits — and the agent/skill isn't throttling or batching requests to respect those limits.
Salesforce enforces per‑org and per‑user API limits and concurrency rules; bursty upserts, parallel jobs, or small batches multiply calls. OpenClaw skills run in the agent runtime and will exceed limits if they send many concurrent requests without client-side throttling, batching, or using the correct Bulk API.
3
Direct answer: Custom Salesforce fields usually don't appear in OpenClaw Mapping Rules because the Salesforce API describe call returned no access to them for the integration user or OpenClaw is still using a cached schema. Common root causes are field‑level security / permission sets, wrong API name or namespace, the field not deployed in that org, or the schema refresh failed/was cached.
4
Short answer: missing events and replayId gaps happen because Salesforce only retains platform events for a limited replay window and because disconnects, reconnections, subscription scope/permissions, or API limits can cause you to ask for a replayId that Salesforce no longer has — producing gaps. Network blips, broker failover, or filtering can also make sequences appear non‑contiguous.
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â