Social Media Action

One node, every supported social platform. Picks the platform from a registry and dispatches universal operations (post, reply, repost, like, search, …) plus platform-specific extras. Use the crosspost sentinel to fan out to multiple platforms in one node, or pair with the Trigger node to listen for new posts across platforms in a single trigger.

Supported providers

PlatformCredentialAuth modelNotes
Blueskybluesky_app_passwordApp passwordNo platform-side review. Cardyb-backed link card embeds for richer posts.
X (Twitter)twitter_oauth2OAuth 2.0 + PKCE280-weighted-char cap with smart-truncate / auto-thread escape hatches (see below).
Mastodonmastodon_oauth2OAuth 2.0 (per-instance app reg)Per-instance app registered dynamically on first connect. 500-char cap.
LinkedInlinkedin_oauth2OAuth 2.0 (UGC API)Personal posts only in V1. postWithMedia + searchPosts gated.
Threadsthreads_oauth2Meta Graph + long-lived tokenTwo-step token (short → long-lived 60d). Daily refresh cron. Single + carousel media (10 max).
Instagraminstagram_oauth2Facebook Login + IG GraphBusiness / Creator account linked to a FB Page. 2200-char captions. Single + carousel up to 10 (image + video). All posts require media — no text-only.
Facebook Pagesfacebook_oauth2Facebook Login + Page tokensPosts to a Page you administer (V1 picks first page). Text + single image; multi-image albums + video deferred.
Meta app setup (Instagram + Facebook): ONE app at developers.facebook.com/apps covers both platforms. Add products: Facebook Login for Business + Instagram Graph API. Set env vars META_OAUTH_CLIENT_ID + META_OAUTH_CLIENT_SECRET. Add valid OAuth redirect URIs: {API_URL}/api/credentials/instagram/oauth/callback + {API_URL}/api/credentials/facebook/oauth/callback.

Operation gates

Most universal operations work on every provider. A few are gated by the platform itself — either behind a paid partnership tier or behind a scope that needs review by the platform's team. The provider throws a clear error when you try to run a gated op so the AI Toolkit and the canvas surface the gate without a silent failure.

searchPosts

Recent-search requires special access on LinkedIn and Threads. The other three providers (Bluesky, X, Mastodon) ship it under their regular auth.

PlatformsearchPostsWhat unlocks it
BlueskyAvailablePublic AT-Protocol search.
X (Twitter)AvailableFree tier ≈ 100 reads/day. Basic ($200/mo) lifts the cap. Hit /2/tweets/search/recent internally.
MastodonAvailablePer-instance search via the standard API.
LinkedInGatedRequires LinkedIn Marketing Developer Platform (MDP) partnership. Apply at linkedin.com/developers. Approval can take weeks to months and is granted to verified marketing/analytics use cases — not casual integrations.
ThreadsGatedRequires the threads_keyword_search scope, which is a premium scope under Meta review. Only granted for approved business / research use cases. Apply via the Threads / Meta App Review flow.
InstagramGatedHashtag search via /ig_hashtag_search needs additional Meta App Review + heavy rate-limit gates. Mentions surface is Webhooks-only push, not pull — different shape entirely. Both deferred until customer demand.
Facebook PagesGatedPage-graph search needs Pages Public Content Access permission, which goes through Meta App Review for any non-developer use. Mentions same gate.
If you try to run searchPosts on a gated platform without the partnership, the provider throws an explicit error explaining what to apply for — not a silent empty result.

postWithMedia

All five providers ship media uploads. Per-platform notes:

  • Bluesky — up to 4 images, 1 MB each, blob upload at post time.
  • X (Twitter) — up to 4 images, 5 MB each, single-shot upload via /2/media/upload.
  • Mastodon — up to 4 attachments via the per-instance /api/v2/media webhook.
  • LinkedIn — IMAGE only in V1, up to 9 images per UGC post, 5 MB each. Uses the 3-step registerUpload dance (register → PUT binary → reference asset URN in the UGC post). VIDEO support deferred — throws a clear early error when a video URL is detected so users aren't left waiting on a confusing LinkedIn rejection.
  • Threads — async container + publish flow with status polling. Single image/video, or CAROUSEL up to 10 mixed image/video children.
  • Instagram — same container + publish flow as Threads. Single image/video or CAROUSEL (10 mixed). All posts require media; no text-only. 2200-char captions.
  • Facebook Pages — V1 ships text + single image only. Multi-image albums (create-then-publish dance) + video (chunked resumable upload) deferred to a follow-up.

X (Twitter) long-text strategies

X enforces a 280-weighted-character cap on Free / Basic / Pro tiers (URLs count as 23 after t.co shortening; emoji as 2). The default rejects posts that exceed the cap at validation time. Switch the “When text exceeds 280 chars” selector under any text-posting op (postText, replyToPost, quoteTweet, postWithMedia) to opt into one of two escape hatches:

  • Smart truncate — trims your text at the last word boundary, appends an ellipsis, and (if you set truncateAppendUrl) reserves room for a permalink at the end. URL is counted as 23 weighted chars regardless of its raw length, so even a 200-char URL only steals 23 from your budget.
  • Auto-thread — splits the text into a chain of replies, posting each ≤ 280-weighted chunk via in_reply_to_tweet_id. Cuts on paragraph (\n\n) → sentence (. ! ?) → word boundaries in that priority order. With threadNumbering (default ON) each chunk gets a (i/N) suffix so readers see the order. The lead tweet's id surfaces at the top of the output (postId, url) for back-compat; the full chain lives in posts[] with _meta.iterable: true for downstream loops.
jsonSample auto-thread output
{
  "platform": "twitter",
  "postedAt": "2026-05-10T14:23:11.582Z",
  "postId": "1791000000000000001",          // lead tweet
  "url": "https://x.com/i/status/1791000000000000001",
  "text": "First chunk … (1/3)",
  "threadCount": 3,
  "_meta": { "iterable": true, "iterateField": "posts", "count": 3 },
  "posts": [
    { "threadIndex": 1, "postId": "1791000000000000001", "url": "...", "text": "First chunk … (1/3)" },
    { "threadIndex": 2, "postId": "1791000000000000002", "url": "...", "text": "Second chunk … (2/3)" },
    { "threadIndex": 3, "postId": "1791000000000000003", "url": "...", "text": "Third chunk (3/3)" }
  ]
}
For postWithMedia + auto-thread, media attaches to the first tweet only — subsequent reply chunks are text-only. Matches what readers expect when scrolling a thread on X.

  • Crosspost — set platform: "crosspost" and supply a credentialIds map + operationConfig.targetPlatforms[] to fan out a single text/media post to N platforms in parallel. Each result lands in output.results[] with per-platform success + error fields so downstream nodes can iterate.
  • Trigger — to listen for new posts across multiple platforms (Bluesky, X, Mastodon today) in a single trigger, see the multi-platform poll configuration.