Skip to main content
Glama

Create or schedule a post

create_post

Create a post targeting one or more connected platform accounts. Control what happens with submissionIntent: 'draft' (save only — the response then carries advisory validationWarnings listing anything that must be fixed before scheduling), 'publish-now', or 'schedule' with a future scheduledAt (ISO 8601 with a UTC offset, e.g. 2026-10-20T09:00:00-03:00 — the timezone parameter does not shift it). platformConnectionIds come from list_platform_connections; the API assigns each connection its platform's canonical posting destination. Attach media with mediaIds (stable library IDs from list_media / import_media_from_url — preferred over URLs). Set platform-specific options via metadata (every network's options — see the metadata parameter), override text per platform with platformContents, and set requiresApproval + approvalEmail to route the post through external approval. When unsure the payload is valid, call validate_post first with the same input.

Platform rules (enforced by the API — validate_post checks all of them):

  • Facebook: ≤63206 chars, ≤10 images, ≤1 videos, video 1–14400s, supports first comment

  • Instagram: ≤2200 chars, media REQUIRED, ≤10 images, ≤1 videos, video 3–900s, supports first comment

  • Threads: ≤500 chars, ≤10 images, ≤1 videos, video 0–300s, supports first comment

  • X (Twitter): ≤280 chars (25000 with a long-post subscription), ≤4 images, ≤1 videos, supports first comment

  • LinkedIn: ≤3000 chars, ≤9 images, ≤1 videos, video 3–1800s

  • TikTok: ≤2200 chars, media REQUIRED, ≤35 images, ≤1 videos, video 3–600s, supports first comment (TikTok Business connections only)

  • YouTube: ≤5000 chars, VIDEO REQUIRED, ≤1 images, ≤1 videos

  • Telegram: ≤4096 chars (≤1024 with media), ≤10 images, ≤1 videos

  • WhatsApp: ≤4096 chars (≤1024 with media), ≤1 images, ≤1 videos

  • Pinterest: ≤500 chars, media REQUIRED, ≤5 images, ≤1 videos, video 4–900s

  • Google Business: ≤1500 chars, ≤10 images, ≤1 videos

  • Discord: ≤2000 chars, ≤10 images, ≤1 videos

  • Bluesky: ≤300 chars, ≤4 images, ≤1 videos, video 0–180s

  • Mastodon: ≤500 chars, ≤4 images, ≤1 videos

  • Images and a video in the same post are refused on Facebook, LinkedIn, Pinterest, X (Twitter), Bluesky, Mastodon, TikTok — attach one kind per post (or per platform override)

  • X counts weighted length, not characters: every URL counts 23, CJK characters count 2, each emoji counts 2; the ≤280 limit applies to that count

  • X long posts: an X connection whose metadata.subscriptionType (list_platform_connections) is Basic, Premium, PremiumPlus may post up to 25000; every X connection the post targets must have one, or the whole post is held to 280. The first comment stays ≤280. A subscription bought after connecting counts once an owner or admin presses Refresh on the X connection in PostRite

  • Bluesky counts graphemes (what a reader sees as one character, so an emoji is 1), not UTF-16 units; the ≤300 limit applies to that count

  • Pinterest: requires metadata.pinterest.boardId (from list_pinterest_boards) unless the account has a default board

  • Discord bot connections: require metadata.discord.channels (map of platformConnectionId -> channelId, from list_discord_channels)

  • WhatsApp: requires metadata.whatsappRecipient (phone number)

  • TikTok: requires metadata.tiktokOptions.privacyLevel, one of the account's privacy_level_options from get_tiktok_creator_info

  • TikTok connection method (metadata.authMethod on list_platform_connections: "business_api" is TikTok Business, absent or "login_kit" is Login Kit): a TikTok Business VIDEO publishes public only — metadata.tiktokOptions.privacyLevel must be PUBLIC_TO_EVERYONE — and is capped at 1 GB (Login Kit: 4 GB); photo posts keep the privacy_level_options from get_tiktok_creator_info on both

  • TikTok Business only (Login Kit refuses both): metadata.tiktokOptions.uploadAsDraft: true sends the video to the creator's TikTok drafts instead of publishing it — TikTok then ignores everything but the caption, though privacyLevel must still be set — and metadata.tiktokOptions.coverMediaId (a library image) sets a separate video cover, which makes TikTok ignore videoCoverTimestampMs

  • TikTok first comment: metadata.tiktokOptions.firstComment (≤1200 chars) is posted only on a TikTok Business connection and never on an uploadAsDraft post; on Login Kit the post still publishes but the comment is not posted (validate_post warns with validation.tiktokFirstCommentBusinessOnly)

  • TikTok photo carousels: metadata.tiktokOptions.photoTitle is an optional headline of ≤90 chars (derived from the caption's first line when omitted); the caption itself is sent as the description (≤4000)

  • Instagram contentType REEL: exactly 1 video, 3–900s

  • Instagram contentType STORY: exactly 1 media, video 3–60s and ≤100 MB, no caption

  • Facebook metadata.facebookOptions.postType REEL: exactly 1 vertical (9:16) video, 3–90s, at least 540×960; Meta allows 30 API-published Reels per Page per 24h

  • Facebook metadata.facebookOptions.postType STORY: exactly 1 image or video (3–60s), no caption, no first comment

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": {
      "content": "<content>",
      "platformConnectionIds": "<platformConnectionIds>"
    },
    "name": "create_post"
  }
}

Input Schema

NameRequiredDescriptionDefault
contentYesThe post body text
mediaIdsNoMedia library IDs (from list_media or import_media_from_url) — the preferred way to attach media; IDs are stable, unlike access URLs. Posts take images and videos only: an audio file or PDF document from the library is refused (validation.mediaNotPostable). On update_post, an empty array leaves the media untouched (passing [] does not remove media — removing all media is done in the app)
metadataNoPlatform-specific options, by block: youtubeOptions (title, privacyStatus: public | unlisted | private, categoryId, madeForKids, isShort, coverMediaId, coverUrl, tags); tiktokOptions (firstComment, privacyLevel, disableComment, disableDuet, disableStitch, isAigc, autoAddMusic, brandOrganicToggle, brandContentToggle, commercialContentEnabled, videoCoverTimestampMs, photoCoverIndex, photoTitle, uploadAsDraft, coverMediaId, coverUrl); instagramOptions (firstComment, isTrialReel, trialGraduation: MANUAL | SS_PERFORMANCE, collaborators, userTags, isAiGenerated, paidPartnership{sponsorIds}, shareToFeed, poll{options, extendedDuration}, commentPrompt, coverMediaId, coverUrl, coverTimestampMs, audio{audioId, audioType, title, artist, coverUrl, durationMs, audioVolume, videoVolume}); facebookOptions (firstComment, postType: FEED | REEL | STORY); linkedinOptions (coverMediaId, coverUrl); twitterOptions (firstComment, replySettings: following | mentionedUsers); threadsOptions (firstComment, replyControl: everyone | accounts_you_follow | mentioned_only, shareToInstagram, enableReplyApprovals, topicTag, linkAttachment, poll{options}, longText, mediaSpoiler, isMadeWithAi, isGhostPost, quotePostId, quotePostPreview{username, text, permalink}); pinterest (boardId, title, link, coverMediaId, coverUrl); googleBusiness (postType: STANDARD | EVENT | OFFER, ctaType: NONE | BOOK | ORDER | SHOP | LEARN_MORE | SIGN_UP | CALL, ctaUrl, event{title, startDate, endDate}, offer{couponCode, redeemOnlineUrl, termsConditions}); discord (channels, username, avatarUrl); bluesky (replyControl: everybody | nobody | mentioned_or_following, quoteControl: everybody | nobody, selfLabels: sexual | nudity | porn | graphic-media); mastodon (visibility: public | unlisted | private | direct, spoilerText, language); telegram (disableNotification, disableWebPagePreview); whatsappRecipient; globalTags. First comments: facebookOptions/instagramOptions/twitterOptions/threadsOptions/tiktokOptions.firstComment (TikTok: Business connections only, see below). facebookOptions.postType: absent means FEED; REEL takes exactly one vertical video, STORY one image or video with no caption and no first comment — independent of the top-level contentType, which is Instagram's placement. twitterOptions.replySettings: absent means everyone can reply. instagramOptions: trialGraduation only with isTrialReel and contentType REEL (absent means MANUAL); isAiGenerated adds Instagram's AI label; paidPartnership.sponsorIds are NUMERIC Instagram user IDs (max 2), ignored on an Instagram Login connection; audio only on a REEL via a Facebook Login connection. threadsOptions.shareToInstagram, enableReplyApprovals and replyControl are omitted unless the organization has the matching scope enabled. threadsOptions.topicTag is the post's one topic (≤50 chars, no ".", "&" or "#"). threadsOptions.poll (options: 2-4 strings of ≤25 chars), linkAttachment (an http(s) URL shown as a preview card) and longText (≤10000 chars attached under the post) only work on a Threads post with no media; poll and longText cannot be combined. mediaSpoiler blurs the attached media. A Threads post counts at most 5 distinct links, linkAttachment included. Spoilers on Threads are written in the text as ||hidden words|| (at most 10); the markers are removed before publishing and do not count against the character limit. Other platforms except Discord show the markers as typed (validate_post warns: validation.spoilerMarkersElsewhere), so give Threads its own text via platformContents when the post also goes elsewhere. instagramOptions.userTags tags people on the post's media: [{ mediaId, username, x, y }]. mediaId is the id of one of the media Instagram receives (from mediaIds); x and y are 0-1 fractions of the image (0,0 top-left) and are REQUIRED on an image and FORBIDDEN on a video/Reel. At most 20 people per media, each once. Story: x/y optional. A template never keeps userTags (the api strips them): they point at one post's media. instagramOptions.shareToFeed (boolean, REEL only) is three-valued: absent sends nothing and Instagram applies its own choice for the account (Meta's reference states no default), true asks for Feed and Reels, false for the Reels tab only. instagramOptions.poll ({ options: 2-4 strings of 1-25 chars, extendedDuration?: boolean }) and instagramOptions.commentPrompt (true) both turn the post's caption into the question, so the post needs a caption; the two cannot be combined, neither works on a Story, and both are published only through a Facebook Login connection (metadata.authMethod facebook_login on list_platform_connections): on an Instagram Login connection validate_post returns the error validation.instagramCaptionAddOnFacebookLoginOnly, and on a mix of connections a warning (validation.instagramCaptionAddOnSomeAccountsWithout). The serialized poll may not exceed 200 characters. threadsOptions.isMadeWithAi adds Threads' AI info label. threadsOptions.isGhostPost publishes a ghost post, archived by Threads after 24 hours: it is text only and supports nothing but text spoilers, so it refuses media, poll, longText, linkAttachment, topicTag, quotePostId, replyControl, enableReplyApprovals, shareToInstagram, isMadeWithAi and mediaSpoiler (validate_post names the option: validation.threadsGhostPostUnsupported). threadsOptions.quotePostId quotes a thread: it is the numeric Threads media id (digits only, not a URL and not a shortcode) of one of the connected account's OWN threads, and it is account-bound, so a template never keeps it. validate_post refuses an id that is not digits (validation.threadsQuotePostInvalid) and a quote on a post that goes to more than one Threads account (validation.threadsQuoteOneAccount); it cannot check that the thread belongs to the account, and what Threads does with someone else's thread has not been measured, so only quote the account's own. quotePostPreview is display data the editor fills in; leave it out. bluesky reply/quote gates are best-effort: a refusal is a warning on the published post, not a failure. mastodon.visibility: absent publishes public. tiktokOptions depends on how the TikTok account was connected (metadata.authMethod on list_platform_connections: business_api is TikTok Business, absent or login_kit is Login Kit). A TikTok Business video publishes public only, so privacyLevel must be PUBLIC_TO_EVERYONE. uploadAsDraft: true sends the post to the creator's TikTok drafts instead of publishing (Business connections only; absent means publish). coverMediaId is a library image id used as the video's cover (Business connections only; TikTok then ignores videoCoverTimestampMs; the api fills coverUrl, never send it). Login Kit refuses uploadAsDraft and coverMediaId. tiktokOptions.firstComment (≤1200 chars) is posted as a comment on the video only on a TikTok Business connection, and never on an uploadAsDraft post; on Login Kit the post still publishes but the comment is not posted (validate_post returns the warning validation.tiktokFirstCommentBusinessOnly). Alternative text is NOT here: it belongs to the media file (update_media), so one description serves every post and platform. Required per destination: pinterest.boardId (ids from list_pinterest_boards; required unless the account has a default board); discord.channels (bot connections: map of platformConnectionId -> channelId, ids from list_discord_channels); whatsappRecipient (recipient phone number); tiktokOptions.privacyLevel (must be one of privacy_level_options from get_tiktok_creator_info).
timezoneNoIANA timezone stored with the post for display (defaults to the org timezone). It does not shift scheduledAt: put the offset in scheduledAt itself
mediaUrlsNoMedia URLs to attach (escape hatch for URLs of items already in the library) — prefer mediaIds
contentTypeNoInstagram post type (POST | REEL | STORY). Only meaningful for Instagram targets.
scheduledAtNoISO 8601 date-time with the UTC offset included, e.g. 2026-10-20T09:00:00-03:00 or 2026-10-20T12:00:00Z. The timezone parameter is stored for display only and does not shift this time, so convert local times to an offset yourself; omit for a draft
approvalEmailNoApprover email (required when requiresApproval is true)
approvalEmailsNoApprover emails (1-5) — any one of them can decide; takes precedence over approvalEmail
organizationIdNoOrganization to act in — an id from list_organizations. Required when this connection was authorized for more than one organization; omit it when there is only one.
platformContentsNoPer-platform text overrides, keyed by Platform enum (e.g. FACEBOOK, LINKEDIN, TWITTER)
requiresApprovalNoRoute this post through external approval before scheduling
submissionIntentNoExplicit intent: 'draft' saves without scheduling, 'publish-now' publishes immediately, 'schedule' requires a future scheduledAt. When omitted the API infers it: no scheduledAt -> draft; scheduledAt within 60s of now -> publish-now; otherwise schedule.
platformMediaUrlsNoPer-platform media overrides, keyed by Platform enum. Accepts media proxy URLs ({apiUrl}/api/media/{mediaId}/file) built from library IDs.
platformConnectionIdsYesConnected platform account IDs to publish to (from list_platform_connections)

Output Schema

NameRequiredDescriptionDefault
postNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already declare readOnly=false, openWorld=true, idempotent=false, but the description goes well beyond them: drafts return advisory validationWarnings, the API enforces per-platform rules and validate_post checks the same ones, approval routing via requiresApproval/approvalEmail, and several account-bound-state caveats. It does not warn that a retried call creates a duplicate post despite idempotentHint=false, nor mention rate limits such as the 30 Reels/Page/24h given only inside a platform bullet.

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

Conciseness2/5

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

Roughly two thousand words in which the opening intent paragraph is followed by 35 lines of per-platform character/media limits that the API already enforces and that validate_post is explicitly said to check. Large blocks (TikTok Business vs Login Kit, Threads options, Instagram caption add-ons) are near-duplicates of the metadata schema description, so the same rules are paid for twice with no clear priority ordering.

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

Completeness5/5

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

For a 15-parameter tool with deep nested metadata across fourteen networks and an output schema, this covers what an agent needs: required IDs and their source tools, per-platform hard requirements, intent semantics, and approval flow. Return values need not be explained because an output schema exists.

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

Parameters3/5

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

Schema coverage is 100%, so the baseline is 3, and much of what the description says about parameters (submissionIntent inference, scheduledAt offset, timezone not shifting it) is already in the schema's own parameter descriptions. It does add meaning the schema only partially covers: mediaIds preferred over mediaUrls, and per-destination required metadata blocks (pinterest.boardId, discord.channels, whatsappRecipient, tiktokOptions.privacyLevel) tied to their source list tools.

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

Purpose4/5

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

Opens with a concrete verb and resource: 'Create a post targeting one or more connected platform accounts,' and the submissionIntent sentence makes clear it can also save drafts and schedule. It distinguishes itself from validate_post ('When unsure the payload is valid, call validate_post first with the same input') but never contrasts itself with create_template or update_post, which an agent targeting an existing post could confuse it with.

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

Usage Guidelines4/5

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

Strong conditional guidance: which submissionIntent to pick, that 'schedule' needs a future scheduledAt, when to run validate_post first, and where dependency IDs come from (list_platform_connections, list_media, list_pinterest_boards, list_discord_channels, get_tiktok_creator_info). It stops short of explicit when-not-to-use guidance against the sibling mutators (update_post, create_template, archive_post).

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