Skip to main content
Glama

Save Post

save_post

Creates a new draft (omit postId; channel required) or edits an existing post: text, channel fields, media, angle, pillar, buyer questions, proposed time. Never submits or publishes. Returns a save receipt: scheduledAt, assetIds, channel, counted body length, buyerQuestions, idea, language and translationOf as stored, updatedAt, and checks (Mark's content checks on the saved content, including placeholder, and keyword on a website post with meta.seo.keyword; a fail is not a refused save, but submit_post refuses a draft while it fails). On a draft or changes-requested post, rev does not change; use updatedAt to confirm the save. A post in review, approved or scheduled needs rev (from get_post): the edit saves as the next rev and the post goes back to in review, so a person approves it again; a stale rev is refused, and so is a phrase this workspace blocks, a placeholder like [needs source], a new website slug another page in your CMS uses, or no language on a website post whose CMS collection stores pages per language. On those posts ideaId or guideVersion alone is refused, since it would drop the approval without changing what publishes. To link any post to an idea without touching its approval, call save_idea with linkPostIds. Posts in any other status are locked; to fix the text of a published social post, call edit_published_post. Any post that is not archived, published and imported posts included, takes postId with angleId and/or pillarId alone (rev optional): that changes only its pillar, angle and persona, so its status, rev, content and approval stay as they are. A website post that is not archived (in review, approved, scheduled, published, failed or missed) also takes postId with buyerQuestionIds alone: that replaces the buyer questions it answers and nothing else, so its status, rev, content and approval stay as they are. Asset ids that are not in this workspace are listed as dropped, never attached silently. Likewise droppedMetaKeys lists the meta keys the channel does not take, at any depth, as paths like cover.url or cmsImages[0].alt, which the save ignored; the save still succeeds. A LinkedIn post can carry one PDF asset (kind document) and no other media, up to 100 MB; meta.documentTitle is the title LinkedIn shows above it, else the file name. A PDF with other media, over 100 MB, or on another channel is refused with the message submit_post gives. An angle sets the post's pillar and persona to the angle's; a pillarId that contradicts the angle is refused. Website posts (blog, faq, competitor_page) take a pillar, never an angle, plus the buyer questions the page answers (buyerQuestionIds, from get_ai_visibility). A LinkedIn Article (linkedin_article) takes a Markdown body and meta { title, cover? }; it needs no connected account, and once approved it waits for a person to publish it on LinkedIn and record the URL with record_post_published. ideaId links the post to an idea from list_ideas (null unlinks); linking never changes the post's pillar, persona or angle. A new draft with ideaId takes what the call leaves unset from the idea: its pillar and angle (a website post takes the pillar and persona, never the angle), its buyer question on a website post while that question is active, and a body that starts with the idea's title and brief. Call get_channel_rules first. To send it for review, call submit_post after saving. Related: get_post, submit_post, add_asset, list_ideas.

Tool call template

This tool declares no required parameters. Add optional parameters from the input schema as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {},
    "name": "save_post"
  }
}

Input Schema

NameRequiredDescriptionDefault
revNoThe post's current rev from get_post. Required to edit a post in review, approved or scheduled; ignored on drafts.
bodyNoPost body / caption.
metaNoChannel-specific fields from get_channel_rules. Instagram, LinkedIn, Facebook and X: altText is the post-wide alt text and altTexts is one alt text per image in media order (on X, post 1's images, then each reply's, in thread order; when threadMedia moves media, send altTexts in the new order); a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn, Facebook, X and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], ["id"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, competitor_page): the cover is cover: { source: "asset", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: "cms", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which).
ideaIdNoIdea id from list_ideas, or null to unlink. Archived ideas can't be newly linked. On a new draft, unset pillar, angle, buyer questions and body come from the idea.
postIdNoExisting post to edit. Omit to create a new draft.
angleIdNoAngle id, or null to clear. Social posts only; refused on website posts. Sets the post's pillar and persona to the angle's.
channelNoRequired to create a draft. When editing, omit it or pass the post's current channel — the channel cannot be changed here.
assetIdsNoAsset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error. An X thread lists every asset in the thread here; meta.threadMedia says which reply each goes on. Website posts take no attachments: set meta.cover instead (assetIds may be omitted or hold only the cover id); other images go in the body as Markdown. A library image goes in a website post body as ![alt](url), with the relative url from search_assets / add_asset (/api/assets/<id>/content); save stores that form whatever host you write.
languageNoWebsite posts only: the language the post is written in, a BCP 47 code such as de or de-CH, stored canonical (de-ch saves as de-CH). A CMS that stores languages (Payload localization) gets it as the locale: the page is saved in that language, not the site's default. null or blank clears it; omit to keep it. Refused on social posts.
pillarIdNoPillar id, or null to clear. With an angle, omit it or pass the angle's own pillar; any other value, null included, is refused.
scheduledAtNoProposed publish time as a UTC ISO string, or null.
guideVersionNoguideVersion from get_drafting_guide for this channel, after checking the draft against that guide.
translationOfIdNoWebsite posts only: the id of the post this one translates (the original). The original must be a website post in this workspace with a language, not itself a translation, and in another language than this post, which needs a language too; each language takes one translation. On Payload, Mark writes this post into the original's page in this post's language instead of making a new page, so publish the original first. Publishing either one makes every language of that page live. null clears it; omit to keep it.
buyerQuestionIdsNoWebsite posts only: the buyer question ids this page answers, from get_ai_visibility. Replaces the set; null or [] clears it; omit to keep it. Active questions only, except ones already linked. Refused on social posts. Sent alone with postId, it also changes a submitted or live website post, without review; refused once archived.

Output Schema

NameRequiredDescriptionDefault
revYes
ideaYesThe idea the post is linked to after this save.
appUrlYes
assetsYes
checksYesMark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder, on a competitor_page also table and open_placeholder, and on a website post with meta.seo.keyword also keyword. On a draft, a fail stops submit_post; warn never does.
guidedYesWhether the post was saved against the workspace's current drafting guide.
postIdYes
statusYes
channelYes
assetIdsYes
languageYesThe post's language as stored (canonical, e.g. de-CH), or null.
updatedAtYes
bodyLengthYes
contentHashYes
scheduledAtYes
guideVersionYesThe drafting guide version recorded on the post, or null.
translationOfYesThe post this one translates after this save, or null.
buyerQuestionsYesThe buyer questions the post answers after this save.
characterLimitYes
droppedAssetIdsYes
droppedMetaKeysYesMeta keys this channel does not take, at any depth, as paths like cover.url or cmsImages[0].alt, so the save ignored them. Empty when none.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / assetIds / description
      Previous value: -"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error. An X thread lists every asset in the thread here; meta.threadMedia says which reply each goes on. Website posts take no attachments: set meta.cover instead (assetIds may be omitted or hold only the cover id); other images go in the body as Markdown."New value: +"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error. An X thread lists every asset in the thread here; meta.threadMedia says which reply each goes on. Website posts take no attachments: set meta.cover instead (assetIds may be omitted or hold only the cover id); other images go in the body as Markdown. A library image goes in a website post body as ![alt](url), with the relative url from search_assets / add_asset (/api/assets/<id>/content); save stores that form whatever host you write."
  2. Changed1 schema field changed
    • changedOutput schema / properties / droppedMetaKeys / description
      Previous value: -"Top-level meta keys this channel does not take, so the save ignored them. Empty when none."New value: +"Meta keys this channel does not take, at any depth, as paths like cover.url or cmsImages[0].alt, so the save ignored them. Empty when none."
  3. Changed1 schema field changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram, LinkedIn and Facebook: altText is the post-wide alt text and altTexts is one alt text per image in media order; a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn, Facebook and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."New value: +"Channel-specific fields from get_channel_rules. Instagram, LinkedIn, Facebook and X: altText is the post-wide alt text and altTexts is one alt text per image in media order (on X, post 1's images, then each reply's, in thread order; when threadMedia moves media, send altTexts in the new order); a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn, Facebook, X and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
  4. Changed8 schema fields changed
    • addedInput schema / properties / language
      Added value: +{
      +  "description": "Website posts only: the language the post is written in, a BCP 47 code such as de or de-CH, stored canonical (de-ch saves as de-CH). A CMS that stores languages (Payload localization) gets it as the locale: the page is saved in that language, not the site's default. null or blank clears it; omit to keep it. Refused on social posts.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram and LinkedIn: altText is the post-wide alt text and altTexts is one alt text per image in media order; a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."New value: +"Channel-specific fields from get_channel_rules. Instagram, LinkedIn and Facebook: altText is the post-wide alt text and altTexts is one alt text per image in media order; a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn, Facebook and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
    • addedInput schema / properties / translationOfId
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "Website posts only: the id of the post this one translates (the original). The original must be a website post in this workspace with a language, not itself a translation, and in another language than this post, which needs a language too; each language takes one translation. On Payload, Mark writes this post into the original's page in this post's language instead of making a new page, so publish the original first. Publishing either one makes every language of that page live. null clears it; omit to keep it."
      +}
    • changedOutput schema / properties / checks / description
      Previous value: -"Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder, and on a competitor_page also table and open_placeholder. On a draft, a fail stops submit_post; warn never does."New value: +"Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder, on a competitor_page also table and open_placeholder, and on a website post with meta.seo.keyword also keyword. On a draft, a fail stops submit_post; warn never does."
    • changedOutput schema / properties / checks / items / properties / checkId / enum
      Previous value: -[
      -  "exclusion",
      -  "char_limit",
      -  "required_meta",
      -  "ai_fluff",
      -  "unsourced_number",
      -  "placeholder",
      -  "table",
      -  "open_placeholder"
      -]New value: +[
      +  "exclusion",
      +  "char_limit",
      +  "required_meta",
      +  "ai_fluff",
      +  "unsourced_number",
      +  "placeholder",
      +  "table",
      +  "open_placeholder",
      +  "keyword",
      +  "brand_fit"
      +]
    • addedOutput schema / properties / language
      Added value: +{
      +  "description": "The post's language as stored (canonical, e.g. de-CH), or null.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / translationOf
      Added value: +{
      +  "anyOf": [
      +    {
      +      "additionalProperties": false,
      +      "properties": {
      +        "id": {
      +          "type": "string"
      +        },
      +        "language": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "title": {
      +          "type": "string"
      +        }
      +      },
      +      "required": [
      +        "id",
      +        "language",
      +        "title"
      +      ],
      +      "type": "object"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The post this one translates after this save, or null."
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "appUrl",
      -  "assetIds",
      -  "assets",
      -  "bodyLength",
      -  "buyerQuestions",
      -  "channel",
      -  "characterLimit",
      -  "checks",
      -  "contentHash",
      -  "droppedAssetIds",
      -  "droppedMetaKeys",
      -  "idea",
      -  "guided",
      -  "guideVersion",
      -  "postId",
      -  "rev",
      -  "scheduledAt",
      -  "status",
      -  "updatedAt"
      -]New value: +[
      +  "appUrl",
      +  "assetIds",
      +  "assets",
      +  "bodyLength",
      +  "buyerQuestions",
      +  "channel",
      +  "characterLimit",
      +  "checks",
      +  "contentHash",
      +  "droppedAssetIds",
      +  "droppedMetaKeys",
      +  "idea",
      +  "guided",
      +  "guideVersion",
      +  "language",
      +  "postId",
      +  "rev",
      +  "scheduledAt",
      +  "status",
      +  "translationOf",
      +  "updatedAt"
      +]
  5. Changed2 schema fields changed
    • changedInput schema / properties / channel / enum
      Previous value: -[
      -  "instagram",
      -  "facebook",
      -  "youtube",
      -  "pinterest",
      -  "tiktok",
      -  "x",
      -  "linkedin",
      -  "linkedin_article",
      -  "blog",
      -  "faq",
      -  "competitor_page",
      -  "landing_page",
      -  "email"
      -]New value: +[
      +  "instagram",
      +  "facebook",
      +  "youtube",
      +  "pinterest",
      +  "tiktok",
      +  "x",
      +  "linkedin",
      +  "linkedin_article",
      +  "blog",
      +  "faq",
      +  "competitor_page"
      +]
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram and LinkedIn: altText is the post-wide alt text and altTexts is one alt text per image in media order; a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."New value: +"Channel-specific fields from get_channel_rules. Instagram and LinkedIn: altText is the post-wide alt text and altTexts is one alt text per image in media order; a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
  6. Changed3 schema fields changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in media order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:<channel>:<mediaId> for a CMS image (get_post shows the channel of each; cms::<mediaId> for an image sent without one). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a carousel, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media called with the same channel: on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."New value: +"Channel-specific fields from get_channel_rules. Instagram and LinkedIn: altText is the post-wide alt text and altTexts is one alt text per image in media order; a blank entry uses altText, and videos and PDFs take none. LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId, collection }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; collection is the media collection search_cms_media named (left out, the default from get_website_cms; one Mark cannot read is refused). Mark fills in the type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:~<collection>:<mediaId> for a CMS image (get_post shows the collection of each; an image saved before names a channel instead, cms:<channel>:<mediaId>; cms::<mediaId> for an image sent without either). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram, LinkedIn and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a post with several media, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, collection, alt? } with an image already in the connected CMS from search_cms_media called with the same channel (collection left out: the default for that channel; a cover comes only from the collection the cover field accepts, else where new media goes, and any other is refused; Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media (any readable collection): on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
    • addedOutput schema / properties / droppedMetaKeys
      Added value: +{
      +  "description": "Top-level meta keys this channel does not take, so the save ignored them. Empty when none.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "appUrl",
      -  "assetIds",
      -  "assets",
      -  "bodyLength",
      -  "buyerQuestions",
      -  "channel",
      -  "characterLimit",
      -  "checks",
      -  "contentHash",
      -  "droppedAssetIds",
      -  "idea",
      -  "guided",
      -  "guideVersion",
      -  "postId",
      -  "rev",
      -  "scheduledAt",
      -  "status",
      -  "updatedAt"
      -]New value: +[
      +  "appUrl",
      +  "assetIds",
      +  "assets",
      +  "bodyLength",
      +  "buyerQuestions",
      +  "channel",
      +  "characterLimit",
      +  "checks",
      +  "contentHash",
      +  "droppedAssetIds",
      +  "droppedMetaKeys",
      +  "idea",
      +  "guided",
      +  "guideVersion",
      +  "postId",
      +  "rev",
      +  "scheduledAt",
      +  "status",
      +  "updatedAt"
      +]
  7. Changed1 schema field changed
    • changedInput schema / properties / channel / enum
      Previous value: -[
      -  "instagram",
      -  "facebook",
      -  "youtube",
      -  "pinterest",
      -  "tiktok",
      -  "x",
      -  "linkedin",
      -  "blog",
      -  "faq",
      -  "competitor_page",
      -  "landing_page",
      -  "email"
      -]New value: +[
      +  "instagram",
      +  "facebook",
      +  "youtube",
      +  "pinterest",
      +  "tiktok",
      +  "x",
      +  "linkedin",
      +  "linkedin_article",
      +  "blog",
      +  "faq",
      +  "competitor_page",
      +  "landing_page",
      +  "email"
      +]
  8. Changed2 schema fields changed
    • changedOutput schema / properties / checks / description
      Previous value: -"Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder. On a draft, a fail stops submit_post; warn never does."New value: +"Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder, and on a competitor_page also table and open_placeholder. On a draft, a fail stops submit_post; warn never does."
    • changedOutput schema / properties / checks / items / properties / checkId / enum
      Previous value: -[
      -  "exclusion",
      -  "char_limit",
      -  "required_meta",
      -  "ai_fluff",
      -  "unsourced_number",
      -  "placeholder"
      -]New value: +[
      +  "exclusion",
      +  "char_limit",
      +  "required_meta",
      +  "ai_fluff",
      +  "unsourced_number",
      +  "placeholder",
      +  "table",
      +  "open_placeholder"
      +]
  9. Changed1 schema field changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. On Instagram and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a carousel, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media called with the same channel: on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in media order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. mediaOrder sets the order of the media on post 1 across assets and CMS images: a list of keys, an asset id as is or cms:<channel>:<mediaId> for a CMS image (get_post shows the channel of each; cms::<mediaId> for an image sent without one). An unknown or repeated key is dropped and media it leaves out goes last; without it, assets go first, then CMS images. altTexts follow this media order. meta without mediaOrder keeps the saved order; [] resets it. On Instagram and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a carousel, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media called with the same channel: on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
  10. Changed1 schema field changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media called with the same channel: on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. On Instagram and Pinterest, a newly attached CMS image with blank alt text takes the CMS alt (its altTexts entry on a carousel, else altText; on Pinterest only when it is the only image on the pin). Edit or clear it like any alt text. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media called with the same channel: on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
  11. Changed1 schema field changed
    • changedInput schema / properties / channel / enum
      Previous value: -[
      -  "instagram",
      -  "facebook",
      -  "youtube",
      -  "pinterest",
      -  "tiktok",
      -  "x",
      -  "linkedin",
      -  "blog",
      -  "faq",
      -  "competitor_page"
      -]New value: +[
      +  "instagram",
      +  "facebook",
      +  "youtube",
      +  "pinterest",
      +  "tiktok",
      +  "x",
      +  "linkedin",
      +  "blog",
      +  "faq",
      +  "competitor_page",
      +  "landing_page",
      +  "email"
      +]
  12. Changed1 schema field changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover. An image already in the CMS goes in the body as ![alt](url) on its own line, with url from search_cms_media called with the same channel: on a rich-text mapping it publishes by reference with no upload and shows the alt text the CMS has (get_channel_rules says which)."
  13. Changed3 schema fields changed
    • changedInput schema / properties / assetIds / description
      Previous value: -"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error. Website posts take no attachments: set meta.cover instead (assetIds may be omitted or hold only the cover id); other images go in the body as Markdown."New value: +"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error. An X thread lists every asset in the thread here; meta.threadMedia says which reply each goes on. Website posts take no attachments: set meta.cover instead (assetIds may be omitted or hold only the cover id); other images go in the body as Markdown."
    • changedInput schema / properties / buyerQuestionIds / description
      Previous value: -"Website posts only: the buyer question ids this page answers, from get_ai_visibility. Replaces the set; null or [] clears it; omit to keep it. Active questions only, except ones already linked. Refused on social posts."New value: +"Website posts only: the buyer question ids this page answers, from get_ai_visibility. Replaces the set; null or [] clears it; omit to keep it. Active questions only, except ones already linked. Refused on social posts. Sent alone with postId, it also changes a submitted or live website post, without review; refused once archived."
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Social posts (not YouTube): cmsImages: [{ mediaId }] attaches images already in the connected CMS (search_cms_media), after the assets on post 1; Mark fills in the channel, type and size of each, takes JPEG, PNG, WebP or GIF, and copies them to the platform at scheduling. They count toward media limits like assets. X: thread is the replies after the body, in order; threadMedia puts media on a reply: one list of asset ids per thread entry, in the same order ([[], [\"id\"]] puts id on post 3). Every threadMedia id must also be in assetIds and on one post only; post 1 gets the assetIds no reply lists. meta replaces the saved meta: when thread keeps its number of replies and threadMedia is left out, the saved threadMedia carries over; when the number changes, send threadMedia too. Each X thread post takes up to 4 images, or 1 video, or 1 GIF. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover."
  14. Changed2 schema fields changed
    • changedOutput schema / properties / checks / description
      Previous value: -"Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number. On a draft, a fail stops submit_post; warn never does."New value: +"Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number, placeholder. On a draft, a fail stops submit_post; warn never does."
    • changedOutput schema / properties / checks / items / properties / checkId / enum
      Previous value: -[
      -  "exclusion",
      -  "char_limit",
      -  "required_meta",
      -  "ai_fluff",
      -  "unsourced_number"
      -]New value: +[
      +  "exclusion",
      +  "char_limit",
      +  "required_meta",
      +  "ai_fluff",
      +  "unsourced_number",
      +  "placeholder"
      +]
  15. Changed1 schema field changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). LinkedIn: documentTitle is the line LinkedIn shows above an attached PDF; blank uses the file name. Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover."
  16. Changed1 schema field changed
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset; alt is required before submit when the CMS takes a cover. Omit cover for no cover."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset, or cover: { source: \"cms\", mediaId, alt? } with an image already in the connected CMS from search_cms_media (Mark fills its type and size, alt defaults to the CMS alt, and an edited alt is written back to that CMS image on approve). alt is required before submit when the CMS takes a cover. Omit cover for no cover."
  17. Changed2 schema fields changed
    • changedInput schema / properties / assetIds / description
      Previous value: -"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error."New value: +"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error. Website posts take no attachments: set meta.cover instead (assetIds may be omitted or hold only the cover id); other images go in the body as Markdown."
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none)."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none). Website posts (blog, faq, landing_page, competitor_page): the cover is cover: { source: \"asset\", assetId, alt } with an image id from search_assets / add_asset; alt is required before submit when the CMS takes a cover. Omit cover for no cover."
  18. Changed1 schema field changed
    • addedInput schema / properties / rev
      Added value: +{
      +  "description": "The post's current rev from get_post. Required to edit a post in review, approved or scheduled; ignored on drafts.",
      +  "maximum": 9007199254740991,
      +  "minimum": 0,
      +  "type": "integer"
      +}
  19. Changed5 schema fields changed
    • addedInput schema / properties / guideVersion
      Added value: +{
      +  "description": "guideVersion from get_drafting_guide for this channel, after checking the draft against that guide.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / checks
      Added value: +{
      +  "description": "Mark's content checks on the saved content: exclusion, char_limit, required_meta, ai_fluff, unsourced_number. On a draft, a fail stops submit_post; warn never does.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "checkId": {
      +        "enum": [
      +          "exclusion",
      +          "char_limit",
      +          "required_meta",
      +          "ai_fluff",
      +          "unsourced_number"
      +        ],
      +        "type": "string"
      +      },
      +      "matches": {
      +        "items": {
      +          "additionalProperties": false,
      +          "properties": {
      +            "end": {
      +              "type": "number"
      +            },
      +            "exclusionId": {
      +              "type": "string"
      +            },
      +            "field": {
      +              "type": "string"
      +            },
      +            "reason": {
      +              "type": "string"
      +            },
      +            "severity": {
      +              "enum": [
      +                "block",
      +                "warn"
      +              ],
      +              "type": "string"
      +            },
      +            "start": {
      +              "type": "number"
      +            },
      +            "text": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "field",
      +            "start",
      +            "end",
      +            "text"
      +          ],
      +          "type": "object"
      +        },
      +        "type": "array"
      +      },
      +      "status": {
      +        "enum": [
      +          "pass",
      +          "warn",
      +          "fail"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "checkId",
      +      "status",
      +      "matches"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / guideVersion
      Added value: +{
      +  "description": "The drafting guide version recorded on the post, or null.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / guided
      Added value: +{
      +  "description": "Whether the post was saved against the workspace's current drafting guide.",
      +  "type": "boolean"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "appUrl",
      -  "assetIds",
      -  "assets",
      -  "bodyLength",
      -  "buyerQuestions",
      -  "channel",
      -  "characterLimit",
      -  "contentHash",
      -  "droppedAssetIds",
      -  "idea",
      -  "postId",
      -  "rev",
      -  "scheduledAt",
      -  "status",
      -  "updatedAt"
      -]New value: +[
      +  "appUrl",
      +  "assetIds",
      +  "assets",
      +  "bodyLength",
      +  "buyerQuestions",
      +  "channel",
      +  "characterLimit",
      +  "checks",
      +  "contentHash",
      +  "droppedAssetIds",
      +  "idea",
      +  "guided",
      +  "guideVersion",
      +  "postId",
      +  "rev",
      +  "scheduledAt",
      +  "status",
      +  "updatedAt"
      +]
  20. Changed3 schema fields changed
    • addedInput schema / properties / ideaId
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "Idea id from list_ideas, or null to unlink. Archived ideas can't be newly linked. On a new draft, unset pillar, angle, buyer questions and body come from the idea."
      +}
    • addedOutput schema / properties / idea
      Added value: +{
      +  "anyOf": [
      +    {
      +      "additionalProperties": false,
      +      "properties": {
      +        "archived": {
      +          "type": "boolean"
      +        },
      +        "id": {
      +          "type": "string"
      +        },
      +        "title": {
      +          "type": "string"
      +        }
      +      },
      +      "required": [
      +        "archived",
      +        "id",
      +        "title"
      +      ],
      +      "type": "object"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "The idea the post is linked to after this save."
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "appUrl",
      -  "assetIds",
      -  "assets",
      -  "bodyLength",
      -  "buyerQuestions",
      -  "channel",
      -  "characterLimit",
      -  "contentHash",
      -  "droppedAssetIds",
      -  "postId",
      -  "rev",
      -  "scheduledAt",
      -  "status",
      -  "updatedAt"
      -]New value: +[
      +  "appUrl",
      +  "assetIds",
      +  "assets",
      +  "bodyLength",
      +  "buyerQuestions",
      +  "channel",
      +  "characterLimit",
      +  "contentHash",
      +  "droppedAssetIds",
      +  "idea",
      +  "postId",
      +  "rev",
      +  "scheduledAt",
      +  "status",
      +  "updatedAt"
      +]
  21. Changed3 schema fields changed
    • addedInput schema / properties / buyerQuestionIds
      Added value: +{
      +  "anyOf": [
      +    {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "description": "Website posts only: the buyer question ids this page answers, from get_ai_visibility. Replaces the set; null or [] clears it; omit to keep it. Active questions only, except ones already linked. Refused on social posts."
      +}
    • addedOutput schema / properties / buyerQuestions
      Added value: +{
      +  "description": "The buyer questions the post answers after this save.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "question": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "question"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "appUrl",
      -  "assetIds",
      -  "assets",
      -  "bodyLength",
      -  "channel",
      -  "characterLimit",
      -  "contentHash",
      -  "droppedAssetIds",
      -  "postId",
      -  "rev",
      -  "scheduledAt",
      -  "status",
      -  "updatedAt"
      -]New value: +[
      +  "appUrl",
      +  "assetIds",
      +  "assets",
      +  "bodyLength",
      +  "buyerQuestions",
      +  "channel",
      +  "characterLimit",
      +  "contentHash",
      +  "droppedAssetIds",
      +  "postId",
      +  "rev",
      +  "scheduledAt",
      +  "status",
      +  "updatedAt"
      +]
  22. Changed2 schema fields changed
    • changedInput schema / properties / angleId / description
      Previous value: -"Angle id, or null to clear."New value: +"Angle id, or null to clear. Social posts only; refused on website posts. Sets the post's pillar and persona to the angle's."
    • changedInput schema / properties / pillarId / description
      Previous value: -"Pillar id, or null to clear."New value: +"Pillar id, or null to clear. With an angle, omit it or pass the angle's own pillar; any other value, null included, is refused."
  23. Changed2 schema fields changed
    • changedInput schema / properties / assetIds / description
      Previous value: -"Asset ids from search_assets / add_asset, in order."New value: +"Asset ids from search_assets / add_asset, in order. Instagram takes at most 10; more is refused with an assetIds error."
    • changedInput schema / properties / meta / description
      Previous value: -"Channel-specific fields from get_channel_rules."New value: +"Channel-specific fields from get_channel_rules. Instagram: altTexts is one alt text per slide in assetIds order (blank entries and videos use altText or none)."
  24. Changed10 schema fields changed
    • addedOutput schema / properties / assetIds
      Added value: +{
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / assets
      Added value: +{
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "type": "string"
      +      },
      +      "name": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "kind",
      +      "name"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / bodyLength
      Added value: +{
      +  "type": "number"
      +}
    • addedOutput schema / properties / channel
      Added value: +{
      +  "type": "string"
      +}
    • addedOutput schema / properties / characterLimit
      Added value: +{
      +  "type": [
      +    "number",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / contentHash
      Added value: +{
      +  "type": "string"
      +}
    • addedOutput schema / properties / droppedAssetIds
      Added value: +{
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / scheduledAt
      Added value: +{
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / updatedAt
      Added value: +{
      +  "type": "string"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "appUrl",
      -  "postId",
      -  "rev",
      -  "status"
      -]New value: +[
      +  "appUrl",
      +  "assetIds",
      +  "assets",
      +  "bodyLength",
      +  "channel",
      +  "characterLimit",
      +  "contentHash",
      +  "droppedAssetIds",
      +  "postId",
      +  "rev",
      +  "scheduledAt",
      +  "status",
      +  "updatedAt"
      +]
  25. First observed

TDQS

A4.6/5.0
Behavior5/5

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

Beyond the annotations, the description discloses the save receipt fields, rev behavior, status transitions, refusal cases, dropped assets, dropped meta keys, and channel-specific media constraints. The annotation destructiveHint=false is not contradicted because the tool saves/edits without deleting or publishing.

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?

The description is extremely long and dense, with many edge cases packed into a single block rather than structured for scanning. Much of the parameter detail duplicates the already complete schema, making it less concise than it could be despite the tool's complexity.

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?

Given 14 parameters, complex status rules, and cross-channel behavior, the description is thorough enough for an agent to call the tool correctly. Although an output schema exists, the description still adds useful context about the save receipt and constraints.

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 100%, so the schema already documents all 14 parameters. The description adds valuable cross-parameter and status-dependent semantics, such as rev requirements for review/approved/scheduled posts, angle/pillar conflicts, idea defaults, and buyerQuestionIds alone changing submitted or live website posts.

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 description opens with a precise verb and resource: it creates a new draft or edits an existing post, and explicitly says it never submits or publishes. It distinguishes the tool from submit_post, edit_published_post, and save_idea by naming those alternatives and their purposes.

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?

It gives explicit when-to-use and when-not-to-use guidance, including calling get_channel_rules first and submit_post afterward for review. It also routes agents to alternatives for locked posts, published social edits, and linking ideas without approval changes.

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