← BulkPublishCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to BulkPublish
Snapshot Sep 30, 2026 · 23:08 UTC · version 2.0.5
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"name": "schedule-post",
"description": "Create, schedule, or publish social media posts via BulkPublish MCP. Use when the user wants to post to social media.",
"included_files": [],
"skill_md_contents": "---\nname: schedule-post\ndescription: Create, schedule, or publish social media posts via BulkPublish MCP. Use when the user wants to post to social media.\n---\n\n# BulkPublish — Post Creation Reference\n\n## create_post parameters\n\n```\ncontent (string, required) — post text\nchannels (array, required) — [{channelId: number, platform: string}]\n Get these from list_channels first\nstatus (\"draft\"|\"scheduled\") — default \"draft\"\nscheduledAt (ISO 8601 string) — required when status is \"scheduled\"\ntimezone (string) — e.g. \"America/New_York\", \"Asia/Karachi\"\nmediaFileIds (number[]) — IDs from upload_media\nplatformContent (object) — per-platform text: {\"x\": \"Short\", \"linkedin\": \"Longer version\"}\npostTypeOverrides (object) — per-platform format: {\"instagram\": \"reel\", \"facebook\": \"story\"}\npostFormat (\"post\"|\"video\"|\"reel\"|\"story\"|\"carousel\"|\"thread\") — \"thread\" requires threadParts\nthreadParts (array) — [{content: string, mediaFileIds?: number[]}], min 2 parts.\n EVERY part is length-checked against every target platform,\n not just the first — an over-long part 400s the whole request\nplatformSpecific (object) — per-platform options; see the\n platform-reference skill. Auto-reply after\n publishing goes here as the top-level key\n \"_firstComment\", NOT a firstComment param:\n {\"_firstComment\": \"Link in bio!\"}\n Unsupported on discord, pinterest, tiktok,\n gmb, tumblr and snapchat (recorded as failed;\n the main post still publishes)\nrequestApproval (boolean) — default false; hold a scheduled post for team\n approval (approvalStatus becomes \"pending\")\nlinkTrackingOverride (boolean|null) — default null; per-post override for\n bulkpubli.sh link tracking. true shortens the\n post's links and counts clicks, false posts\n them as written, null inherits the org setting\n```\n\n`linkTrackingOverride` is tri-state, so **omit it unless the user actually asked\nfor one behaviour or the other** — sending `false` is an explicit \"post the links\nas written\" and is not the same as leaving it unset. Shortening happens at\npublish time, per channel, and is **skipped** for any channel where the rewrite\nwould push the post past that platform's character limit: a short URL is 28\ncharacters and can be longer than the link it replaces, so on X (280) or Bluesky\n(300) tracking may silently not apply. The post still publishes, with its\noriginal links.\n\nEvery post object returned by the API also carries the read-only approval fields\n`approvalStatus` (`\"none\"` default | `\"pending\"` | `\"approved\"` | `\"rejected\"`),\n`approvedBy` (string|null), `approvedAt` (date-time|null) and `rejectionReason`\n(string|null). See \"Team approval\" below.\n\n## Post type overrides\n\n| Platform | Types |\n|---|---|\n| Instagram | `reel`, `story`, `carousel` |\n| Facebook | `story`, `reel` |\n| TikTok | `slideshow` |\n| YouTube | `short` |\n| X/Twitter | `thread` |\n| Threads | `thread` |\n| Bluesky | `thread` |\n| Mastodon | `thread` |\n\n## Team approval\n\n`approvalStatus` is **orthogonal to `status`**: the scheduler skips `pending` and\n`rejected` posts even when they are scheduled and overdue. Default is `\"none\"`.\n\n- **Requesting approval** — pass `requestApproval: true` on `create_post` or\n `update_post` (default `false`) to hold a scheduled post for team approval;\n `approvalStatus` becomes `\"pending\"`. For API keys belonging to members whose\n role lacks `post:publish` (contributors), this is **forced server-side\n regardless of the flag** — their scheduled posts always land in the approval\n queue. Never tell such a user their post was scheduled: check the returned\n `approvalStatus` and say it is awaiting approval.\n- **The approval queue** — `list_posts` with `approvalStatus: \"pending\"` (the\n filter accepts `none` | `pending` | `approved` | `rejected`), or\n `GET /api/posts?approvalStatus=pending`.\n- **Approving or rejecting** — done by a teammate with an approver role (owner, admin, approver) in BulkPublish. Approval releases the post: it publishes at its scheduled time, or immediately if that time has passed. A rejected post returns to draft with a reason, and the author can edit and resubmit. You cannot approve or reject from here — say who needs to act.\n- **`APPROVAL_REQUIRED`** — `publish_post` and `retry_post` return **403** with\n error code `APPROVAL_REQUIRED` for roles without `post:publish`. Do not retry:\n create/update the post with `requestApproval: true` and tell the user a\n teammate has to approve it. Publishing a pending/rejected post *as an\n approver* implicitly approves it.\n\n## Publishing flow\n\n- **Draft then publish**: `create_post` (status: \"draft\") → `publish_post` (postId)\n- **Schedule for later**: `create_post` (status: \"scheduled\", scheduledAt: \"2026-04-12T09:00:00Z\")\n- **Schedule with review**: `create_post` (status: \"scheduled\", scheduledAt: ..., requestApproval: true) → a teammate approves it in BulkPublish\n- **Optimal timing**: call `get_queue_slot` (optionally pass `timezone`, default UTC) to get the best next slot. It returns `{suggestedTime, timezone}` — it does NOT take a channelId or date (any such args are ignored).\n- **Retry failures**: `retry_post` (postId, optional `republish`) re-queues the\n post's `failed` platforms. A platform can also end in status `unconfirmed` —\n terminal: the publish request may have reached the platform but its response\n was lost, so the post **may already be live**; it is never auto-retried. If\n the post has unconfirmed platforms and no failed ones, `retry_post` returns\n **400** with code `UNCONFIRMED_REQUIRES_REPUBLISH` — ask the user to check\n the account on the platform, and only pass `republish: true` (default false)\n after they confirm the post is not live; it also retries the unconfirmed\n platforms and **can duplicate the post**.\n- **Publish as story**: set `postTypeOverrides` to `\"story\"` for Facebook/Instagram/Snapchat — publishes directly as a story, no separate call needed (Snapchat stories need exactly 1 image or video and send no caption)\n\n## Stories vs postTypeOverrides\n\nTo publish as a story, use `postTypeOverrides` at creation time:\n```json\n{ \"postTypeOverrides\": { \"facebook\": \"story\", \"instagram\": \"story\" } }\n```\nThis publishes the post AS a story. Set it at creation time — an existing regular post cannot be turned into a story from here.\n\n## Character limits\n\n| Platform | Limit |\n|---|---|\n| X/Twitter | 280 (25,000 long posts) |\n| Instagram | 2,200 |\n| Facebook | 63,206 |\n| LinkedIn | 3,000 |\n| TikTok | 2,200 |\n| YouTube | 5,000 (description) |\n| Threads | 500 |\n| Bluesky | 300 |\n| Pinterest | 500 |\n| Google Business | 1,500 |\n| Mastodon | 500 |\n| Snapchat | 160 (Spotlight description / Saved Story title fallback — plain stories send no text) |\n\n## Platform media requirements\n\n| Platform | Requires | Notes |\n|---|---|---|\n| YouTube | Video ONLY | Do NOT include YouTube for image-only posts |\n| TikTok | Video ONLY | Or images for `photo_slideshow` type |\n| Instagram | Depends on type | `feed_photo`=image, `reel`/`feed_video`=video, `carousel`=2-10 mixed |\n| Pinterest | Image or video | Needs board ID in `platformSpecific` or channel default |\n| Facebook/X/LinkedIn/Threads/Bluesky/Mastodon | Any or none | Text-only posts OK |\n| Snapchat | Exactly 1 image or video | jpg/png or mp4/mov, vertical, 5–60s (Spotlight 6–60s video-only), max 1GB |\n\n## Common mistakes\n\n- `channels` takes objects `{channelId, platform}`, NOT just IDs\n- Always call `list_channels` first to get valid channelId + platform pairs\n- `scheduledAt` must be in the future and in ISO 8601 format\n- To publish immediately: create as draft, then call `publish_post`\n- `mediaFileIds` are numbers from `upload_media`, not file paths\n- **Do NOT send image-only posts to YouTube or TikTok** — they will fail\n- **Instagram defaults to `feed_photo`** — set `postTypeOverrides.instagram` to `reel` or `feed_video` for video\n- **Pinterest needs a board ID** — set via `platformSpecific.pinterest.boardId` or it tries to auto-create one\n- **Never assume a scheduled post will go out** — if the response has\n `approvalStatus: \"pending\"`, it is held until someone approves it. Report that,\n not \"scheduled\".\n- **`requestApproval` defaults to `false`** and `approvalStatus` defaults to\n `\"none\"` — only set the flag when the user asks for review, but always read the\n response back because contributors get it forced on.\n- **Do not call `publish_post` again after a 403 `APPROVAL_REQUIRED`** — the role\n cannot publish; submit for approval instead.\n- **Content char limits** are enforced per-platform — use `platformContent` for shorter overrides on Pinterest (500), Bluesky (300), etc.\n"
}SHA-256: 9d1401341d7ce2fd455f448a51d8b2b1f0252b0eb861b44217b89374afc3489a