---
name: social-publish-article
sheetId: "1.29"
description: "Publishes a finalized post to your SOCIAL channels — Facebook, Threads & Instagram — via the Buffer API, with per-channel formatting, Instagram image support, and a preview-confirm gate before anything posts. Use when you say 'publish my post', 'post this to Threads/IG/FB', 'push this draft live to social', or 'schedule this post'. The SOCIAL publisher — not the website/blog one (seo-publish-article)."
category: brand-marketing
project: social-publish-article
platform: claude
status: Done
author: Peter Tu
input: "A finalized post/caption (plus a public image URL for Instagram), which channels to target, and whether to draft, post now, or schedule"
process: "Formats the copy per channel (Threads 500-char aware; Instagram needs a public image URL + caption), previews the exact copy for confirmation, then publishes through the Buffer API using your own token and connected channels"
output: "The post created in Buffer — as a draft, an immediate publish, or a scheduled post — with the Buffer post id and status returned per channel"
synergy: []
---

# Social Publish Article

```bash
npx skills add https://github.com/peter-tu-zynkr/zynkr-skill-builder --skill social-publish-article
```

Publish a finished post to your social channels — Facebook, Threads, and Instagram — through Buffer, so you don't have to open three apps or Buffer's dashboard. You bring your own Buffer access token and your already-connected channels; the skill formats the copy per platform, shows you exactly what will go out, and only publishes after you confirm. It can stage a draft in Buffer, post immediately, or schedule for later.

---

## Configuration — connect Buffer (one-time)

This skill talks to **your own** Buffer account — nothing is shared or hardcoded.

1. In Buffer, connect the channels you want (Facebook Page, Threads, Instagram **Business/Creator** account) and create a Buffer **API access token** (Buffer → Developers; choose the longest expiry offered).
2. Copy `.secrets.env.example` to `.secrets.env` (it's gitignored — keep it that way) and paste your token as `BUFFER_ACCESS_TOKEN`. Never put the token in chat or in `SKILL.md`.
3. Discover your org + channel IDs with the helper, then paste them into `.secrets.env`:
   ```bash
   python3 scripts/buffer_post.py --list-channels
   ```
   Fill in `BUFFER_ORG_ID`, `BUFFER_CHANNEL_FACEBOOK`, `BUFFER_CHANNEL_THREADS`, `BUFFER_CHANNEL_INSTAGRAM`.

The token is read only by the helper at runtime — it never appears in output or on the command line.

## The one rule — confirm before anything goes public

Never publish (`now` / `schedule`) without showing the user the exact final copy per channel and getting an explicit "yes." Posting is public and hard to undo. The default mode is `draft` (stages the post in Buffer; nothing public) until the user says go live.

---

## Step 1 — Gather the final post

Collect the finished copy from the user (pasted text, a file, or a doc). Confirm per channel:
- **Facebook / Threads:** the post text.
- **Instagram:** a caption **and** an image (see Step 3 — IG can't post text-only).

If the same copy is reused across channels, confirm that's intended; otherwise take per-channel text.

## Step 2 — Pick channels and mode

- **Channels:** `facebook`, `threads`, `instagram` — whichever apply. Text-only content skips Instagram unless an image is supplied.
- **Mode:** `draft` (default — stage in Buffer for a final look), `now` (publish immediately), or `schedule` (needs an ISO timestamp).

## Step 3 — Format per channel

- **Facebook (text):** full body + optional link; no hard length limit in practice.
- **Threads (text, 500-char cap):** Threads posts cap at 500 characters. If the copy fits, post once. If it's longer, split into ≤500-char chunks on sentence/paragraph boundaries and post each as a separate Threads post — this helper posts **one** Buffer post per call and does not chain a native reply-thread, so note the split in the preview.
- **Instagram (image + caption):** Instagram requires an image — text-only is rejected. Buffer takes a **public image URL**, not a file upload, so host the image at a reachable URL first (any public bucket/CDN) and pass that URL. The post text becomes the caption.

## Step 4 — Preview & confirm  ⟵ the gate

Show the user, clearly separated per channel: the exact text (with Threads char counts / chunk splits), and for Instagram the image URL + caption. State the **mode** explicitly ("mode: draft" vs "mode: NOW — goes public immediately"). Then ask: **"Post these now? (yes / edit / cancel)"** Only on an explicit yes proceed. "edit" → revise and re-preview. Default to NOT publishing live.

## Step 5 — Publish via Buffer

Run the helper once per channel. It reads your token from `.secrets.env`; never put the token on the command line.

```bash
python3 scripts/buffer_post.py --channel facebook  --text "<copy>"                          --mode draft
python3 scripts/buffer_post.py --channel threads   --text "<chunk>"                          --mode draft
python3 scripts/buffer_post.py --channel instagram --image "<public-url>" --text "<caption>" --mode draft
```

Swap `--mode draft` → `--mode now` to publish immediately (only after the Step 4 "yes"), or `--mode schedule --due 2026-06-20T09:00:00Z` to queue. For a split Threads thread, call the threads line once per chunk, in order.

Read each result: `OK post id=… status=…` (success) or `FAIL <ErrorType>: <message>`. If any call fails, **stop** and report which channels posted and which didn't — a half-published set is worse left silent. On a daily-limit error, surface Buffer's posting limit rather than blind-retrying.

## Outputs

- The post(s) created in Buffer — draft, immediate, or scheduled — with the Buffer post id + status per channel. For `now`/`schedule`, Buffer performs the actual publish (you do not need your own scheduler).

## Configuration files

- `.secrets.env` — your Buffer token + channel IDs (gitignored; copy from `.secrets.env.example`). Never commit or reveal it.
- `scripts/buffer_post.py` — the Buffer GraphQL helper. Modes: `--list-channels`, and `--channel/--text/--image/--mode/--due`.

## Limitations

- One Buffer post per call — a Threads thread posts as separate posts, not a native reply-chain.
- Instagram needs a **public** image URL (no direct byte upload) and a Business/Creator account connected in Buffer.
- Buffer access tokens **expire** — pick the longest expiry and refresh before it lapses, or publishing silently 401s.
- Buffer's API returns no analytics — this skill publishes only.
