---
title: "Social Publish"
description: "Publish a caption, with media or as text alone, to one or more connected social channels. Media is optional on Threads, Bluesky and Mastodon; Instagram, YouTube, LinkedIn and TikTok refuse a post without it. Posts go out at the time you set; posting cadence (min interval, max/day) and caption variation are checked and reported as warnings."
updated: 2026-10-03
---

# Social Publish

**Node type:** `action:social_publish`  
**Category:** `Integrations`

## Description

Publish a caption, with media or as text alone, to one or more connected social channels. Media is optional on Threads, Bluesky and Mastodon; Instagram, YouTube, LinkedIn and TikTok refuse a post without it. Posts go out at the time you set; posting cadence (min interval, max/day) and caption variation are checked and reported as warnings.

## Canvas ports

These appear as port handles on the left side of the node.

| ID        | Label       | Details                |
| --------- | ----------- | ---------------------- |
| `media`   | **Media**   | `UNKNOWN` _(required)_ |
| `caption` | **Caption** | `TEXT` _(required)_    |

## Sidebar config

These render as form fields in the right-side config panel when the node is selected.

_No sidebar config fields._

## Outputs

| ID           | Label           | Type    |
| ------------ | --------------- | ------- |
| `status`     | **Status**      | `TEXT`  |
| `posts`      | **Posts**       | `ARRAY` |
| `firstError` | **First Error** | `TEXT`  |

## Platform options

Pick accounts in the config panel, then set options per platform. Each
platform only shows the options it actually supports.

| Platform                   | Options                                                                            |
| -------------------------- | ---------------------------------------------------------------------------------- |
| LinkedIn                   | First comment, alt text for images                                                 |
| Instagram                  | First comment (plus the Instagram options below)                                   |
| YouTube                    | Title, visibility, go-public time, tags, thumbnail                                 |
| Threads, Bluesky, Mastodon | Alt text for images                                                                |
| TikTok                     | Who can watch, interactions, AI label, cover frame, commercial content, inbox mode |

**YouTube visibility:** a new node starts at Private, so nobody sees the
upload until you pick Unlisted or Public. A node saved before this setting
existed keeps posting Public until you change it. Setting a go-public time
keeps the video private and lets YouTube publish it at that time; if that
time has already passed when the video uploads, it goes public straight away.
YouTube may keep uploads from Wireflow private until Google finishes reviewing
Wireflow's use of its API; when it does, the result says so. The title falls
back to the first line of the caption. Custom thumbnails need a verified
YouTube channel.

**YouTube tags:** type them as a comma separated list, like `ai, video, shorts`.
Saved config and the API also take a list of strings, like `["ai", "video"]`.
Anything else, such as a number or an object, is refused before anything is
posted. YouTube keeps 500 characters of tags in total (commas count, and a tag
with a space counts two more), so Wireflow keeps the first tags that fit and
drops the rest.

The first comment and the thumbnail are added after the post is live. If
either fails, the post still counts as published, is not re-posted, and the
result shows a warning saying what did not happen.

**Instagram first comment:** posted only when the connected account granted
Wireflow the comment permission. Wireflow does not ask for it while the Meta
app is in review, so most connections do not have it; the post then goes out
without the comment and the result warns "first comment not posted: this
Instagram connection lacks the comment permission". When it is posted, the
comment's id comes back as `firstCommentId`. Instagram has no API to pin a
comment, so pinning stays a tap in the app. If the comment request times out
the result says the comment "may or may not have posted": check the post
before commenting by hand. The comment is also skipped, with a warning, when
Instagram did not hand back the post's id directly and it had to be matched
afterwards, so it can never land on the wrong post.

**Instagram first comment limits:** at most 2,200 characters, 5 @mentions
and 1 link. A longer or more link-heavy automated comment on a brand-new
Instagram post reads as spam, so it is refused before anything is posted. An
email address or a dotted handle such as @brand.co is not counted as a link.
LinkedIn first comments have no such limit here.

A first comment on any other platform (X, Threads, YouTube, and so on) is
refused before anything is posted rather than silently dropped.

## Account ids and per-platform keys

An agent or script sets these in the node's config.

**`config.accounts`** is a list of account ids such as `native:cm1abc23`. List them with `GET /api/v1/social/accounts` using an API key that has `social:read` (the MCP `list_social_accounts` tool returns the same list, but needs the opt-in `social:publish` permission). An account a person has just connected shows up there on the next call. Details: [Social Accounts](/docs/api/social).

**`config.perPlatformOverrides`** is keyed by platform, then by option, for example `{ "youtube": { "title": "How it was made", "privacy": "unlisted" } }`. These are the keys each platform reads:

| Platform    | Keys                                                                                                                                                       |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `linkedin`  | `caption`, `firstComment`, `altText`                                                                                                                       |
| `instagram` | `caption`, `firstComment`, `aiLabel`                                                                                                                       |
| `youtube`   | `caption`, `title`, `privacy`, `scheduledPublishAt`, `tags`, `thumbnailUrl`                                                                                |
| `threads`   | `caption`, `altText`                                                                                                                                       |
| `bluesky`   | `caption`, `altText`                                                                                                                                       |
| `mastodon`  | `caption`, `altText`                                                                                                                                       |
| `tiktok`    | `caption`, `privacyLevel`, `isAigc`, `coverTimestampMs`, `disableComment`, `disableDuet`, `disableStitch`, `brandOrganic`, `brandContent`, `uploadToInbox` |

What each one takes:

- `caption`: the post text for that platform, used instead of the node's caption. A Threads post's text is its caption. There is no separate `text` key.
- `title` (YouTube): up to 100 characters. Without it the title is the first line of the caption.
- `privacy` (YouTube): `private`, `unlisted` or `public`. Leave it out of a hand-written config and the video posts **public**. The node panel and the agent door fill in `private` for you; nothing does that for config you write yourself, so set it.
- `scheduledPublishAt` (YouTube): an ISO 8601 time. The video stays private until then. A time that has already passed means public now.
- `tags` (YouTube): comma separated, such as `"ai, video"`, or a list of strings, such as `["ai", "video"]`. Anything else is refused. YouTube keeps 500 characters of tags in total; the first tags that fit are kept.
- `thumbnailUrl` (YouTube): a public https URL. Needs a verified channel.
- `firstComment` (LinkedIn, Instagram): text posted as a comment right after the post goes live. Instagram caps it at 2,200 characters, 5 @mentions and 1 link.
- `altText` (LinkedIn, Threads, Bluesky, Mastodon): one image description, used for every image.
- `aiLabel` (Instagram): `true` or `false`. On unless you send `false`.
- `privacyLevel` (TikTok): `PUBLIC_TO_EVERYONE`, `MUTUAL_FOLLOW_FRIENDS`, `FOLLOWER_OF_CREATOR` or `SELF_ONLY`. Until TikTok audits Wireflow's app only `SELF_ONLY` is accepted (it is the default) and anything else is refused. After the audit there is no default, so set it.
- `isAigc` (TikTok): `true` or `false`. On unless you send `false`.
- `coverTimestampMs` (TikTok): the cover frame, in whole milliseconds into the video, 0 to 600000. Unset lets TikTok pick the first frame.
- `disableComment`, `disableDuet`, `disableStitch` (TikTok): `true` or `false`. All three are `true` (off) unless you send `false`, as TikTok requires.
- `brandOrganic`, `brandContent` (TikTok): `true` or `false`, default `false`. `brandOrganic` labels the video "Promotional content". `brandContent` labels it "Paid partnership" and it cannot be private.
- `uploadToInbox` (TikTok): `true` sends the video to the creator's TikTok inbox to finish in the app, and the result reads "Sent to TikTok inbox", never "Published". Default `false`.

Two more groups of keys are read from config but are not accepted by the agent door: Instagram's `shareToFeed`, `coverUrl`, `thumbOffsetMs`, `collaborators`, `trialReel` and `trialGraduation` (the Instagram options described further down), and Mastodon's `privacy`, which takes `public`, `unlisted` or `private` and means `unlisted` when unset. The node panel has no field for Mastodon's.

## Text-only posts

With NOTHING wired to Media, the caption posts on its own to Threads, Bluesky
and Mastodon. Instagram, YouTube and LinkedIn need an image or a video, and
TikTok a video, so a text-only post that includes one of them is refused
before anything is posted, and the error names the account.

A wired Media port is different. If the node feeding it fails, Social Publish
is skipped, exactly as before. If it finishes but hands over nothing that can
be posted, the post is refused. Either way the caption never goes out alone.

Captions are checked before anything is posted, never truncated:

- **Threads:** at most 500 characters, and Meta counts each emoji as its UTF-8
  bytes (2 to 4), so emoji use up the limit faster. At most 5 links.
- **Bluesky:** at most 300 characters.
- **Mastodon:** the limit is set per server and is checked when the post goes
  out.

Shorten the caption, or set a shorter one for that platform under
per-platform variations.

## Source key

Set **Source key** to the id of the post this one copies, such as an X post or
draft id. Each account gets at most one post per key: running the node again
with the same key returns the post already there, marked `duplicate`,
instead of posting it twice. A cancelled post does not count, and neither does
one that provably never went out (it could not be queued, or every attempt
failed before posting). A post that failed in a way that may have published it
anyway (a timeout while posting) still counts: on Threads, Wireflow checks the
account's recent posts and posts again only when the earlier one is provably
not there; otherwise the node says so and posts nothing. Connected accounts
only.

Each entry in `posts` carries the platform's post id (`postId`) and
`liveUrl`; a Threads post also carries its own `permalink`.

## When is a post "published"

- **LinkedIn video**: only after LinkedIn finishes processing the video. If it
  cannot, nothing is posted and the node reports why.
- **YouTube**: after the upload, the node waits up to two minutes for YouTube
  to finish processing. If YouTube rejects the video, the node fails. If it is
  simply slow, the post is reported published, because the video exists.
- **TikTok**: when TikTok reports it complete. A private post (every post
  until TikTok audits Wireflow's app) reads "Posted privately, flip to public
  in TikTok", never plain Published; inbox mode reads "Sent to TikTok
  inbox". See TikTok below.

## Scheduled posts

A scheduled post that hits a temporary error (a platform outage, a rate
limit) is retried with a growing wait, up to five attempts. A post more than
24 hours past its time is not sent at all. The scheduler never sends one post
twice: if a run is interrupted mid-publish, the post is marked failed with a
note to check the account before posting again.

## Ban-safety limits

Every connected account gets at most 3 feed posts in any 24 hours, at least 3
hours apart. The limits belong to the account on the platform, not to the node:
every board, every Wireflow login connected to the same account, and the MCP
`publish_post` tool share one budget. They are judged on when each post goes
out (after jitter), so you can queue a week of posts in one sitting. Instagram
Trial Reels run on their own budget (5 in 24 hours, 15 minutes apart) that
feed posts never count toward.

The node's Ban-safety knobs (min interval, max posts / 24h) set the limits for
this node, and are used both when the post is scheduled and when it is sent.
0 means the default; 0 does not turn the limits off. If a post cannot go at
its time, scheduling refuses it and says the earliest legal time.

A post deleted on Instagram stops counting once Instagram confirms it is gone.
A scheduled post that would break the limits when it actually fires (after an
outage, for example) is moved to the next allowed time, and the post's
warnings say why. A run that starts a few seconds after the post's time is
judged at the post's own time, so a set spaced at the minimum goes out on time.

## Posting from an agent

The Wireflow MCP server can post without a node: `list_social_accounts`,
`publish_post`, `get_post_status` and `cancel_post`. They need the `social:publish`
permission, which is off by default and only the account owner can tick on the
authorization screen. `publish_post` takes an `idempotencyKey`: an agent
that retries with the same key gets the original post back instead of a second
one. It queues the post and returns right away; `get_post_status` reports
when it is live, and `cancel_post` stops a post that is still scheduled. The
same ban-safety limits apply as in the node. Omit `mediaUrls` for a text-only
post (same platforms as the node), and pass `sourceKey` so one source post
reaches each account once.
Instagram-specific options (cover, collaborators, Trial Reel, share-to-feed)
are node-UI only for now; passing them to `publish_post` is refused. The first
comment and the AI label are the exceptions: `publish_post` takes
`firstComment` and `aiLabel` (true or false) for Instagram.

## Connecting an account

Open the node and use the Connect buttons under Accounts. Each one opens the
platform's consent screen in a new tab; approve it, come back, and press
Refresh. Tokens are stored encrypted and refreshed for you.

Bluesky connects with an app password instead: the button opens a small form
in the node. An agent can start any connection with the MCP
`connect_social_account` tool, which returns a link for the account owner (or
connects Bluesky with an app password they give it).

## Instagram

Instagram is in preview: until Meta approves the app, the Connect Instagram
button is shown only to Wireflow staff and to accounts approved for preview
access. A team admin role alone does not show it.

Instagram needs a Business or Creator account. Personal accounts cannot publish
through the API.

What gets posted depends on what you wire into Media:

| Media         | Posts as                             |
| ------------- | ------------------------------------ |
| One video     | A Reel                               |
| One image     | A feed post                          |
| 2 to 10 files | A carousel (images, videos, or both) |

Images are made to fit Instagram's rules before upload: JPEG, at most 8MB,
320 to 1440 wide, and between 4:5 and 1.91:1. An image outside that range gets
white bars rather than a crop, so a 9:16 still posts as 4:5 with bars left and
right. Reels must be MP4 or MOV. More than 10 files is refused rather than
trimmed.

Selecting an Instagram account shows these options:

- **Label as AI-generated.** On by default, so every Instagram post goes out
  with the "AI info" label (Instagram limits reach for AI-person profiles that
  skip it). Turn it off only for real footage. Once a post is sent, the result
  shows `aiLabel` as `true`, `false`, `disabled` (the operator kill switch withheld it), or `fallback` (Instagram refused the label
  for that media type, so the post went out without it and the result carries a
  warning: add the label in the app).
- **Also show Reel in the main feed.** On by default. Off keeps the Reel in the
  Reels tab only.
- **Trial Reel.** Shows the Reel to non-followers first. Choose whether it is
  shared with followers when you decide in the app, or automatically if it
  performs well. Only works on a single video, and cannot have collaborators.
- **Cover.** A cover image URL, or a frame of the video in milliseconds. The
  frame wins when both are set.
- **Collaborators.** Up to 3 usernames. They accept the invite in the app.

Instagram allows **100 API posts per account in any 24 hours**, shared with
every other tool connected to that account. When it is used up the node fails
with a message saying so and posts nothing.

The node waits for Instagram to finish processing each video before publishing
(up to 4 minutes for the whole post) and returns the post's permalink in `posts`.

Full setup, including the Meta app settings: [Publishing to Instagram](/docs/publishing-to-instagram).

## TikTok

TikTok is in preview: the Connect TikTok button is shown only to Wireflow
staff while TikTok reviews Wireflow's TikTok app.

Wireflow posts through TikTok's own Content Posting API. Press Connect
TikTok, approve on TikTok's consent screen, and come back. Wireflow asks for
three permissions: your basic profile, posting videos, and uploading videos to
your TikTok inbox. Tokens are stored encrypted and refreshed for you.

**Until TikTok audits Wireflow's app, every TikTok post is private.** TikTok
only accepts "Only me" posts from an app it has not audited yet, and may
require the TikTok account itself to be set to private while testing. The
post goes out private and the result says so; open it in the TikTok app and
change Who can watch to make it public.

Selecting a TikTok account shows the account you are posting to (TikTok's
nickname for it) and these options:

- **Who can watch.** Only me until the audit. After it, you must pick one of
  the options TikTok offers for your account; there is no default.
- **Allow comments, duets, stitches.** Off until you turn them on, as TikTok
  requires. An interaction you turned off for the whole account in TikTok is
  greyed out.
- **Label as AI-generated.** On by default, so TikTok shows its "Creator
  labeled as AI-generated" tag.
- **Cover frame.** Milliseconds into the video. Unset uses the first frame.
- **Disclose commercial content.** Off by default. "Your brand" labels the
  video "Promotional content"; "Branded content" labels it "Paid partnership"
  and cannot be private.
- **Send to my TikTok inbox instead of posting.** The video lands in your
  TikTok inbox and you finish it in the app, for example to add a trending
  sound. The caption and settings do not travel with it, and the result reads
  "Sent to TikTok inbox", never "Published".

Each post is one video (MP4, MOV or WebM, up to 10 minutes). The caption is
the TikTok caption, up to 2,200 characters.

A TikTok post counts as published when TikTok reports it complete. A public
post links the video by TikTok's post id; if TikTok has not released the id
within the 8 minute wait (it can lag behind moderation), the post still counts
as published, links your profile and says so. TikTok gives no id for a private
post, so a private post reads "Posted privately, flip to public in TikTok",
links your profile, and never counts as a public post. These are failures,
never "published": TikTok failing the video (too long, wrong format), TikTok
dropping a direct post into your inbox, and no answer within 8 minutes (the
post may still appear, so check the account before posting again).

A TikTok private post or inbox upload counts as posted for a sourceKey:
flip the post public in TikTok rather than posting it again. If you deleted
the post or discarded the draft, post again with a new sourceKey.

Comments, duets and stitches start off on every TikTok post, as TikTok
requires. Tick Allow comments (and duets, stitches) in the node, or pass
"disableComment": false from an agent, to turn them on for that post.

Scheduled TikTok posts go out at their exact time, like every other platform.
TikTok allows about 15 posts a day per account across all apps.

**Approve every post.** TikTok's rules say the creator must see each post (the
video, the caption and its settings) and agree to it before it goes out. In
Wireflow that approval is the node: you see the video and caption you wired
in, pick the options, and run or schedule it yourself. Do not build flows that
post videos to TikTok that nobody has looked at. By posting, you agree to
TikTok's Music Usage Confirmation (and its Branded Content Policy for branded
content).

An agent can start the connection with the MCP `connect_social_account` tool,
or `GET /api/social/connect/tiktok` with a Wireflow API key that holds
`social:publish`: both return a link the account owner opens while signed in
to Wireflow. `GET /api/social/tiktok/creator-info?accountId=...` returns the
same account options the node shows.

---

_Auto-generated from the Wireflow node registry._

---

Documentation index: fetch https://www.wireflow.ai/llms.txt for the full list of Wireflow docs.
