Publish or schedule a post (goes live)
publish[PUBLISHES to social accounts · publish scope] PUBLISHES TO REAL SOCIAL ACCOUNTS. Posts now, or at scheduledAt (ISO-8601, future). Prefer contentId (a draft from create_content: its text and media become the post) over inline content. The post goes out through the target brand's own connected accounts. Always pass an idempotencyKey so a retry never double-posts. platformText ({ twitter: "…", linkedin: "…" }) gives each platform its own caption, as update_scheduled_post takes it (saved on the draft with contentId); unknown arguments are refused (422 unknown_fields). The same safety gates as the dashboard apply: platform readiness/media rules are checked up front (422); if the brand requires scheduled-post review nothing is sent and you get { status: "pending-review", reviewId }; a missing required creator credit in a push-down brand is refused (422 attribution_missing) or held; the "Posted with flayr.social" caption watermark and video watermark follow the plan (409 watermark_pending means the watermarked video is still rendering — retry after retryAfterSeconds); 409 duplicate_content means the same content already went to that account in the last 24 hours (existingPostId names it; nothing new was posted). facebook: { contentType: "reel" } posts a Facebook Reel (exactly one video), "story" a Story, "feed" a regular post; instagram.contentType works the same; omitted → the draft's saved post type. instagram.trialReel: { enabled: true, graduation: "manual" | "auto" } posts a Reel to non-followers first (Instagram Trial Reel; omitted graduation → admin default; ignored with a warning when not a Reel). pinterest: { boardId, title, link } picks the board (ids from list_pinterest_boards; required: omitted → the brand's last-picked board for that account, flagged as defaultBoardApplied, else 422 pinterest_board_required), pin title and destination link. requiresApproval: true files it for human approval instead of posting. A draft that is already scheduled on one of the platforms is refused (409 already_booked): change the booked post instead, or pass allowDuplicateBooking: true only when the user asked to post it again. Needs the publish scope (or admin) and the Pro plan or higher; each publish counts against the plan's direct posts for the month. Confirm with the user before posting now. Then call get_content to see the live URLs.
(Wraps POST /api/v2/publish.)
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"platforms": "<platforms>"
},
"name": "publish"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| memo | No | "When it posts" memo: the people-steps after the post goes live (reply to a comment, share to a personal account, pin a link, add a first comment flayr can't). Saved on the draft (else the post). When the post goes live, the brand's recipients (Brand → When a post goes live; default owners + admins) get ONE notice — in-app, email and push per their own settings — linking /publish/live/{convexPostId}: live links, the shared checklist, Copy buttons and a share link. | |
| poll | No | A native poll. Only on twitter (X), linkedin and discord, and with no media: any other platform or media is a 422 (code poll_invalid), never a plain post. X: 2-4 choices of at most 25 characters, 5 minutes to 7 days, and the post text is the question. LinkedIn: question (at most 140) required, 2-4 choices of at most 30 characters, 1, 3, 7 or 14 days. Discord: question (at most 300) required, 2-10 choices, whole hours up to 768. Cannot be combined with requiresApproval, or with a brand whose scheduled-post review is required (409 poll_not_holdable). Vote counts are not available through the API. | |
| labels | No | Labels for this post on top of the draft's own (the draft wins for type/cut/variant). Normalized "key:value" labels (keys and values are lower-cased, spaces become "-"). Linked keys: campaign:<media campaign id>, experiment:<experiment id> (+ variant:<name> or the experiment's compareKey), type:<post type> (short, long-form, carousel, image, text, reel, story, poem, timelapse…). Free keys work too: cut:opus, lane:tech, parent:pantheon-part-1, ab:<pair id> (an A/B pair). Labels are copied onto every post made from the draft, and compared in GET /api/v2/analytics/experiments. | |
| brandId | No | An id from GET /api/v2/brands, or "personal". With contentId it must match the item's brand. | |
| content | No | Post text. Required unless contentId is given. | |
| reshare | No | Share/repost another post: a flayr post id (`postId`) or a pasted link (`url`). Per platform: linkedin a native reshare with your text on top (not with media or a poll); twitter a quote of your own X post, otherwise a retweet of it fired once this post publishes (no text); every other platform a link share — a new post carrying the original's link, because it has no repost API. X alone that can only retweet is a 422. Cannot be combined with requiresApproval. | |
| youtube | No | Overrides the content item's stored YouTube options field by field. YouTube upload options, applied to YouTube targets only. madeForKids is always sent to YouTube (default false). A missing title is derived from the first sentence of the caption, cut to 100 characters at a word boundary. SCHEDULED UPLOADS: the video is uploaded ahead of scheduledAt. "public" is uploaded as private and released by YouTube at the scheduled time; "unlisted" is watchable by anyone with the link from upload time; "private" stays private. | |
| No | Facebook post type. "reel" posts a Reel (exactly one video, no images; anything else is a 422 with code content_type_invalid), "story" a Page Story, "feed" a regular post. Omitted → the content item's stored platformSpecificData.facebook.contentType (what the composer saved), else a regular post. | ||
| imageUrl | No | ||
| videoUrl | No | ||
| contentId | No | An existing content item id (GET /api/v2/content/{id} access rules). Cannot be combined with imageUrl / videoUrl. | |
| No | Instagram post type, same rules: "reel" (exactly one video — a single video is posted as a Reel), "story", or "feed". Omitted → the content item's stored platformSpecificData.instagram.contentType. `trialReel` posts the Reel as an Instagram Trial Reel (shown to non-followers first): `graduation` "manual" (graduate it from the Instagram app) or "auto" (Instagram graduates it when it performs well); omitted → the admin default. Reels only: on a Story, images or a carousel it is ignored with a warning. A trial post is labelled `trial-reel:<graduation>`. | ||
| No | FFE-428: Pinterest pin options for every Pinterest target. Overrides the content item's stored platformSpecificData.pinterest field by field. A board is required. boardId omitted → the brand's last-picked board for that Pinterest account, flagged in the response (defaultBoardApplied, defaultBoardName); with no default the publish is refused (422 pinterest_board_required). The board picked is remembered as the default. title omitted → the first line of the caption. | ||
| platforms | Yes | ||
| aiGenerated | No | Label the post as AI-generated / synthetic media: YouTube containsSyntheticMedia, Instagram isAiGenerated, TikTok videoMadeWithAi, X madeWithAi, and the disclosure line (default "#AIgenerated") on platforms with no label field. Omitted → the content item's own setting, else on when its media was made with flayr AI (a character, a cloned voice, generated video or images). false on flayr-generated media posts without a label and adds a warning. A character post always carries the label. | |
| scheduledAt | No | Must be in the future. Omitted → publish now. | |
| strictMedia | No | Refuse (422 media_fit) when the video does not fit a booked platform (length, shape, smallest frame, loudness) instead of posting with `warnings`. | |
| firstComment | No | Explicit first comments per platform, posted right after the post goes out. Always wins over the brand's auto first comment. Threads: posted as a reply (it counts toward Threads' 250 posts/24h). Facebook and Instagram: not on Stories. YouTube: use youtube.firstComment. Not supported on TikTok, X, Bluesky or Pinterest. | |
| platformText | No | Per-platform captions for this post, the same shape, validation and limits as PATCH /api/v2/posts/{id}: { "twitter": "…" } gives X its own caption, null / "" clears one, null clears them all; an over-limit caption is a 422. With contentId they are merged into the item's stored captions and saved on it first (so a later edit, re-book or review uses the same ones); with `content` they apply on top of that text. Platforms without one get the text, auto-fit. Per-platform captions: platform id → the caption that platform gets instead of `text`, e.g. { "twitter": "short version" }. Each must fit its platform: X 280 (a link counts 23, an emoji 2), Bluesky 300, Threads 500, Instagram 2200, LinkedIn 3000. Platforms without one get `text` when it fits, else an automatic short version: the first line, the credits in short form, as many hashtags as fit and the short flayr link (hashtags go first, then the credit link becomes "Model by <author>"; words are never cut). PATCH merges: a null (or "") value clears that platform, platformText: null clears all. At publish every platform is validated on its own caption: one failing platform no longer fails the others — the post publishes to the rest and reports the failures in platformErrors. | |
| idempotencyKey | No | Unique per API key. A retry with the same key never posts twice. | |
| instanceLabels | No | Internal labels per platform/account instance. An array of {platform, accountId?, labels}; accountId is the Zernio connected account id. Omit accountId when unknown. instanceKey is derived as canonical platform + ":account:" + URL-encoded account id, or ":pending" when unknown (twitter canonicalizes to x). Replaces the instance set when editing; [] clears it. Never published. | |
| onPublishTasks | No | Shorthand for memo.tasks (appended after them). Response: `memoId`; a memo that could not be saved is a `warnings` entry, never a failed publish. | |
| referralFooter | No | Caption watermark: appends "Posted with flayr.social — try it: flayr.social/r/<code>/<ch>" to each platform's text (<code> is your referral code — your custom one if you claimed it; <ch> names the platform: yt, x, li, ig, tt, fb, th, bs). (YouTube: description only, never the title; X: the shorter "via flayr.social/r/<code>/x" or nothing when it would not fit — your text is never cut). On by default. false removes it only on plans that can remove the Flayr watermark (Pro and up); omitted follows your watermark setting. Beats the content item's stored value. | |
| mediaCampaignId | No | Link to a media campaign; with a brand target it must belong to that brand. | |
| autoFirstComment | No | When the target brand has "Auto first comment" on (dashboard: Brand → First comment), LinkedIn, Instagram, Facebook, Threads and YouTube targets that have no first comment of their own get the brand's comment, with a tracked link per platform. false posts this one without it. No effect when the brand has it off. | |
| followUpComments | No | Comments after publish, sent once each (Idempotency-Key) after the post is live on that platform and its tokens resolve: {{postUrl}}, {{postUrl:<platform>}}, {{parentPostUrl:<post or draft id>}}, {{youtubeUrl:<post or draft id>}}. TikTok: TikTok for Business accounts only (unverified); not on Pinterest. | |
| requiresApproval | No | ||
| allowDuplicateBooking | No | A contentId that is already scheduled (a live booking still ahead) on one of these platforms is refused with 409 { code: "already_booked", bookedAt, bookedPlatforms }. Pass true only to book the same draft again on purpose. |