{"id":13915,"plugin_id":"plugin_asdk_app_6aa1e36d0a108191b38d12b95e3d5568","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:08:25.731Z","digest":"9d1401341d7ce2fd455f448a51d8b2b1f0252b0eb861b44217b89374afc3489a","against":null,"payload":{"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"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}