Inklate skill · Publish

posts-update

Change or call off a post that already exists in Inklate: find it, show what it says now, edit the content, channels, media, labels, campaign, or the schedule…

frontmatter · SKILL.md+
name: posts-update
description: >
  Change or call off a post that already exists in Inklate: find it, show what it says
  now, edit the content, channels, media, labels, campaign, or the schedule in one call,
  and let the server re-validate it on the spot — or take it off the calendar and delete
  it. Covers LinkedIn, X (Twitter), Instagram, and Facebook today, with Mastodon, Threads,
  Bluesky, TikTok, and YouTube in platform review. Use when the user says "edit that
  scheduled post", "change the time", "unschedule that", "cancel tomorrow's post", or
  "delete that draft". Requires the Inklate CLI (npm i -g inklate) or the Inklate MCP
  connection. Reads social-context.md when present so edits stay on-voice. Published
  posts are immutable and this skill says so rather than pretending; every edit's
  response carries fresh per-channel validation, and every cancellation is confirmed
  first.
license: MIT
metadata:
  version: 0.2.0
  topics:
    - editing
    - publishing
  examplePrompt: "Change the time on tomorrow's LinkedIn post and fix the typo in the first line"
  pairsWith:
    - social-post
  mcpTools:
    - posts_list
    - posts_get
    - posts_update
    - posts_delete
    - search
  cliCommands:
    - posts list
    - posts get
    - posts update
    - posts delete
    - search

Edit, reschedule, or cancel a post that already exists — the half of the loop posts-create and posts-publish leave to the dashboard.

Transports

  • CLI (preferred): inklate (npm i -g inklate) — structured YAML on stdout (--json for JSON), progress on stderr, and typed exit codes: 3 no credentials, 4 a deliberate refusal — arming a not-ready post with --scheduled-at — with its machine-readable payload on stdout, 5 not found, 6 the resource changed underneath you, 7 rate limited but safe to re-run later. Pass --no-input when running unattended so a missing flag fails loudly instead of prompting, and --yes to carry an approval the user has already given in chat.
  • MCP (fallback): the Inklate MCP tools when the CLI is not installed.

Steps below name the MCP operation with the CLI form in parentheses; the fields are the same on both.

Connection check

  1. Run inklate auth check (MCP: organizations_list) first.
  2. On exit 3 (no credentials): interactively run inklate auth login; headless, the key comes from INKLATE_API_KEY=inklate_… in the environment or inklate auth set --token inklate_…. If neither transport is available at all, tell the user to install with npm i -g inklate (or connect at https://inklate.com/skills/connect?ref=skills-posts-update) and STOP.
  3. If multiple organizations exist, ask which one owns the post (CLI: --org <slug>).

What can change, and what cannot

Read this before touching anything — it decides whether the request is even possible:

Post state Editable? How
draft yes posts_update — content/channels/media/labels/campaign, or add --scheduled-at to arm it
scheduled yes posts_update--scheduled-at reschedules, --clear-schedule returns it to draft
failed yes posts_update, then hand off to posts-publish (inklate posts publish <id>)
publishing (in flight) no — wait poll posts_get; it is mid-flight, not stuck — there is no separate status call
published never immutable; say so and offer the real alternatives

Published is immutable. No flag changes that. When the user asks to edit something already live, say it plainly and offer what actually exists: delete it on the platform and publish a corrected post, or leave it and post a follow-up. Never imply an edit went through.

Craft boundary

This skill owns find → show → change → re-validate → confirm. The WRITING belongs to the paired craft skill: when social-post is installed, hand it the new copy and use what comes back. Without it, make the edit the user asked for and nothing more — an edit request is not an invitation to restyle the post.

Context

Read social-context.md (project root, then .agents/social-context.md) if present. ## Voice and ## Never constrain any rewriting you do while editing; ## Platforms resolves a vague reference ("the LinkedIn one") to a channel. Proceed without it if absent.

Workflow

  1. Find the post, don't guess it. A vague reference ("tomorrow's post", "the roadmap one") resolves through posts_list (CLI: inklate posts list --status scheduled --scheduled-from … --scheduled-to …) or search (CLI: inklate search "roadmap") — never through memory of an earlier session. If more than one candidate matches, list them with IDs and times and ask which one. Acting on the wrong post is unrecoverable once it publishes.
  2. Read it before you change it. Call posts_get (CLI: inklate posts get <id>) and show the user what the post says right now — per-channel content (content.markdown — the copy exactly as you would write it back — and content.text, the same copy as the platform will show it), media, scheduled time, and status. This is also where you capture the post's updated_at for step 4's concurrency guard, and where you check the state table above.
  3. State the change in one line and get a yes. "Replacing the first line and moving Tuesday 9:00 → Wednesday 14:00" — then wait. Editing a scheduled post is a real-world action: it changes what goes out and when.
  4. Apply the edit in one call. inklate posts update <id> --content … --channel … --media … --scheduled-at <time> --expected-updated-at <from step 2> (MCP: posts_update) handles content, targets, media, labels, campaign, and the schedule together — there is nothing left to sync or re-materialize separately.
    • --expected-updated-at is not optional. It is the optimistic-concurrency guard: pass the updated_at you read in step 2 and the edit refuses (exit 6) if anyone touched the post in between. Skip it and you silently overwrite a teammate's dashboard edit.
    • On exit 6, do not retry with a fresh timestamp on autopilot. Re-read the post, show the user what changed underneath them, and let them decide.
    • --scheduled-at and --clear-schedule are mutually exclusive — pick one.
    • A channel's copy is only ever overwritten on purpose. --per-channel '{"x": {"content": "…"}}' edits one channel without touching the others; --per-channel '{"x": null}' is the explicit reset back to the shared content — the only way a channel's own copy (hand-edited or auto-fixed earlier) gets discarded. A plain --content edit never silently overwrites a channel that already has its own copy.
    • Adding a channel with --channel (repeatable) gives the new channel a freshly adapted copy of the shared content; existing channels' copies are untouched.
  5. Mind the approval pipeline. If the organization gates publishing on approval, any content change after approval sends the post back through it. Say this at the moment of editing, not after: a user who thinks they fixed a typo on an approved post needs to know it now needs re-approval.
  6. Re-validation is automatic. The posts_update response is the same Post View as posts_create/posts_get: top-level ready, and per channel errors[], warnings[], and fixes_applied[] from server-side autofix. There is no separate re-check call — read the response you already got back. An edit that was fine on LinkedIn can overflow X; if it did, the response says so. Auto-fixable violations are applied and reported the same way as in posts-create; judgment calls go back to the user.
  7. Rescheduling is --scheduled-at on posts update (CLI: inklate posts update <id> --scheduled-at <time> --yes — ISO-8601 with offset, or 30m/2h/3d/1w). Restate the new time unambiguously in the organization's timezone before booking it. Arming refuses (exit 4) if the post isn't ready for its current channels — nothing about the post changes when that happens; fix the errors and retry.
  8. Taking it off the calendar is --clear-schedule on posts update (CLI: inklate posts update <id> --clear-schedule --yes) — the post returns to draft, content intact, nothing published. This is the reversible cancellation and the right default when the user says "cancel that".
  9. Deleting is posts_delete (CLI: inklate posts delete <id> --yes, add --takedown to also best-effort remove the provider-side copies) and it is not reversible. Confirm the specific post by ID and summary, name what is being destroyed, and get an explicit yes. When the user's intent might be "just don't post it", offer --clear-schedule first — most "cancel" requests mean unschedule, not delete.
  10. Verify from the platform, not from your own request. Re-read with posts_get (or posts_list for the window) and report the state that came back. There is no separate status call — posts_get also carries live per-channel publish results (url, external_post_id, or a sanitized error) once something has shipped.

Quality bar

  • Every post acted on was resolved this session by ID from posts_list, search, or posts_get — never from a remembered ID or an assumed "the one we just made".
  • --expected-updated-at accompanies every posts_update that changes an existing post. An edit without it is a bug, even when it succeeds.
  • Published posts are reported as immutable, with the two real alternatives — never worked around, never described as edited.
  • Unschedule (--clear-schedule) is offered before delete whenever the user's words are ambiguous; delete requires the post named back to them and an explicit yes.
  • Every content or target change is followed by reading the fresh ready / errors[] the posts_update response already carries — no second call needed to see it.
  • A channel's own copy (hand-edited or auto-fixed) is only ever discarded by the explicit --per-channel '{"<channel>": null}' reset, never as a side effect of a shared-content edit.
  • When approval gating is in play, re-entry into the approval pipeline is stated at edit time.
  • Exit 6 is a fact about the world, not a retry signal: re-read, show, ask.
  • This skill changes and cancels; it never publishes. Shipping is posts-publish's job.

Receipt

End with what actually changed, one row per action:

Post ID Action Before After Status
post_abc123 edited "Our roadmap is dead…" "We killed our roadmap…" scheduled, ready
post_abc123 rescheduled Tue Jul 21, 9:00 AM Wed Jul 22, 2:00 PM scheduled
post_def456 unscheduled scheduled Wed Jul 22 draft draft, content intact

Below the table: the post IDs touched, any fixes applied on the way, anything refused with the reason (published, exit 6 conflict, approval re-entry), and the single next action — publish via posts-publish, or nothing if the user wanted it parked.

Related skills