Call the LinkedIn API with curl + jq. The user's bearer token is in
$LINKEDIN_TOKEN; every call needs Authorization: Bearer $LINKEDIN_TOKEN.
LinkedIn posts must be authored by the member's URN. Get the member id from the
OpenID userinfo endpoint, then build urn:li:person:{sub}.
SUB=$(curl -sS -H "Authorization: Bearer $LINKEDIN_TOKEN" \
"https://api.linkedin.com/v2/userinfo" | jq -r .sub)
AUTHOR="urn:li:person:$SUB"
echo "$AUTHOR"
Errors are JSON with message / serviceErrorCode — show them verbatim.
401 → token expired (tokens last ~60 days), re-connect the LinkedIn connector.
Publish a post (Posts API)
Confirm the text with the user first. Use the versioned Posts API; set the
LinkedIn-Version header to a recent YYYYMM (LinkedIn requires a valid recent
version — if you get 426/version errors, bump to the current month).
jq -n --arg a "$AUTHOR" --arg t "My update text. Check out https://studio.acedata.cloud" \
'{author:$a, commentary:$t, visibility:"PUBLIC",
distribution:{feedDistribution:"MAIN_FEED", targetEntities:[], thirdPartyDistributionChannels:[]},
lifecycleState:"PUBLISHED", isReshareDisabledByAuthor:false}' \
| curl -sS -X POST "https://api.linkedin.com/rest/posts" \
-H "Authorization: Bearer $LINKEDIN_TOKEN" \
-H "Content-Type: application/json" \
-H "LinkedIn-Version: 202401" \
-H "X-Restli-Protocol-Version: 2.0.0" \
-d @- -D - -o /dev/null | tr -d '\r' | awk '/^[Xx]-[Rr]estli-[Ii]d:|^[Ll]ocation:/{print}'
A successful create returns 201 with the post URN in the x-restli-id
(or Location) response header — report that URN / the resulting post URL.
Link posts: put the URL inline in
commentary(LinkedIn auto-unfurls it). Rich article attachments need the assets/images API — out of scope for a simple text/link share; keep links inline.
Fallback: legacy ugcPosts
If the versioned endpoint is unavailable for the app, the older
POST https://api.linkedin.com/v2/ugcPosts with a
specificContent."com.linkedin.ugc.ShareContent" body also works with
w_member_social. Prefer /rest/posts above.
Gotchas
- Author URN must match the token's member (
urn:li:person:{sub}) — you can't post as someone else. w_member_socialonly allows posting to the member's own feed; company Page posts needw_organization_social+ an admin role (not in this connector).- The
LinkedIn-Versionheader is mandatory for/rest/*; a stale value returns a version error — bump to the currentYYYYMM.
Record the output
After you successfully publish and obtain the live result URL, call the built-in
publish_artifact tool ONCE so the user can track this deliverable in My Outputs:
publish_artifact(kind="article", channel="linkedin", title="<title>", url="<the REAL returned URL>", status="delivered")
Use the real returned URL — never fabricate one. Call it once per published item,
only after delivery is confirmed; skip it (or use status="failed") if publishing failed.
See _shared/artifacts.md.