CLI posting commands
Create, schedule, publish, update, and delete posts from the command line. All
commands honor the global flags and print JSON with --json.
Commands
| Command | Description |
|---|---|
posts:list | List posts. Flags: --status <draft|in_approval|scheduled|published|failed|rejected>, --limit, --offset. A post that waits for approval or was rejected prints an Approval: line that names posts:approval <id> |
posts:get <id> | Full details for one post |
posts:create | Create a draft or scheduled post. --text is required |
posts:create-and-publish | Create a post and publish it immediately (no --schedule) |
posts:update <id> | Update a draft or scheduled post; takes the same flags as posts:create |
posts:publish <id> | Publish an existing draft or scheduled post now |
posts:retry <id> | Retry only the failed platforms of a failed or warning post; succeeded channels are never re-posted (max 3 retries per platform) |
posts:approve <id> | Approve the current step of a post's approval workflow (approval_status: "pending"). You must be a listed approver for that step; steps approve in order |
posts:reject <id> | Reject a post's approval workflow, stopping it immediately (not just the current step). Flag: --comment "..." (optional, shown to the requester and other approvers) |
posts:approval <id> | Read the review of a post that went through an approval workflow: the step it waits on and who still has to decide, who rejected it and why, every step with each approver's decision, and the comment thread. Read-only; --json returns the full review object |
posts:delete <id> | Delete a post (irreversible) |
posts:recent-platform | Fetch recent posts live from the connected platforms, including content published outside OmniSocials. Flags: --limit (1-50, default 25; X defaults to 10 unless set explicitly), --platforms <a,b>. X results may be served from a snapshot up to 24h old (X bills per returned post); it refreshes right after you publish to X through OmniSocials. Requires the analytics:read scope |
Post options
For posts:create, posts:create-and-publish, and posts:update:
| Flag | Description |
|---|---|
--text <str> | Caption / post content (required on create) |
--channels <a,b> | Comma-separated channel ids |
--type <post|story|reel> | Content type (default post) |
--schedule <ISO8601> | Schedule time, e.g. 2026-08-01T09:00:00Z |
--draft | With --schedule: store a planned draft that keeps the time but is not scheduled (a person confirms it in the dashboard, or posts:update <id> --no-draft). On posts:update alone, --draft unschedules a scheduled post and keeps its time |
--no-draft | posts:update only: schedule a planned draft at the time it already carries |
--media-urls <url,url> | Attach media by URL |
--media-ids <id,id> | Attach media from the library |
--media-fit <ch=action,...> | What happens to a feed image outside what a channel shows uncropped, per channel: instagram=keep,pinterest=crop. Actions auto (default), crop, keep. Without it Instagram crops a 9:16 image to 4:5 around the subject and Pinterest keeps a Pin taller than 2:3; every decision is printed as a warning after the post is created. On posts:update alone, re-decides the stored images (keep gives the original back). See Image fit per channel |
--link-url <url> | Link-preview card (LinkedIn / Facebook) |
--link-title <str> | Override the link-preview title |
--link-description <str> | Override the link-preview description |
--link-thumbnail-url <url> | Override the link-preview thumbnail |
--location-id <id> | Instagram place tag (find ids with locations:search) |
--collaborators <a,b> | Instagram co-author usernames (max 3) |
--user-tags '<json>' | Instagram photo tags: JSON array of {"username","x","y","image_index?"} |
--x-thread "a || b || c" | X thread; parts split on || |
--bluesky-thread "a || b || c" | Bluesky thread |
--mastodon-thread "a || b || c" | Mastodon thread |
--threads-thread "a || b || c" | Threads thread (Meta Threads) |
--video-cover-json '<json>' | Thumbnail for a post whose media is one video: {"type":"frame","thumb_offset":3000} (milliseconds into the video) or {"type":"custom","cover_url":"https://..."}, plus optional "overrides" keyed by platform. Applied on Instagram, Facebook, LinkedIn, TikTok (frame only), Pinterest and YouTube Shorts (channel-dependent, see below). The literal value null removes it on update. See Creating reels |
--threads-location-id <id> | Threads place tag, rolling out (find ids with locations:search --platform threads; the literal value null clears the tag on update) |
Platform-specific flags
| Flag | Description |
|---|---|
--instagram-share-to-feed | Also share the reel to the feed |
--instagram-cover-url | Custom reel thumbnail URL |
--instagram-thumbnail-type | from-video or from-library |
--instagram-thumb-offset | Video timestamp in ms (for from-video) |
--instagram-audio-id | Licensed music for reels (find ids with audio:search) |
--instagram-audio-volume | Music volume 0-100 |
--instagram-video-volume | Original audio volume 0-100 (0 = music only) |
--instagram-first-comment | First comment (not for stories) |
--instagram-trial-reel | Publish as a trial reel (roughly 1,000+ followers required) |
--instagram-trial-graduation-strategy | MANUAL or SS_PERFORMANCE |
YouTube
| Flag | Description |
|---|---|
--youtube-title | Video title |
--youtube-privacy | public, private, or unlisted |
--youtube-tags | Comma-separated tags |
--youtube-category-id | Category ID |
--youtube-made-for-kids | Mark as made for kids |
--youtube-notify-subscribers | Notify subscribers on publish |
--youtube-contains-synthetic-media | Mark as AI-generated |
--youtube-first-comment | First comment (video must allow comments) |
A custom thumbnail (--video-cover-json) shows on a Short only on channels where YouTube has enabled custom Shorts thumbnails (Partner Program channels first). Other channels show a frame from the video.
TikTok
| Flag | Description |
|---|---|
--tiktok-privacy | PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY |
--tiktok-disable-comment | Disable comments |
--tiktok-disable-duet | Disable duets |
--tiktok-disable-stitch | Disable stitches |
--tiktok-video-cover-timestamp-ms | Cover frame timestamp in ms |
--tiktok-is-aigc | Mark as AI-generated content |
--tiktok-brand-content-toggle | Paid partnership disclosure |
--tiktok-brand-organic-toggle | Own-brand disclosure |
--tiktok-auto-add-music | Auto-add music to photo carousels |
--tiktok-title | Title of a photo carousel (max 90 characters; ignored on video) |
--tiktok-first-comment | First comment on the video (max 150 characters; needs comments enabled on the TikTok channel, and the video must be public) |
| Flag | Description |
|---|---|
--pinterest-board-id | Board ID (required for Pinterest; find it with accounts:get) |
--pinterest-title | Pin title |
--pinterest-link | Destination URL |
--pinterest-video-cover | Cover image URL for video pins |
--pinterest-alt-text | Accessibility alt text |
--pinterest-product-tags | Products to tag on the Pin: comma-separated Pin IDs or Pin links of your own product Pins, max 24 (find ids with pinterest:products) |
Product tags are added right after the Pin is published. Pinterest accepts a product only when its Pin is public, belongs to the same Pinterest account and links to a website that account claimed. A product that Pinterest refuses never fails the post. After publishing, posts:list prints the outcome as Pinterest products: 1 of 2 tagged (skipped: <pin_id> PIN_IS_PRIVATE), and posts:get returns it as pinterest.product_tags_result. On posts:update the Pinterest flags replace the stored Pinterest options as one object, so pass them all again; leaving --pinterest-product-tags out removes the tags.
Other platforms
| Flag | Description |
|---|---|
--facebook-first-comment | First comment on the page post |
--linkedin-first-comment | First comment on a personal profile post |
--linkedin-page-first-comment | First comment on a company page post |
--linkedin-carousel-as-images | true = publish 2+ images as a plain gallery instead of the default document carousel (profile post) |
--linkedin-page-carousel-as-images | Same for the company page post |
--linkedin-document-source | original_pdf (default) or slides. When the images are the unchanged pages of one uploaded PDF, the default sends that original file to LinkedIn; slides rebuilds the document from the slide images (profile post) |
--linkedin-page-document-source | Same for the company page post |
--x-reply-settings | following or mentionedUsers (empty = everyone) |
--google-business-cta-action | Google Business button: LEARN_MORE, BOOK, ORDER, SHOP, SIGN_UP, or CALL |
--google-business-cta-url | Button link, http(s):// (required for every action except CALL) |
--google-business-topic-type | STANDARD (default), EVENT, or OFFER |
--google-business-json | The full google_business object, for EVENT and OFFER posts (an offer needs event.title and event.schedule.startDate too, see Google Business) |
Telegram
| Flag | Description |
|---|---|
--telegram-silent | Send without a notification sound |
--telegram-link-preview false | Hide the link preview of a text-only post (shown by default) |
--telegram-pin | Pin the post in the chat (needs the bot's Edit messages right in a channel, Pin messages in a group) |
--telegram-protect | Stop forwarding and saving of the post |
--telegram-spoiler | Blur the photos and videos until someone taps them |
--telegram-caption-above | Show the text above the media |
--telegram-buttons-json '<json>' | Up to 3 link buttons: [{"text":"Shop now","url":"https://..."}]. Not on an album or a poll |
--telegram-first-comment | A silent reply under the post (max 4,096 characters) |
--telegram-json '<json>' | The full telegram object, merged over the flags |
See Telegram for the rules.
Discord
| Flag | Description |
|---|---|
--discord-silent | Send without a push or desktop notification |
--discord-no-link-preview | Hide the previews of the links in the text (shown by default) |
--discord-allow-mentions | Let @everyone, @here, role and user mentions in the text ping. Off by default: they show as text and ping nobody |
--discord-spoiler | Blur every image and video until someone clicks it |
--discord-thread-name "<title>" | The post title in a forum or media channel (1 to 100 characters). Required there; on a text channel the post goes out without it |
--discord-button "text|url" | A link button (label up to 80 characters, https:// or http:// link). Repeat the flag for up to 5. Not with a poll |
--discord-json '<json>' | The full discord object, merged over the flags |
See Discord for the rules.
Polls
Each flag takes one JSON object, and the post text is the question (LinkedIn polls carry their own question). A poll and media do not go together: X, Threads, Mastodon, Telegram and Discord refuse the request, and on LinkedIn the poll wins over the media. On a thread the poll goes on the first part. On posts:update, the literal value null removes the poll.
| Flag | Description |
|---|---|
--linkedin-poll-json '<json>' | LinkedIn polls, independent per channel: {"linkedin":{"question":"...","options":["...","..."],"duration":"SEVEN_DAYS"},"linkedin_page":{...}}. Question max 140 characters, 2 to 4 options of up to 30, duration ONE_DAY, THREE_DAYS, SEVEN_DAYS or FOURTEEN_DAYS |
--x-poll-json '<json>' | {"options":["A","B"],"duration_minutes":1440}: 2 to 4 options of up to 25 characters, 5 to 10080 minutes (default 1440) |
--threads-poll-json '<json>' | {"options":["A","B"]}: 2 to 4 options of 1 to 25 characters; Threads closes the poll after 24 hours. Text-only posts |
--mastodon-poll-json '<json>' | {"options":["A","B"],"expires_in":86400,"multiple":false,"hide_totals":false}, within the instance's limits (expires_in defaults to 86400) |
--telegram-poll-json '<json>' | {"options":["A","B"],"open_period":86400,"multiple":false}: 2 to 12 options of up to 100 characters, 5 to 2628000 seconds (leave it out to keep the poll open); the question is max 300 characters |
--discord-poll-json '<json>' | {"options":["A","B"],"duration_hours":24,"multiple":false}: 2 to 10 options of up to 55 characters, 1 to 768 hours (default 24); the question is max 300 characters. No buttons with a poll |
See Polls for the rules.
Comment to DM (Instagram and Facebook)
These flags attach a comment-to-DM automation to the post: whoever comments one of the keywords gets a DM, once per person. Instagram or Facebook must be in --channels, and the key needs the automations:write scope. On Facebook the DM lands in Messenger.
| Flag | Description |
|---|---|
--dm-keywords <a,b> | The keywords |
--dm-message "<text>" | The DM to send (max 1000 characters) |
--dm-reply "<text>" | Public reply under the comment; repeat the flag (or pass a comma list) for up to 10 variations |
--dm-match <word|contains|any> | word (default): the keyword on its own; contains: anywhere; any: every comment |
--dm-max-sends <n> | Stop after this many DMs |
--dm-stop-at <ISO8601> | Stop at this time |
--dm-platforms <instagram,facebook> | The channels that get the automation, one each with the same keywords and DM. Each must be in --channels. Leave it out for Instagram when the post goes there, else Facebook. On posts:update a channel left out loses its automation |
Examples
Schedule an image post to Instagram and LinkedIn:
Terminal
Publish an X thread right away:
Terminal
Publish a reel with licensed music:
Terminal
Tag products on a Pinterest Pin:
Terminal
Find out why a post was rejected, or who it waits for:
Terminal
Text
While a post is still in review, the same command prints a Waiting on: line with the current step and the approvers who still have to decide.
Ask your X and Threads followers a question:
Terminal
Check what went out recently, straight from the platforms:
Terminal