PostNitro

Create on-brand social media carousels and single-image posts and schedule them to LinkedIn, Instagram, TikTok, and Threads from a single command. Turn a topic, article, or X threa…

Muneeb Awan

@iammuneeb

What This Skill Does

Creates on-brand social media carousels and single-image posts from a topic, article, or X thread, then schedules them to LinkedIn, Instagram, TikTok, and Threads via a single CLI command. Fully scriptable with JSON input/output for AI agent workflows.

Replaces manually designing and scheduling each social media post by automating the entire create-to-publish pipeline for carousels and images across multiple platforms.

When to Use It

  • Turn a blog article or X thread into a multi-slide LinkedIn carousel
  • Generate and schedule a branded single-image post for Instagram
  • Import your own slide content and publish it as a TikTok carousel
  • Create a series of carousel posts from a topic and draft them to Threads
  • Repurpose existing content into a formatted social media post with brand assets
  • Automate daily social media posting by chaining content creation and scheduling in one command

Install

$ openclaw skills install @iammuneeb/postnitro

PostNitro — Create & Schedule Social Posts

PostNitro creates on-brand social media posts — multi-slide carousels and single images — and schedules them across LinkedIn, Instagram, TikTok, and Threads. This skill drives it from the command line, so an agent can take a topic, article, or your own content and produce a finished, scheduled post in one workflow. Every command is JSON in / JSON out — safe to script and chain.

Setup

  1. Install the CLI:
    npm install -g @postnitro/cli
    
  2. Sign up at https://postnitro.ai and subscribe to a plan — the Embed API requires a paid subscription (no free tier). Then generate an API key at account settings → Embed → Generate API Key.
  3. Authenticate (in order of precedence: --api-key flag → env var → saved config):
    postnitro auth set-key pn-xxxx        # persists to ~/.postnitro-cli/config.json
    export POSTNITRO_API_KEY="pn-xxxx"    # or use an env var
    
  4. Save defaults so you don't repeat template/brand/preset IDs on every call (the CLI's alternative to per-call flags — no env vars needed):
    postnitro defaults set --template-id <id> --brand-id <id> --preset-id <id> --response-type PDF
    
Env varRequiredPurpose
POSTNITRO_API_KEYYes*API key (*unless passed via --api-key or saved with auth set-key)

Key rule: carousel generate/import return an embedPostId (the async job). To schedule the result you need its designId (from the --wait output or carousel output). Never pass an embedPostId to schedule.

Security: auth set-key stores your API key in plaintext at ~/.postnitro-cli/config.json. Restrict its file permissions and avoid shared or untrusted machines; run auth clear to remove it. This skill runs no background processes, cron jobs, or startup scripts — the only local state is the API key and saved defaults, both disclosed above.

When NOT to use: as a general social-media strategy advisor when no content is being produced. (PostNitro composes with other tools — e.g., create a carousel here, then schedule it elsewhere — so pairing it with another scheduler is fine.)

Core Workflow

Every create command is asynchronous — --wait handles the polling and returns the finished output (including designId and editorUrl) in one call.

1. Create a post

Two independent choices decide which command you run:

  • Post type — a multi-slide carousel or a single image.
  • Content source — let AI write it (generate) or supply your own (import).

That's four commands:

generate (AI writes it)import (you supply content)
carousel (multi-slide)postnitro carousel generatepostnitro carousel import
image (single)postnitro image generatepostnitro image import

All four take --template-id/--brand-id (from flags or saved defaults) plus the modifiers in step 2, and every result includes the designId (needed to schedule) and an editorUrl.

AI-generated (generate)

postnitro carousel generate \
  --context "5 tips for growing your LinkedIn audience in 2026" \
  --type text \
  --instructions "Professional tone, actionable advice" \
  --wait

# Single image instead — same flags, just the image subcommand
postnitro image generate --context "Announce our new scheduling feature" --wait

--type values:

  • text--context is the topic/text to turn into a post
  • article--context is an article URL to extract and convert
  • x--context is an X (Twitter) post/thread URL

generate also needs an AI preset (--preset-id, or a saved default).

Import your own content (import)

# Carousel — slides is an ARRAY of typed slides (inline --slides overrides --file)
postnitro carousel import --slides '{"slides":[
  {"type":"starting_slide","heading":"Your Title","description":"Intro text"},
  {"type":"body_slide","heading":"Key Point","description":"Details here"},
  {"type":"ending_slide","heading":"Take Action!","cta_button":"Learn More"}
]}' --wait
postnitro carousel import --file ./slides.json --wait

# Single image — slide is a SINGLE OBJECT (not an array), via --slide
postnitro image import --slide '{"heading":"Welcome!","sub_heading":"Subtitle","cta_button":"Learn more"}' --wait
  • Carousel (--slides, array): exactly 1 starting_slide (first), ≥1 body_slide, exactly 1 ending_slide (last); heading required on every slide. Run postnitro carousel import-template for the schema. Ready-to-use: examples/import-default.json, examples/import-infographics.json.
  • Image (--slide, one object): fields heading (required), sub_heading, description, cta_button, image, background_image (plus infographic — see step 2). Sending an array here is rejected. Run postnitro image import-template for the schema.

2. Options for any create command

These layer onto any of the four commands above (and onto generate-and-schedule).

Output format — --response-type PDF | PNG | DESIGN

Default PDF (single file URL). PNG returns one URL per slide. DESIGN skips rendering — no file is produced; you get just the designId + editorUrl, the fastest option when you only need to schedule or edit.

AI image generation — --generate-images (any post type, any source)

Opt in to have AI generate images and bake them into the design before rendering. Works on all four create commands (and generate-and-schedule), for both carousels and images. Requires an --image-context brief:

postnitro carousel generate --context "How scheduling saves marketers time" \
  --generate-images --image-context "upbeat and professional, product-focused" \
  --image-placement auto --image-strategy all --wait
  • --image-context <text>required when generating images: a short visual brief for the image prompts
  • --image-placement auto|background|in-line (default auto — AI decides per slide)
  • --image-strategy strategic|all (strategic ≈ 50% of slides, default; all = every eligible slide)

Best-effort: the post still completes if images fail or aren't permitted. With --wait, the result carries an imageGeneration field (the GENERATE_IMAGES step); if its status is FAILED, its message explains why (e.g. free plan can't generate AI images, or the org is over its AI-image quota). AI images bill against the org's separate AI-image quota, not the post's slide credits, and add latency. Without --wait, the same step shows up in status logs.

Infographic layout — import only

On either import command, set layoutType: "infographic" on a slide (with a layoutConfig) to render data columns (max 3) instead of an image. Every column and content item needs a caller-provided id, and each item description is an HTML string. See carousel import-template / image import-template for the full schema.

3. Check status / get output (only if you didn't use --wait)

postnitro carousel status <embedPostId>   # progress + step logs; poll until COMPLETED
postnitro carousel output <embedPostId>   # final file URL(s) + designId + editorUrl

(image status / image output work identically for image posts.)

Output is a PDF (single URL) or PNG (one URL per slide) in data, plus the designId and editorUrl. Those can be handed to another tool — e.g. a different scheduler — to publish on platforms PostNitro doesn't cover. With --response-type DESIGN there's no rendered file, so data/mimeType/outputType are omitted (you still get designId and editorUrl).

4. Schedule the design to social accounts

postnitro social list   # get account IDs + platforms

postnitro schedule create \
  --status SCHEDULED \
  --scheduled-at "2026-12-31T13:00:00Z" \
  --design-id <designId> \
  --selected-accounts '["<socialAccountId>"]' \
  --linkedin-post-settings '{"postType":"document","postTitle":"5–90 char title"}' \
  --post-content '{"common":"Caption with #hashtags"}'

5. One-shot: create + schedule

Create and schedule in a single call — generate-and-schedule (AI writes it) or import-and-schedule (your own content). Both accept the schedule flags from step 4, --post-type CAROUSEL|IMAGE, and the AI-image options from step 2. If creation succeeds but scheduling fails, the error returns the designId so you can retry schedule create without re-creating (or re-spending credits).

# AI-generated
postnitro generate-and-schedule \
  --context "topic" --type text \
  --status SCHEDULED --scheduled-at "2026-12-31T13:00:00Z" \
  --selected-accounts '["<socialAccountId>"]' \
  --linkedin-post-settings '{"postType":"document","postTitle":"..."}' \
  --post-content '{"common":"..."}'

# Your own content — --slides (carousel array) or --slide (single image); --slides-file for a file.
# (Here --file is the SCHEDULE body, so slides come from --slides / --slides-file.)
postnitro import-and-schedule \
  --slides '{"slides":[ ... ]}' \
  --status SCHEDULED --scheduled-at "2026-12-31T13:00:00Z" \
  --selected-accounts '["<socialAccountId>"]' \
  --post-content '{"common":"..."}'

Common Patterns

Pattern 1: LinkedIn thought-leadership carousel

postnitro carousel generate \
  --context "5 mistakes startups make with their LinkedIn strategy and how to fix each one" \
  --type text --instructions "Professional but conversational. One clear takeaway per slide." --wait

Pattern 2: Repurpose a blog post

postnitro carousel generate --context "https://yourblog.com/posts/social-media-strategy-2026" \
  --type article --instructions "Extract the 5 most actionable points. Keep slide text concise." --wait

Pattern 3: Repurpose an X thread

postnitro carousel generate --context "https://x.com/username/status/1234567890" \
  --type x --instructions "Maintain the original voice and key points" --wait

Pattern 4: Data-driven infographic carousel

Import slides with layoutType: "infographic" body slides — see examples/import-infographics.json.

Pattern 5: Generate → schedule to LinkedIn

DID=$(postnitro carousel generate --context "5 remote work habits" --type text --wait | jq -r .designId)
LINKEDIN=$(postnitro social list | jq -r 'first(.accounts[] | select(.platform=="linkedin").id)')
postnitro schedule create --status SCHEDULED --scheduled-at "2026-12-31T13:00:00Z" \
  --design-id "$DID" --selected-accounts "[\"$LINKEDIN\"]" \
  --linkedin-post-settings '{"postType":"document","postTitle":"5 remote work habits"}' \
  --post-content '{"common":"New carousel 🚀 #remotework"}'

Pattern 6: Batch scheduling

DATES=("2026-02-14T09:00:00Z" "2026-02-15T09:00:00Z")
TOPICS=("Monday motivation 💪" "Tuesday tips 💡")
for i in "${!DATES[@]}"; do
  postnitro generate-and-schedule --context "${TOPICS[$i]}" --type text \
    --status SCHEDULED --scheduled-at "${DATES[$i]}" \
    --selected-accounts '["<socialAccountId>"]' \
    --linkedin-post-settings '{"postType":"document","postTitle":"Daily post"}'
done

Content Strategy Tips

  • LinkedIn: professional tone, actionable insights, 6–10 slides, clear CTA. PDF carousels post as postType: "document" (needs a 5–90 char postTitle).
  • Instagram: visual-first, concise text, 5–8 slides, storytelling arc. postAsStory for stories.
  • TikTok: trendy, punchy, 4–7 slides, hook on slide 1.
  • X / Threads: data-driven, 3–6 slides, provocative opening.

Common Gotchas

  1. Not authenticated — run postnitro auth status; set a key before anything else.
  2. Scheduling with embedPostId — use the designId from --wait/carousel output, never the embedPostId.
  3. Scheduled at is in the past--scheduled-at must be a future ISO-8601 datetime with a trailing Z.
  4. Invalid inline JSON — wrap JSON flags in single quotes; the CLI validates before any network call.
  5. Missing --brand-id ... multiple candidates — pass the ID or save a default with defaults set.
  6. LinkedIn document rejectedpostType:"document" needs a 5–90 char postTitle.
  7. Empty post — a scheduled post needs either --design-id or non-empty --post-content.
  8. Destructive commandssocial disconnect and schedule delete require --yes.
  9. SCHEDULED publishes live — use DRAFT when unsure; confirm time/account with the user first.
  10. responseType — the CLI defaults to PDF (pass --response-type PNG for individual slide images, or DESIGN to skip rendering and only create the design). Note: the underlying API now defaults to DESIGN when omitted, but the CLI always sends PDF explicitly, so CLI behavior is unchanged.
  11. Credits vary — AI generation ≈ 2 credits/slide; content import ≈ 1 credit/slide. Warn before large batches.

Credits & Pricing

The Embed API requires a paid subscription — there is no free tier, and free accounts cannot call the API. See https://postnitro.ai/plans for current plans and credit allowances.

Approximate usage cost (observed):

  • Content import: ~1 credit per slide
  • AI generation: ~2 credits per slide

Supporting Resources

In this skill:

Full project docs (GitHub):

Quick Reference

# Auth (do this first)
postnitro auth set-key pn-xxxx | postnitro auth status | export POSTNITRO_API_KEY=pn-xxxx

# Discover + defaults
postnitro template list | brand list | preset list | social list
postnitro defaults set --template-id <id> --brand-id <id> --preset-id <id> --response-type PDF

# Create (async — use --wait; result has designId + editorUrl)
# --response-type PDF|PNG|DESIGN (DESIGN skips rendering; CLI default is PDF)
# Optional AI images (any generate/import): --generate-images --image-context "brief" [--image-placement auto|background|in-line] [--image-strategy strategic|all]
postnitro carousel generate --context "topic|url" --type text|article|x [--instructions "..."] --wait
postnitro carousel import (--slides '{"slides":[...]}' | --file ./slides.json) --wait
postnitro carousel status <embedPostId>        # if not using --wait
postnitro carousel output <embedPostId>        # file URLs + designId + editorUrl

# Single-image posts (mirror carousel; import takes ONE slide object, not an array)
postnitro image generate --context "topic|url" --type text|article|x --wait
postnitro image import (--slide '{"heading":"..."}' | --file ./slide.json) --wait

# Schedule (date REQUIRED + future; use designId, not embedPostId)
postnitro schedule create --status SCHEDULED|DRAFT --scheduled-at "<iso>" --design-id <id> \
  --selected-accounts '["<id>"]' --linkedin-post-settings '{"postType":"document","postTitle":"..."}' \
  --post-content '{"common":"caption"}'
postnitro schedule list --from "<date>" --to "<date>" | get <id> | delete <id> --yes

# One-shot: create + schedule (both take --post-type + AI-image flags)
postnitro generate-and-schedule --context "topic" --status SCHEDULED --scheduled-at "<iso>" ...
postnitro import-and-schedule --slides '{"slides":[...]}' --status SCHEDULED --scheduled-at "<iso>" ...   # or --slide for image

Tips for the Agent

  • Always confirm authentication (postnitro auth status) before running any other command. Never fabricate an API key — ask the user.
  • Prefer --wait so one call returns the finished output and designId.
  • Discover IDs with the list commands; save workspace defaults with defaults set so later calls stay short.
  • Schedule with the designId, not the embedPostId.
  • For --type article/x, --context must be a URL. For plain text, use --type text.
  • responseType defaults to PDF in the CLI — pass --response-type PNG for individual slide images, or DESIGN to skip rendering when you only need the designId/editorUrl (e.g. for scheduling).
  • --scheduled-at must be a future ISO-8601 datetime (trailing Z).
  • status: SCHEDULED creates a live post that will publish — confirm the time and account with the user, or use DRAFT when unsure.
  • Parse stdout as JSON; on failure read .error.message from stderr and fix the inputs.
  • Warn about credit costs before large batches (AI generation is ~2× the cost of import).
  • If the user doesn't specify a platform, suggest LinkedIn (most common carousel use case) and remember PDF carousels post there as document.

Top skills in this category