arizukocomponents › linkd

linkd

What it is

linkd is the LinkedIn channel adapter. It targets the LinkedIn v2 community-management API for the bot account’s own shares and comments — UGC posts, likes, reshares, comments, deletes. JID prefix is linkedin:.

Why it exists

LinkedIn DMs and company-page posting are gated behind partner-only OAuth scopes that arizuko deployments cannot get. linkd carves out the subset that works under w_member_social — the agent’s own UGC posts and the social actions on them — and returns structured hints for the rest so the agent has a concrete alternative instead of a silent failure.

How it fits

LinkedIn (api.linkedin.com)
        |  poll: own UGC shares + comments, every LINKEDIN_POLL_INTERVAL
        v
      linkd     (LISTEN_ADDR=:8080)
        |  POST /v1/messages    (signed by CHANNEL_SECRET)
        v
      gated
        |  POST /v1/send         (callback to linkd)
        v
      linkd     (REST: /v2/ugcPosts | /v2/socialActions/{urn}/...)
        |
        v
      LinkedIn

Wired verbs (linkd/client.go): send / reply as comments on a post URN (POST /v2/socialActions/{urn}/comments); when LINKEDIN_AUTO_PUBLISH=true, send with a non-comment JID falls back to publishing a ugcPost. post (/v2/ugcPosts), like (/v2/socialActions/{urn}/likes), delete (DELETE /v2/ugcPosts/{urn} for own posts), repost (/v2/ugcPosts with referenceUgcPost), fetch_history (comments on a post URN). send_file, forward, quote, dislike, edit are 501-with-hint.

The edit hint exists because UGC edit requires the versioned /rest/posts PARTIAL_UPDATE flow, which is not wired. send_file needs the /assets registerUpload + binary PUT dance; same story.

Standalone usage

linkd is a plain Go binary. It needs a reachable gated and a LinkedIn application with member-social scopes. linkd refreshes the access token automatically on 401 via the refresh token.

export ROUTER_URL=http://gated:8080
export CHANNEL_SECRET=$(grep ^CHANNEL_SECRET .env | cut -d= -f2)
export LISTEN_ADDR=:8080
export LINKEDIN_CLIENT_ID=...
export LINKEDIN_CLIENT_SECRET=...
export LINKEDIN_ACCESS_TOKEN=...
export LINKEDIN_REFRESH_TOKEN=...
export LINKEDIN_POLL_INTERVAL=300s
export LINKEDIN_AUTO_PUBLISH=false
./linkd

LinkedIn-side setup: register an app at linkedin.com/developers. Required scopes: r_liteprofile, w_member_social. Walk through the OAuth code flow once to mint the initial access + refresh tokens; copy them into env. Company-page posting and DM messaging both need partner approval and are intentionally out of scope.

GET /health returns 503 when the access token is expired and refresh fails.

Go deeper