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 LinkedIn with OpenClaw by implementing a standard OAuth 2.0 flow for user or organization authentication, storing tokens securely outside the agent runtime, implementing the LinkedIn REST calls (e.g., GET /v2/me, POST /v2/ugcPosts) inside a ClawHub-installed skill, and keeping any long-running or stateful components (token refresh, scheduled posting, webhook receivers) in external services that the skill calls or that call the skill via a well-authenticated API. Ensure you request the exact LinkedIn scopes you need (profile, email, w_member_social, w_organization_social), verify redirect URIs, validate and log HTTP responses, and design the skill so it only runs short-lived API calls — persist credentials and schedules in an external secrets store and database.
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=r_liteprofile%20r_emailaddress%20w_member\_social
curl -X POST https://www.linkedin.com/oauth/v2/accessToken \\
-d grant_type=authorization_code \\
-d code=AUTHORIZATION\_CODE \\
-d redirect_uri=YOUR_REDIRECT\_URI \\
-d client_id=YOUR_CLIENT\_ID \\
-d client_secret=YOUR_CLIENT\_SECRET
curl -H "Authorization: Bearer ACCESS\_TOKEN" \\
"https://api.linkedin.com/v2/me"
urn:li:person:<id>.curl -X POST https://api.linkedin.com/v2/ugcPosts \\
-H "Authorization: Bearer ACCESS\_TOKEN" \\
-H "X-Restli-Protocol-Version: 2.0.0" \\
-H "Content-Type: application/json" \\
-d '{
"author": "urn:li:person:PERSON\_ID",
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": {
"text": "Hello from OpenClaw-integrated skill"
},
"shareMediaCategory": "NONE"
}
},
"visibility": {
"com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
}
}'
urn:li:organization:ORG\_ID and ensure the authenticated user has admin permissions plus the appropriate scope.
const fetch = require('node-fetch');
const params = new URLSearchParams();
params.append('grant_type', 'authorization_code');
params.append('code', code); // code from redirect
params.append('redirect_uri', REDIRECT_URI);
params.append('client_id', CLIENT_ID);
params.append('client_secret', CLIENT_SECRET);
const res = await fetch('https://www.linkedin.com/oauth/v2/accessToken', {
method: 'POST',
body: params
});
const tokenBody = await res.json(); // contains access_token and expires_in
const postRes = await fetch('https://api.linkedin.com/v2/ugcPosts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'X-Restli-Protocol-Version': '2.0.0',
'Content-Type': 'application/json'
},
body: JSON.stringify({
author: `urn:li:person:${PERSON_ID}`,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: { text: 'Posting via OpenClaw skill' },
shareMediaCategory: 'NONE'
}
},
visibility: { 'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC' }
})
});
const postBody = await postRes.json(); // inspect for success or error
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
Direct answer: The LinkedIn invalid_redirect_uri means the redirect_uri parameter in your OAuth request does not exactly match a Redirect URL registered in your LinkedIn app. Fix by making the URL identical (scheme, host, path, port, trailing slash and percent‑encoding) in both your app settings and the value your OpenClaw skill/agent sends; ensure the skill's env var or ClawHub OAuth redirect setting is correct.
2
Short answer: most failures are connectivity, signature mismatch, or missing app secret in the agent/skill environment. Ensure your webhook URL is publicly reachable (HTTPS), Log raw headers/body, verify LinkedIn’s signature (HMAC-SHA256 using your app secret) before accepting, and configure the same secret as an env var in ClawHub/OpenClaw so the skill can validate requests.
// Node/Express example: verify X-LI-Signature
const crypto = require('crypto');
app.post('/webhook', express.raw({type:'application/json'}), (req,res)=>{
// compute HMAC-SHA256
const sig = 'sha256=' + crypto.createHmac('sha256', process.env.LINKEDIN_SECRET).update(req.body).digest('hex');
if (crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(req.headers['x-li-signature']||''))) {
// valid
res.sendStatus(200);
} else {
res.sendStatus(401);
}
});
3
Handle LinkedIn 429s by: honor Retry-After, apply exponential backoff with jitter, record per-credential rate state outside the OpenClaw runtime (Redis or DB), use a circuit-breaker to pause sync jobs and reschedule via your external queue, and make syncs idempotent and incremental so retries are safe.
// simple pattern
async function callLinkedIn(url, token, org) {
for(let i=0;i<5;i++){
const res = await fetch(url,{headers:{Authorization:`Bearer ${token}`}});
if(res.status!==429) return res;
const ra = res.headers.get('retry-after');
const wait = ra?parseInt(ra,10)*1000: Math.min(60000, (2**i)*1000);
await redis.set(`linkedin:cooldown:${org}`,1,'EX',Math.ceil(wait/1000));
await new Promise(r=>setTimeout(r, wait + Math.random()*500));
}
throw new Error('rate limited');
}
4
Direct answer: Use an explicit external mapping (database or key‑value store) that links each organization page ID to an OpenClaw account ID, and only allow changes when the executing agent/skill presents a token containing the rw_organization_admin permission. Validate scopes server-side, persist mappings outside the agent runtime, and keep credentials in env vars or a secrets store.
Practical pattern:
// Express webhook: check token scopes and update mapping
const jwt = require('jsonwebtoken')
// req.body.pageId, req.body.accountId
const token = process.env.OPENCLAW_TOKEN
const claims = jwt.decode(token) // // validate signature in real code
if (!claims.scopes || !claims.scopes.includes('rw_organization_admin')) throw new Error('missing permission')
await db.upsertMapping(req.body.pageId, req.body.accountId)
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.