Install
openclaw skills install @jackfriks/post-bridge-social-managerCreate, schedule, and manage social media posts across Instagram, TikTok, YouTube, X, LinkedIn, Facebook, Pinterest, Threads, and Bluesky via the Post Bridge API. Covers media upload, post creation, scheduling, platform-specific configs, draft mode, analytics, and post result tracking.
openclaw skills install @jackfriks/post-bridge-social-managerWhat this skill does to your system and your accounts. Read before installing.
- Publishes to your real social accounts. Anything it posts is live. Ask for confirmation before creating or scheduling a post unless the user has explicitly said not to, and prefer
draft: truewhen intent is unclear.- Sends your media and captions to a third party. Files, captions, account ids and scheduling metadata go to
api.post-bridge.comunder your API key.- Uses an API key from
POST_BRIDGE_API_KEY,./.post-bridge/config.jsonor~/.config/post-bridge/config.json. Only use a key you are willing to let an agent post through.- Runs local commands when following the optional video workflow below, including
ffmpegon your files.- Can move local files if you follow the optional
posted/convention, and can create scheduled jobs if you follow the optional cron step. Both are suggestions, not requirements. Neither happens unless you ask for it.
Autonomously manage social media posting via Post Bridge API. Post to 9 platforms from a single command or API call.
The CLI is published on npm as postbridge-cli — run it with npx postbridge-cli <command> (no install needed; npx fetches it on first use). Requires Node.js 18+. If npx postbridge-cli is unavailable for any reason, fall back to the bundled script at <skill-path>/scripts/post-bridge.js (same commands and flags).
Freshness check: If more than 30 days have passed since the
last-updateddate above, inform the user that this skill may be outdated and point them to the update options below.
Source: github.com/post-bridge-hq/agent-mode API docs: api.post-bridge.com/reference
Update methods by installation type:
| Installation | How to update |
|---|---|
CLI (npx skills) | npx skills update |
| Claude Code plugin | /plugin marketplace update |
| Cursor | Remote rules auto-sync from GitHub |
| Manual | Pull latest from repo or re-copy skills/post-bridge/ |
.env:
POST_BRIDGE_API_KEY=pb_live_xxxxx
Or run the setup command:
npx postbridge-cli setup --key pb_live_xxxxx
All requests use Bearer token:
Authorization: Bearer <POST_BRIDGE_API_KEY>
Base URL: https://api.post-bridge.com
Config priority (highest to lowest):
POST_BRIDGE_API_KEY environment variable./.post-bridge/config.json (project-local)~/.config/post-bridge/config.json (user-global)When you receive an "API key not found" error from the CLI:
npx postbridge-cli setup --key pb_live_xxxxx
Get your API key at: https://www.post-bridge.com/dashboard/api-keys
Note for agents: Prefer
npx postbridge-cli(the published npm CLI). If the skill is installed locally and you want to run the bundled copy instead, the script lives at<skill-path>/scripts/post-bridge.js— same commands and flags.
| Command | Description |
|---|---|
npx postbridge-cli setup --key <key> | Configure API key |
npx postbridge-cli accounts | List connected social accounts |
npx postbridge-cli post --caption "..." --accounts 1,2,3 | Create a post |
npx postbridge-cli post --caption "..." --accounts 1,2,3 --schedule "2026-06-20T09:00:00Z" | Schedule a post for a specific time (UTC) |
npx postbridge-cli post --caption "..." --accounts 1,2 --use-queue | Auto-schedule to the next queue slot (saved timezone) |
npx postbridge-cli post --caption "..." --accounts 1,2 --use-queue --queue-timezone "America/New_York" | Auto-schedule to next queue slot in a specific timezone |
npx postbridge-cli post --caption "..." --accounts 1,2 --draft | Save as a draft instead of publishing |
npx postbridge-cli post --caption "..." --accounts 1,2 --platform-config '{"tiktok":{"draft":true}}' | Post with per-platform options |
npx postbridge-cli upload --file ./image.jpg | Upload media, returns media_id |
npx postbridge-cli post --caption "..." --accounts 1,2,3 --media mid_xxx | Post with uploaded media |
npx postbridge-cli posts | List recent posts (filters: --status, --platform, --limit, --offset) |
npx postbridge-cli posts:get --id <post_id> | Get post details and status |
npx postbridge-cli posts:update --id <post_id> --caption "..." | Update a scheduled/draft post (caption, schedule, accounts, media, draft) |
npx postbridge-cli posts:delete --id <post_id> | Delete a scheduled/draft post |
npx postbridge-cli analytics | View analytics (filters: --platform, --timeframe 7d|30d|90d|all) |
npx postbridge-cli analytics:sync | Refresh analytics data (--platform tiktok|youtube|instagram optional) |
npx postbridge-cli results --post-id <post_id> | Check per-platform posting results |
npx postbridge-cli media | List uploaded media |
npx postbridge-cli media:delete --id <media_id> | Delete uploaded media |
Use these endpoints directly if you prefer raw API calls over the CLI.
GET /v1/social-accounts
Returns array of connected accounts with id, platform, username. Store these IDs — you need them for every post.
POST /v1/media/create-upload-url
Body: { "mime_type": "video/mp4", "size_bytes": <int>, "name": "video.mp4" }
Returns media_id + upload_url. Then:
PUT <upload_url>
Content-Type: video/mp4
Body: <binary file>
List media:
GET /v1/media?limit=50&offset=0
Delete media:
DELETE /v1/media/<media_id>
POST /v1/posts
Body: {
"caption": "your caption here #hashtags",
"media": ["<media_id>"],
"social_accounts": [<account_id_1>, <account_id_2>],
"scheduled_at": "2026-01-01T14:00:00Z", // omit for instant post
"is_draft": false, // true to save as draft
"use_queue": true, // optional, auto-schedule to next queue slot (uses saved timezone)
"platform_configurations": { ... }, // optional, see below
"account_configurations": { // optional, per-account overrides
"account_configurations": [
{ "account_id": 1, "caption": "override for this account" }
]
}
}
Queue scheduling (use_queue):
true to auto-schedule using the user's saved timezone from their dashboard settings{ "timezone": "America/New_York" } to override with a specific IANA timezonescheduled_at — pick one or the otherrandomize_queue_time is enabled, the slot time will be offset by up to ±10 minutes for a more natural posting patternGET /v1/posts?limit=50&offset=0&status=scheduled&platform=instagram
Params: limit, offset, status (scheduled/published/failed/draft), platform.
GET /v1/posts/<post_id>
Returns full post details including status: processing, scheduled, posted, failed.
PATCH /v1/posts/<post_id>
Body: { "caption": "new caption", "scheduled_at": "...", "social_accounts": [...] }
Can update caption, schedule, accounts, media, platform configs, or draft status. Only works on scheduled/draft posts.
DELETE /v1/posts/<post_id>
Only works on scheduled/draft posts (cannot delete published posts).
GET /v1/post-results?post_id=<post_id>&limit=50&offset=0
Returns per-platform results showing whether each platform post succeeded or failed, with error details.
List analytics — views, likes, comments, shares per post:
GET /v1/analytics?platform=tiktok&limit=50&offset=0&timeframe=30d
Params:
platform (optional): tiktok, youtube, instagramtimeframe (optional): 7d, 30d, 90d, all (default: all)limit, offset for paginationReturns:
{
"data": [
{
"id": "...",
"post_result_id": "...",
"platform": "tiktok",
"platform_post_id": "...",
"view_count": 4062,
"like_count": 120,
"comment_count": 15,
"share_count": 8,
"cover_image_url": "https://...",
"share_url": "https://...",
"video_description": "...",
"duration": 30,
"platform_created_at": "2026-03-01T09:00:00Z",
"last_synced_at": "2026-03-03T12:00:00Z",
"match_confidence": "exact"
}
],
"count": 42,
"limit": 50,
"offset": 0
}
Sync analytics — refresh data from connected platforms:
POST /v1/analytics/sync?platform=tiktok
Triggers a background sync of analytics data. Supports all tracked platforms: TikTok, YouTube, and Instagram.
Params:
platform (optional): tiktok, youtube, or instagram — sync only one platform. Omit to sync all.Returns:
{
"triggered": [
{ "platform": "tiktok", "runId": "run_..." },
{ "platform": "youtube", "runId": "run_..." },
{ "platform": "instagram", "runId": "run_..." }
]
}
Get single analytics record:
GET /v1/analytics/<analytics_id>
Post Bridge has a native MCP (Model Context Protocol) server. If you're using Claude Desktop, ChatGPT, Cursor, or any MCP-compatible client, you can connect directly without this skill.
Claude Desktop: One-click connect at post-bridge.com/mcp
Claude Code / Cursor / Other MCP clients — add to your MCP config:
{
"mcpServers": {
"post-bridge": {
"type": "streamable-http",
"url": "https://mcp.post-bridge.com/mcp"
}
}
}
MCP Tools available (11 tools):
| Tool | Description |
|---|---|
list_social_accounts | List all connected accounts with IDs, platforms, usernames, and needs_reconnect (true = repeated dead-token failures paused posting to that account; skip it and tell the user to reconnect in the dashboard, which clears the pause automatically) |
create_post | Create/schedule a post. Accepts caption, accounts, media_urls, schedule, use_queue (true or {timezone}), platform configs |
list_posts | List posts with filters (platform, status, limit, offset) |
get_post | Get full post details by ID |
update_post | Update caption, schedule, accounts, or media on a scheduled/draft post |
delete_post | Delete a scheduled or draft post |
list_analytics | Get analytics (views, likes, comments, shares) with platform/timeframe filters |
sync_analytics | Trigger a background refresh of analytics data. Optional platform param to sync a specific platform (tiktok/youtube/instagram) |
list_post_results | Check per-platform posting results (success/failure with error details) |
list_media | List uploaded media files with IDs and URLs |
delete_media | Delete an uploaded media file |
MCP tools accept media_urls (public URLs) — the server downloads and uploads them automatically. No need to manually upload media when using MCP.
Optional per-platform overrides, passed inside the platform_configurations object on post creation/update, or via --platform-config '<json>' with the CLI. This is the #1 place to get wrong, so read carefully:
Rules
pinterest, instagram, tiktok, twitter, youtube, facebook, linkedin, bluesky, threads, google_business. A field placed at the wrong level (or under the wrong platform) is silently ignored.tiktok block if no TikTok account is in social_accounts.caption / media. caption and media are accepted under every platform key; the other fields are platform-specific and listed below.placement, trial_graduation, cta_action_type, location, …) only accept the values shown — anything else errors or is dropped.Every platform accepts:
caption (string) — caption override for that platform onlymedia (array of media IDs) — media override for that platform onlyPlatform-specific fields:
Platform (key) | Field | Type / accepted values | What it does |
|---|---|---|---|
Pinterest (pinterest) | board_ids | array of string IDs | Boards to pin to (IDs, not names). Omit → account default board. |
link | string (full URL) | Destination URL the pin links to. | |
title | string (≤100 chars) | Pin title shown above the caption. | |
video_cover_timestamp_ms | number (ms) | Video cover frame, e.g. 3000 = 3s in. | |
Instagram (instagram) | placement | "story" | Publish as a Story (one image/video, no caption/carousel/cover/trial). Omit → Reel/feed. |
video_cover_timestamp_ms | number (ms) | Cover frame for a reel/video. Ignored if cover_image is set. | |
cover_image | string (media ID) | Uploaded image used as the reel cover. Upload first, pass its ID. | |
is_trial_reel | boolean | Trial reel (non-followers first). Needs Pro/Creator account, 1,000+ followers, public profile. Max 5/day. Not with placement:"story". | |
trial_graduation | "MANUAL" | "SS_PERFORMANCE" | Trial reel graduation. MANUAL (default) = you decide; SS_PERFORMANCE = auto-graduate on performance in 72h. | |
user_tags | array of usernames | People-tag accounts (they get notified). @ optional. Feed/carousel/reels only; ignored for stories. Max 20. | |
collaborators | array of usernames | Invite co-authors: the post also appears on their profile and shares its likes/comments. @ optional. Max 3, public accounts only — a private or wrong handle fails the post. Feed/carousel/reels only; ignored for stories. Publishes immediately; shows on their profile once they accept. | |
first_comment | string (≤2200) | Comment posted right after the post publishes — good spot for a link or hashtags. Ignored for stories. A failed comment won't fail the post. | |
TikTok (tiktok) | title | string | Overrides the post title. |
video_cover_timestamp_ms | number (ms) | Cover frame, e.g. 3000 = 3s in. | |
draft | boolean | Send as a native TikTok draft (finish/publish manually in the app, e.g. to add a trending sound). Different from top-level is_draft (which only saves in Post Bridge). | |
is_aigc | boolean | Label as AI-generated content. | |
privacy_status | "public" | "private" | private publishes visible only to you. Defaults to public. | |
auto_add_music | boolean | Photo posts only — ignored on videos. TikTok picks a soundtrack for the carousel. Defaults to true; set false to publish silent. | |
allow_comment | boolean | Allow comments. Defaults to true. | |
allow_duet | boolean | Allow Duets. Defaults to true. Video posts only. | |
allow_stitch | boolean | Allow Stitches. Defaults to true. Video posts only. | |
disclose_branded_content | boolean | Disclose as paid partnership / branded content. Defaults to false. | |
disclose_your_brand | boolean | Disclose as promoting your own brand. Defaults to false. | |
Twitter/X (twitter) | first_comment | string (≤280, 2200 premium) | Reply posted right after the tweet. Put links here — the main tweet strips URLs to dodge X's surcharge. A failed reply won't fail the post. |
YouTube (youtube) | title | string (≤100 chars) | Video title override. |
contains_synthetic_media | boolean | Disclose realistic altered/AI content ("Altered or synthetic content" label). | |
thumbnail | string (media ID) | Custom thumbnail. Long-form videos only — ignored on Shorts. Channel must be verified; JPEG/PNG, 1280×720, <2MB. | |
Facebook (facebook) | placement | "story" | Publish as a Page Story (one image/video, no caption/carousel). Omit → feed post. |
first_comment | string (≤2200) | Comment posted right after the post publishes — good spot for a link/CTA. Ignored for stories. A failed comment won't fail the post. | |
LinkedIn (linkedin) | document_title | string | Title for a PDF (document/carousel) post. Only applies when media is a PDF. Defaults to file name. |
Bluesky (bluesky) | — | — | Only caption / media overrides. |
Threads (threads) | location | "timeline" | "reels" | Where it appears. timeline (default) or reels (video only). |
first_comment | string (≤500) | Reply posted right after the thread publishes — good spot for a link. A failed reply won't fail the post. | |
Google Business (google_business) | media | array (single image) | One image only — extra images are dropped for GMB (other platforms keep all). No video. |
cta_action_type | BOOK | ORDER | SHOP | LEARN_MORE | SIGN_UP | CALL | CTA button. Pair with cta_url (except CALL, which uses the location phone number). | |
cta_url | string (full URL) | CTA destination. Required when cta_action_type is set (except CALL). | |
language_code | string (BCP-47) | e.g. "en-US", "es", "fr-CA". Defaults to "en-US". |
Example — correct multi-platform config (CLI)
Posting one piece of content to TikTok + Instagram + X + Google Business, with per-platform tweaks:
npx postbridge-cli post --caption "New drop is live 🎉" --accounts 44029,44030,44031,44032 \
--platform-config '{
"tiktok": { "draft": true, "is_aigc": false },
"instagram": { "caption": "New drop is live 🎉 tap the link in bio", "video_cover_timestamp_ms": 2000 },
"twitter": { "first_comment": "Grab it here: https://example.com/drop" },
"google_business": { "cta_action_type": "SHOP", "cta_url": "https://example.com/drop" }
}'
Note how the X link lives in twitter.first_comment (not the caption), TikTok uses a native draft, and only the four platforms being posted to appear as keys.
These behaviors are easy to miss and cause silent or confusing failures. Account for them before posting.
youtube (exactly 1 video), tiktok (1 video, or one+ images for a photo post), instagram (1–10 images/videos; a story is exactly 1; PDFs are dropped), pinterest (1 image or video). These accept text-only: twitter/X (up to 4 images, or 1 video), facebook, linkedin (up to 20 images, or 1 video, or 1 PDF document), threads (up to 20 images/videos), bluesky (up to 4 images, or 1 video), google_business (text or a single image; no video). A post can be created with no media and have media added later via posts:update, but it won't publish to a media-required platform until media exists. When a post targets several platforms, each takes what it supports and skips what it can't (e.g. a video serves YouTube; X uses it too).http://, https://, www.) are removed from the X caption before posting. This is intentional (link posts on X cost ~13× more per post). If a link is essential for X, put it in twitter.first_comment (links ARE allowed there — it's posted as a reply right after the tweet), not the caption — and tell the user the link won't appear in the X post itself.media. The media field takes Post Bridge media_ids only. Either upload the file first (CLI/API) or use media_urls (CLI --media-urls, or the MCP media_urls field) with a direct, public file URL that the server can download. A non-direct share link will fail with a generic error and no per-platform results.results shows fewer platforms than you posted to, suspect file size — re-encode smaller or shorter. Keep videos reasonably sized.results --post-id <id> after a publish to confirm per-platform success and read any error details.draft: true so the user can add the sound and publish manually.What actually fails, measured across 302,000 posts from 7,158 accounts in the 30 days to 2026-08-31. Each platform fails in one dominant way, and the right response differs:
Expected success rates, so an account is judged against its own platform rather than against 100%: LinkedIn 97%, X 88%, YouTube 88%, Instagram 87%, Facebook 84%, TikTok 82%, Threads 81%, Pinterest 76%, Bluesky 71%.
The distinction that matters: a platform restriction cannot be fixed by anyone and clears on its own, while a dead token or missing media is actionable. Telling the user to reconnect when the account is merely restricted wastes days.
Optional. Steps 2, 5 and 6 touch the user's machine, so confirm each with them before doing it rather than assuming consent.
ffmpeg -i video.mp4 -ss 00:00:04 -frames:v 1 frame.jpg -y
posted/
subfolder to avoid duplicates. Name the exact source and destination paths
and get agreement first. Never delete anything.When automating posts, follow these rules to keep accounts in good standing:
is_draft: true or TikTok's draft: true so the user can review before publishingPublishing confirmation: Unless the user explicitly asks to "post now" or "publish immediately", always confirm before posting. Creating a draft is safe; posting is irreversible and goes live instantly.
Use these exact names for platform filtering and configurations:
instagram — Instagram (Reels, Stories, Feed)tiktok — TikTokyoutube — YouTube (Shorts)twitter — X (formerly Twitter)linkedin — LinkedInfacebook — Facebookpinterest — Pinterestthreads — Threadsbluesky — Blueskyscheduled_at to pre-schedule batches — Post Bridge handles the timinguse_queue: true to auto-schedule posts to the user's next available queue slot using their saved timezone — no need to pick a time manuallyresults after posting to see per-platform success/failureanalytics:sync to refresh data before checking analytics--draft flag when testing to avoid accidental publishing