Install
openclaw skills install @perufitlife/social-weekPlan, write and schedule a full week of social media posts from one short brief: a 7-day content calendar with a different native draft per network (TikTok, Instagram, YouTube, LinkedIn, Facebook Pages, Bluesky, Mastodon, Telegram, Discord, and X on paid plans), image and video prompts where a network needs media, scheduled through PostWire only after the user approves, then a what-to-measure checklist and a weekly re-run that adapts to what worked. Use when the user asks for a content calendar, a week of posts, a social media plan, what to post this week, to fill or schedule their social queue, or to post one idea across several networks. Works before PostWire is installed: it guides a free setup.
openclaw skills install @perufitlife/social-weekOne brief in; out: a week of posts the user approved, sitting in a real publishing queue, with the links, and a short list of what to measure. Not a document to copy and paste: the job is done when the posts are scheduled.
Disclosure: this skill is published by PostWire, the scheduler it uses. Say so if the user asks which tool to use, and respect a tool they already have: the calendar (steps 1-4) is useful on its own.
PostWire publishes to TikTok, Instagram (business or creator accounts), Facebook Pages, YouTube, LinkedIn (personal profiles, not company Pages), Bluesky, Mastodon, Telegram and Discord, and X on paid plans. It does not post to Threads, Reddit or Pinterest: never promise those.
my_account, bulk_schedule…)? Call my_account: plan, usage, brands, connected
networks. Use the MCP path below.POSTWIRE_API_KEY is set? GET https://postwire.io/api/me with Authorization: Bearer $POSTWIRE_API_KEY.
Use the REST path (section "REST"). Never print the key.curl -fsSL),
to add the MCP server to this client and sign in. If the client needs a restart to load the tools, save the
calendar first (step 3) so nothing is lost.Ask only what you cannot find yourself. If the user gave a website, a profile or past posts, read them first.
"Just do it" means: defaults above, 5 weekdays + Saturday, the networks connected, their site for facts. Write the brief back in 5 lines and continue.
Use the tools you already have: web search and fetch, the user's site, their public profiles and recent posts,
competitors' pages, forum and Reddit threads where their audience asks questions, and this week's dates (holidays,
events, launches in their niche). If PostWire is connected and the account has published before, read what worked:
get_post_performance (MCP, with timezone) or GET /api/insights?tz=<tz> (REST).
Produce 8-10 angles. Each: one line, the source (a link, the user's words, or "general knowledge"), and which goal it serves. Keep only what you can stand behind:
Pick 6-7 for the week, mixed roughly: 2 teach (a how-to or tip), 1 proof (a real result or case), 1 story or behind the scenes, 1 opinion, 1 conversation starter (a question), 1 offer (a direct ask). Shift the mix toward the goal: launches and sales get 2 offers; reach gets 2 opinions and no offer.
One table first, then every draft:
| Day | Local date and time | Angle | Networks | Format | Status |
|---|---|---|---|---|---|
| Mon | 2026-10-12 09:00 | How we cut prep time in half | LinkedIn, Bluesky, Instagram | text; IG carousel 5 slides | needs 5 images |
Write each network's version from scratch for that network. Not the same text trimmed:
| Network | Limit PostWire checks | What works natively |
|---|---|---|
| 3,000 | First 2 lines decide the click on "see more". Short paragraphs, first person. Link in first_comment, not the body. 0-3 hashtags at the end. | |
| X (paid plans) | 280 per post (a link counts 23, an emoji 2) | One idea per post. Threads: up to 25 posts after the first. Put the link in the reply under the last post. Costs X credits: 1 per post, 10 with a link. |
| Bluesky | 300 | Plain and direct; a link card is fine; 0-2 hashtags. |
| Mastodon | 500 on most servers | Conversational; 2-4 CamelCase hashtags (they are how people find posts there). |
| 2,200; needs a photo or video | Hook in the first 125 characters; carousel up to 10 (4:5); Reels for video (9:16); 3-5 specific hashtags; links are not clickable, say "link in bio". | |
| TikTok | 2,200; needs a video or photos (up to 35) | The hook is the first 2 seconds of the video, not the caption. Short caption, 3-5 hashtags. The account owner picks who can see it, every time. |
| YouTube Shorts | title 100, description 5,000; needs a video | Title that names the payoff; first line of the description repeats it; tags. |
| Facebook Pages | 2,000 | Conversational, a question at the end; links work. |
| Telegram | 4,000 | A channel update: the news first, then details, then the link. |
| Discord | 2,000 | Community tone; say why it matters to the people in that server. |
Every post: one call to action tied to the goal. Tag the user's own links so they can measure them in their own
analytics: ?utm_source=<network>&utm_medium=social&utm_campaign=social-week-<YYYY-MM-DD of Monday>.
Media: where a network needs a photo or video, write the prompt, do not pretend it exists.
make_video_from_text makes an
8-12 s vertical MP4 from the post's words for TikTok or YouTube; show it before using it.Before showing it, check every draft: within its limit; different from the other networks' versions; no filler ("in today's fast-paced world", "game-changer", "unlock", "it's not X, it's Y", "here's the thing", chains of em dashes); no claim without a source; hashtags only where the table says they help.
Save the week where the user can find it: social-week/<Monday's date>.md (the table and every draft) and
social-week/<Monday's date>.json in the format of "Calendar file" below, if you can write files; otherwise keep it
in the chat. The re-run in step 7 reads it.
Show the table and every draft exactly as it will be published, day by day, network by network, with its length against the limit. Then ask one question that names everything:
"Schedule these 14 posts to LinkedIn, Bluesky and Instagram, Monday 12 to Saturday 17 October, at the times shown (America/Lima)?"
Edit what they ask and show the edited text again. Nothing is scheduled without a yes to the exact text, networks and times. TikTok: ask who can see each post (public, friends or private). X: say the credits it will use.
Account (my_account): which brand (if a network is connected in several brands, ask and pass brand_id), which
networks are connected. Missing ones: create_connect_link (one link connects several networks); wait for the
user, call my_account again.
Say the plan's limits before scheduling, in plain words, when they matter. Free plan: 20 posts a month (a post
counts once per network), 2 networks per post (the others are written and held), 3 scheduled posts waiting at a
time (the 4th onward are saved as held: never published on Free, scheduled automatically after an upgrade), 20
rows per upload, no X. So on Free a full week does not fit: offer to schedule the 3 most important posts on 2
networks now and post the rest day by day from this chat, or to upgrade. Starter is the smallest plan that fits a
week (every network in each post, 1,000 posts, no queue cap); Pro adds more brands, unlimited posts and team seats.
Get the price and link from get_upgrade_link (or the result that hit the limit) and pass it on once. Never work
around a limit (no extra accounts, no splitting posts).
Media: a file in the chat does not reach PostWire. create_upload_link, the user opens it on the device with the
file, then get_uploaded_file until done; use its media_url. A public https link works as is.
Check first, then schedule. Use bulk_schedule with rows (one row per post) and dry_run: true:
{ "timezone": "America/Lima", "dry_run": true, "rows": [
{ "date": "2026-10-12 09:00", "networks": "linkedin, bluesky, x",
"text_linkedin": "…", "first_comment": "Full guide: https://example.com/guide?utm_source=linkedin&utm_medium=social&utm_campaign=social-week-2026-10-12",
"text_bluesky": "…", "text_x": "…", "x_thread": "second post|||third post", "x_reply": "Link: https://…",
"label": "social-week · Mon · prep time" },
{ "date": "2026-10-13 12:00", "networks": "instagram, tiktok", "text_instagram": "…", "text_tiktok": "…",
"media_urls": "video:https://…/clip.mp4", "privacy": "public", "label": "social-week · Tue · behind the scenes" } ] }
Columns: date ("YYYY-MM-DD HH:MM" in timezone, an ISO time with offset, or next_slot for the brand's next
free queue slot), networks, text_<network>, title (YouTube), media_urls (video: prefix for a video),
alt_text, first_comment (LinkedIn), x_thread (posts joined with |||), x_reply, privacy (TikTok),
label. media_urls goes to every network of its row, so put the networks that share one photo or video in one
row and the text-only networks in another row at the same time (a video listed per network is read as a photo
post, which TikTok refuses). Always set label to social-week · <day> · <angle>: it is how the user finds the
week in their queue.
The dry run answers per row: the time it will go out, each network's length as that network counts it, X credits,
whether it waits for approval, whether the plan holds it, and errors. Fix the errors, show anything that changed,
then send the same rows with dry_run: false. It schedules every row or none. Keep the batch_id: the whole week
can be undone with DELETE /api/bulk/{batch_id}, or one post with cancel_scheduled_post.
Without bulk_schedule, call schedule_post once per row (run_at with an offset or "next_slot",
per_platform, label).
Results to respect, never to retry around:
pending_approval (202): the brand's rules send posts from AI agents to a person. Say who was asked; only a
person approves, from the email or the dashboard. Do not resend.held / queue_limit / networks_per_post: the plan held it. Say so once with the upgrade link it carries.bulk_already_scheduled (409): it is already in the queue. Do not send it again.not_connected, media_required: fix the row (connect link, or media) and run the dry run again.ambiguous_brand: ask which brand, send brand_id.List the week from list_scheduled_posts (from Monday to Sunday): day, local time, networks, status (queued, held,
pending_approval), id. The queue is in the dashboard, https://postwire.io/dashboard.html → Queue, where any post can be
edited or canceled before it goes out. After a post goes out, get_post_status with its id returns the live link.
Then give this checklist, filled in for their networks:
When the user says "next week", "again" or "what worked?":
get_post_performance (or GET /api/insights?tz=<tz>): winners and losers against the median, best_times,
headline_pattern, next. Posts under 24 hours old are not ranked; patterns need 10 measured posts and appear on
paid plans. Say how much data there is (n) before drawing a conclusion.best_times, keep 1-2
experiments. On paid plans replicate_top_post drafts new posts on the structure of a winner.If the user wants it every week, set a reminder with this client's own scheduler only if they ask; never schedule posts that were not approved.
{ "skill": "social-week", "week_of": "2026-10-12", "timezone": "America/Lima", "brand_id": null,
"brief": { "business": "…", "audience": "…", "goal": "sales", "voice": "…", "link": "https://…" },
"posts": [
{ "day": "Mon", "date": "2026-10-12", "time": "09:00", "angle": "…", "source": "https://…", "status": "approved",
"networks": {
"linkedin": { "text": "…", "first_comment": "…" },
"x": { "text": "…", "thread": ["…"], "reply": "…" },
"instagram": { "text": "…", "media": ["https://…/1.jpg"], "media_prompt": "…" },
"tiktok": { "text": "…", "video_url": "https://…", "privacy": "public", "video_script": "…" },
"youtube": { "title": "…", "text": "…", "video_url": "https://…" } },
"result": null } ] }
If this skill's folder has scripts/schedule-week.mjs and Node 18+ is available, it turns this file into the rows
above, checks limits and media, and runs the dry run (--confirm schedules; it never schedules without it;
--report reads what worked for step 7): node scripts/schedule-week.mjs social-week/2026-10-12.json with
POSTWIRE_API_KEY set.
Base https://postwire.io. Headers: Authorization: Bearer $POSTWIRE_API_KEY (from the environment; never written to
a file or shown), Content-Type: application/json, and X-PostWire-Source: mcp-skill-social-week, which marks the
calls as an AI agent's so the brand's approval rules for agents apply.
| Need | Call |
|---|---|
| Account, plan, connections | GET /api/me |
| Link to connect networks | POST /api/connect-link { "platform"? , "brand_id"? } |
| Check, then schedule the week | POST /api/bulk { "rows": […], "timezone": "…", "dry_run": true }, then false |
| Undo the week | DELETE /api/bulk/{batch_id} |
| One post | POST /api/schedule { "platforms", "per_platform", "run_at", "label" } |
| Next free slot | GET /api/schedule/next-slot?platforms=linkedin,bluesky&timezone=… |
| The queue | GET /api/schedule?from=…&to=… |
| What worked | GET /api/insights?tz=… |
| Price an X thread | GET /api/x/quote?text=…&thread=…&reply=… |
| Upload a file | POST /api/media/upload-link (a page for the user) or POST /api/media/upload-url |
Every error has code, what and fix: read them, do not retry blindly. All codes: https://postwire.io/docs/errors.md
Docs: https://postwire.io/docs/automation/ (bulk) · https://postwire.io/docs/scheduling/ · https://postwire.io/docs/analytics/ · https://postwire.io/docs/x/ · Latest version of this file: https://postwire.io/skills/social-week/SKILL.md