Skip to main content

CLI

Use the CLI when you want to post from a terminal, script, CI job, local automation, or an AI coding agent that can run shell commands.

The CLI has two ways to reach social accounts:

  1. Local CLI accounts — credentials live on the machine. Posting calls the SDK directly. For X, YouTube, Facebook, Instagram, and Bluesky the CLI ships with a bundled OAuth app, so simplepost account add <platform> is the whole setup. TikTok, Threads, LinkedIn, and Pinterest require your own OAuth app (you provide the client secret through an environment variable). Telegram uses a bot token and chat ID — no OAuth at all.
  2. Scheduler-connected accounts — the CLI authorizes against a running Scheduler app and uses accounts the user has already connected there from the interactive posting flow. The Scheduler app keeps the raw social tokens; the CLI never sees them.

You can use both at the same time: locally stored accounts and Scheduler-connected accounts appear together in the interactive picker. Non-interactive --account <platform>:<alias> posting targets local CLI accounts.

Install

The published binary is simplepost. From a checkout of the core repo:

yarn workspace @simple-post/cli build
node cli/bin/run.js --help

The examples below use simplepost. Inside the repo, replace it with node cli/bin/run.js.

Configure secret storage

The CLI needs to know how to store secrets before any account command runs.

simplepost setup --backend keychain
BackendBest forRequires
keychainDeveloper machinesOS keychain (macOS Keychain, Windows Credential Manager, libsecret)
file-encryptedScripts, CI, headless serversA password supplied through SIMPLE_POST_CONFIG_PASSWORD, or an interactive prompt
file-plainLocal testing onlyNothing — secrets are stored unencrypted

For non-interactive setup (CI, Docker), set SIMPLE_POST_CONFIG_PASSWORD first, then run simplepost setup --backend file-encrypted. Without the env var the CLI will prompt and the script will hang.

Local CLI accounts

Bundled OAuth providers

X, YouTube, Facebook, Instagram, and Bluesky ship with a SimplePost OAuth client, so you do not need a developer app:

simplepost account add x --alias main
simplepost account add youtube --alias channel
simplepost account add bluesky --alias personal

Each command opens a loopback browser flow on http://127.0.0.1:5000/oauth/callback (Instagram uses https://localhost:5000/oauth/callback because Meta requires HTTPS for that product). The browser returns an access token, the CLI stores it through your chosen backend, and it appears under simplepost account after that.

For Telegram, no OAuth is involved:

simplepost account add telegram \
--alias announcements \
--bot-token "$TELEGRAM_BOT_TOKEN" \
--chat-id "@channel"

Bring-your-own-OAuth-app providers

TikTok, Threads, LinkedIn, and Pinterest do not have a bundled OAuth client. Create your own platform app first, then pass the client secret through the appropriate env var before running account add:

PlatformRequired env var
TikTokSIMPLE_POST_TIKTOK_CLIENT_SECRET (and the matching client key inside the CLI prompt)
ThreadsSIMPLE_POST_THREADS_CLIENT_SECRET
LinkedInSIMPLE_POST_LINKEDIN_CLIENT_SECRET
PinterestSIMPLE_POST_PINTEREST_CLIENT_SECRET

Optional SIMPLE_POST_<PLATFORM>_REDIRECT_URI env vars override the loopback URI when your platform app expects a different redirect.

The provider guides under Platform setup describe what to enable in each developer dashboard before running account add.

Scheduler-connected accounts

When a user has already connected accounts in the Scheduler app, the CLI can use those without ever holding the raw social tokens:

simplepost connect --url https://schedule.simplepost.dev
simplepost account
simplepost post --interactive

The browser flow authorizes the CLI on the Scheduler, which returns a SimplePost CLI token. The CLI stores that token in the configured secret store. After connect, Scheduler-connected accounts appear alongside local accounts in the interactive posting flow.

For CI or another non-interactive environment that only needs to establish the Scheduler connection, pass a token directly:

simplepost connect --url https://schedule.example.com --token "$SIMPLE_POST_CLI_TOKEN"

Self-hosted Scheduler:

simplepost connect --url https://schedule.example.com

Replace schedule.example.com with your deployment hostname.

When posting through Scheduler accounts, media must be reachable through a public URL — Scheduler does not pull bytes from local CLI files. For fully offline-friendly local-file posting, use local CLI accounts instead.

Non-interactive posting through Scheduler account IDs is not exposed by the CLI yet. For CI publishing, use local CLI accounts or call the Scheduler API directly with accountIds.

Posting

Interactive (the default when no flags are passed):

simplepost post

Non-interactive across multiple stored accounts:

simplepost post \
--account x:main \
--account telegram:announcements \
--text "Hello from SimplePost CLI" \
--image ./image.png \
--telegram-chat-id "@channel"

Repeat --account to target multiple stored accounts. The format is <platform>:<alias>.

Pass a full SDK Post payload as JSON:

simplepost post --post-json ./post.json --account x:main
{
"content": {
"text": "Launch day",
"media": [{ "type": "image", "url": "https://cdn.example.com/image.jpg" }]
},
"platforms": ["x"]
}

Merge platform options with --options-json:

simplepost post \
--account x:main \
--text "Replying from the CLI" \
--options-json '{"x":{"replyToId":"1234567890"}}'

Useful flags

The simplepost post command groups flags by purpose. The most common ones:

GroupFlags
Workflow--interactive
Targets--account (repeatable, <platform>:<alias>)
Content--text
Media--image, --video, --media-json
JSON input--post-json, --options-json
Advanced--strict-mode, --log-level
X--x-reply-to-id
Telegram--telegram-chat-id, --telegram-parse-mode
YouTube--youtube-tags, --youtube-category-id, --youtube-playlist-id, --youtube-made-for-kids, --youtube-publish-at, --youtube-privacy-status
Facebook--facebook-publish-at
TikTok--tiktok-publish-mode, --tiktok-visibility, --tiktok-allow-comment, --tiktok-allow-duet, --tiktok-allow-stitch
LinkedIn--linkedin-visibility
Pinterest--pinterest-board-id, --pinterest-title, --pinterest-description, --pinterest-link, --pinterest-alt-text

Run simplepost post --help for the generated full list, including option descriptions.

Other commands

CommandWhat it does
simplepost setupConfigure or change the secret storage backend.
simplepost accountList local and Scheduler-connected accounts.
simplepost account add <platform>Add a local account. Supports --alias, --bot-token, --chat-id, --no-browser, --callback-url, --redirect-uri.
simplepost account remove <alias>Remove a local account by alias.
simplepost connectAuthorize the CLI against a Scheduler app. Supports --url, --token, --no-browser.
simplepost disconnectRemove the stored Scheduler CLI token.

Token rotation

X rotates refresh tokens on every refresh: a successful refresh returns a new refresh token and invalidates the old one. The CLI persists rotated tokens back into the local secret store automatically when posting through a stored X account. If you instead use SDK env vars (X_CLIENT_ID, X_REFRESH_TOKEN, ...), nothing rewrites your .env; you have to capture the refreshed values yourself. See token rotation.