Skip to main content
Glama

Publish or schedule a post (goes live)

publish
Destructive

[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

NameRequiredDescriptionDefault
memoNo"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.
pollNoA 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.
labelsNoLabels 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.
brandIdNoAn id from GET /api/v2/brands, or "personal". With contentId it must match the item's brand.
contentNoPost text. Required unless contentId is given.
reshareNoShare/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.
youtubeNoOverrides 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.
facebookNoFacebook 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.
imageUrlNo
videoUrlNo
contentIdNoAn existing content item id (GET /api/v2/content/{id} access rules). Cannot be combined with imageUrl / videoUrl.
instagramNoInstagram 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>`.
pinterestNoFFE-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.
platformsYes
aiGeneratedNoLabel 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.
scheduledAtNoMust be in the future. Omitted → publish now.
strictMediaNoRefuse (422 media_fit) when the video does not fit a booked platform (length, shape, smallest frame, loudness) instead of posting with `warnings`.
firstCommentNoExplicit 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.
platformTextNoPer-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.
idempotencyKeyNoUnique per API key. A retry with the same key never posts twice.
instanceLabelsNoInternal 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.
onPublishTasksNoShorthand for memo.tasks (appended after them). Response: `memoId`; a memo that could not be saved is a `warnings` entry, never a failed publish.
referralFooterNoCaption 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.
mediaCampaignIdNoLink to a media campaign; with a brand target it must belong to that brand.
autoFirstCommentNoWhen 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.
followUpCommentsNoComments 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.
requiresApprovalNo
allowDuplicateBookingNoA 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.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • addedInput schema / properties / instanceLabels
      Added value: +{
      +  "description": "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.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "accountId": {
      +        "maxLength": 200,
      +        "type": "string"
      +      },
      +      "labels": {
      +        "items": {
      +          "type": "string"
      +        },
      +        "maxItems": 30,
      +        "type": "array"
      +      },
      +      "platform": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "platform",
      +      "labels"
      +    ],
      +    "type": "object"
      +  },
      +  "maxItems": 20,
      +  "type": "array"
      +}
  2. Changed1 schema field changed
    • addedInput schema / properties / platformText
      Added value: +{
      +  "additionalProperties": {
      +    "maxLength": 10000,
      +    "type": [
      +      "string",
      +      "null"
      +    ]
      +  },
      +  "description": "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.",
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
  3. Changed3 schema fields changed
    • changedInput schema / properties / instagram / description
      Previous value: -"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."New value: +"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>`."
    • addedInput schema / properties / instagram / properties / trialReel
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "enabled": {
      +      "type": "boolean"
      +    },
      +    "graduation": {
      +      "enum": [
      +        "manual",
      +        "auto"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "enabled"
      +  ],
      +  "type": "object"
      +}
    • addedInput schema / properties / strictMedia
      Added value: +{
      +  "default": false,
      +  "description": "Refuse (422 media_fit) when the video does not fit a booked platform (length, shape, smallest frame, loudness) instead of posting with `warnings`.",
      +  "type": "boolean"
      +}
  4. Changed2 schema fields changed
    • addedInput schema / properties / memo
      Added value: +{
      +  "description": "\"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.",
      +  "properties": {
      +    "notes": {
      +      "maxLength": 4000,
      +      "type": "string"
      +    },
      +    "tasks": {
      +      "items": {
      +        "oneOf": [
      +          {
      +            "description": "The to-do, e.g. \"Share to personal LinkedIn\".",
      +            "maxLength": 500,
      +            "type": "string"
      +          },
      +          {
      +            "properties": {
      +              "copyText": {
      +                "description": "Text to paste (a reply, a first comment). The live page shows a Copy button.",
      +                "maxLength": 5000,
      +                "type": "string"
      +              },
      +              "id": {
      +                "description": "PUT only: keep an existing to-do (and its tick) by id.",
      +                "type": "string"
      +              },
      +              "platform": {
      +                "description": "The platform the step is for, e.g. \"youtube\".",
      +                "type": "string"
      +              },
      +              "text": {
      +                "maxLength": 500,
      +                "type": "string"
      +              },
      +              "url": {
      +                "description": "Where to do it (http/https).",
      +                "format": "uri",
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "text"
      +            ],
      +            "type": "object"
      +          }
      +        ]
      +      },
      +      "maxItems": 30,
      +      "type": "array"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedInput schema / properties / onPublishTasks
      Added value: +{
      +  "description": "Shorthand for memo.tasks (appended after them). Response: `memoId`; a memo that could not be saved is a `warnings` entry, never a failed publish.",
      +  "items": {
      +    "oneOf": [
      +      {
      +        "description": "The to-do, e.g. \"Share to personal LinkedIn\".",
      +        "maxLength": 500,
      +        "type": "string"
      +      },
      +      {
      +        "properties": {
      +          "copyText": {
      +            "description": "Text to paste (a reply, a first comment). The live page shows a Copy button.",
      +            "maxLength": 5000,
      +            "type": "string"
      +          },
      +          "id": {
      +            "description": "PUT only: keep an existing to-do (and its tick) by id.",
      +            "type": "string"
      +          },
      +          "platform": {
      +            "description": "The platform the step is for, e.g. \"youtube\".",
      +            "type": "string"
      +          },
      +          "text": {
      +            "maxLength": 500,
      +            "type": "string"
      +          },
      +          "url": {
      +            "description": "Where to do it (http/https).",
      +            "format": "uri",
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "text"
      +        ],
      +        "type": "object"
      +      }
      +    ]
      +  },
      +  "maxItems": 30,
      +  "type": "array"
      +}
  5. Changed1 schema field changed
    • addedInput schema / properties / pinterest
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "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.",
      +  "properties": {
      +    "boardId": {
      +      "description": "From GET /api/v2/pinterest/boards for the same brand.",
      +      "maxLength": 200,
      +      "type": "string"
      +    },
      +    "link": {
      +      "description": "Destination link (http or https).",
      +      "format": "uri",
      +      "maxLength": 2048,
      +      "type": "string"
      +    },
      +    "title": {
      +      "maxLength": 100,
      +      "minLength": 1,
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  6. First observed

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true, openWorldHint=true and idempotentHint=false, so the side-effecting nature is declared structurally. The description still adds real context beyond them: scope (`publish`) and plan gating, consumption of monthly direct-post quota, up-front readiness/media validation, review hold with { status: "pending-review", reviewId }, watermark and 409 watermark_pending/duplicate_content semantics, and per-platform validation reporting in platformErrors.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded well: the destructive real-world effect, timing, contentId preference, and idempotencyKey advice all land in the first few clauses. But it is a single very long paragraph that re-narrates schema-documented fields (Facebook/Instagram contentType, Pinterest board defaults, platformText limits) rather than deferring to the schema, bloating the read for an agent that also has the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 28-parameter, nested, no-output-schema tool the description covers what matters most: post-conditions, failure codes that require a decision, approval paths, quota cost, and a next step (get_content). Some parameters (memo, onPublishTasks, instanceLabels, followUpComments, strictMedia, reshare) are left entirely to the 86%-covered schema, which is acceptable but leaves the coverage uneven.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 86%, so the baseline is 3; the description clears that bar slightly by adding cross-parameter rules the schema does not state — contentId beats inline content, unknown arguments are refused (422 unknown_fields), and platformText must match update_scheduled_post's shape. Most of its parameter prose (facebook/instagram contentType, pinterest boardId, allowDuplicateBooking, platformText limits) restates schema field descriptions almost verbatim, so it is not fully additive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening states a specific verb and resource with its real-world effect: "PUBLISHES TO REAL SOCIAL ACCOUNTS. Posts now, or at `scheduledAt` (ISO-8601, future)." It separates the tool from create_content (drafts) by telling the agent to prefer `contentId` from create_content, and from update_scheduled_post for booked drafts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use and when-not rules: prefer `contentId` over inline `content`; an already-scheduled draft is refused (409 already_booked) and the agent should "change the booked post instead"; `allowDuplicateBooking: true` only "when the user asked to post it again"; and "Confirm with the user before posting now." It also names the follow-up tool (get_content) for live URLs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources