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:
- Local CLI accounts — credentials live on the machine and posting calls the SDK directly. OAuth platforms use your own developer app credentials through
SIMPLE_POST_<PLATFORM>_CLIENT_IDand, where required,_CLIENT_SECRET. Telegram uses a bot token and chat ID instead. - 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
Install the public npm package:
npm install -g @simple-post/cli
simplepost --help
To run a development build 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
| Backend | Best for | Requires |
|---|---|---|
keychain | Developer machines | OS keychain (macOS Keychain, Windows Credential Manager, libsecret) |
file-encrypted | Scripts, CI, headless servers | A password supplied through SIMPLE_POST_CONFIG_PASSWORD, or an interactive prompt |
file-plain | Local testing only | Nothing — 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.
Scheduler-connected accounts
Hosted CLI access requires Advanced, Pro, or an active trial. Install the CLI and configure its secret store first using the steps above. This requirement applies to hosted account sharing, not to direct local publishing with your own credentials.
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://app.simplepost.social
simplepost account
simplepost post --interactive
The browser flow authorizes the CLI on the Scheduler and returns a short-lived, single-use code to the loopback listener. The CLI exchanges that code directly for a 90-day SimplePost CLI token, so the bearer token never appears in browser history. The CLI stores it 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:
SIMPLE_POST_CLI_TOKEN="sp_cli_..." simplepost connect --url https://schedule.example.com
Self-hosted Scheduler:
simplepost connect --url https://schedule.example.com
Replace schedule.example.com with your deployment hostname.
Scheduler-connected posting accepts local images/videos and public URLs. The CLI uploads local files to the Scheduler before creating the post; see upload limits.
simplepost post --app-account-id ACCOUNT_ID \
--text "Launch day" --image ./image.png
CLI posting submits immediately. It does not save SimplePost drafts or schedule posts in the SimplePost calendar. Use the web app, MCP, or Scheduler API for those workflows. Provider-specific publishAt options upload now and ask the provider to publish later; TikTok publishMode: "draft" uploads to TikTok's inbox. Neither creates a SimplePost draft.
For non-interactive Scheduler posting, get target IDs from simplepost account, then repeat --app-account-id <id> on simplepost post.
Local CLI accounts
For a complete first local post, follow the local Telegram quickstart.
OAuth providers
Register your own developer app and export its client ID (and client secret when required) before connecting:
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 port 5000. The browser returns an authorization code, the CLI exchanges it directly with the platform, stores the resulting credentials through your chosen backend, and the account appears under simplepost account.
If port 5000 is occupied, use --callback-port 6123 or set SIMPLE_POST_CALLBACK_PORT=6123. Register the resulting loopback URI exactly in the platform developer app. SIMPLE_POST_<PLATFORM>_REDIRECT_URI remains available when the complete redirect must be overridden and takes precedence over the shared port.
For Telegram, no OAuth is involved:
simplepost account add telegram \
--alias announcements \
--bot-token "$TELEGRAM_BOT_TOKEN" \
--chat-id "@channel"
X can use a public PKCE client without a secret. Facebook and Instagram use public Meta apps. YouTube, TikTok, Threads, LinkedIn, and Pinterest require a client secret. Bluesky uses the URL of your hosted atproto client metadata as its client ID.
The provider guides under Platform setup describe what to enable in each developer dashboard before running account add.
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:
| Group | Flags |
|---|---|
| Workflow | --interactive |
| Targets | --account (repeatable, <platform>:<alias>), --app-account-id (repeatable) |
| 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-made-for-kids, --youtube-publish-at, --youtube-privacy-status |
--facebook-publish-at | |
| TikTok | --tiktok-publish-mode, --tiktok-visibility, --tiktok-allow-comment, --tiktok-allow-duet, --tiktok-allow-stitch |
--linkedin-visibility | |
--pinterest-board-id, --pinterest-title, --pinterest-description, --pinterest-link, --pinterest-alt-text |
Run simplepost post --help for the generated full list, including option descriptions. The legacy --youtube-playlist-id flag is rejected by the current hosted app; omit it. See YouTube release differences.
Boolean options use positive/negative forms rather than string values: --strict-mode / --no-strict-mode, --youtube-made-for-kids / --no-youtube-made-for-kids, and the TikTok --tiktok-allow-* / --no-tiktok-allow-* variants.
Other commands
| Command | What it does |
|---|---|
simplepost setup | Configure or change the secret storage backend. |
simplepost account | List local and Scheduler-connected accounts. |
simplepost account add <platform> | Add a local account. Supports --alias, --bot-token, --chat-id, --no-browser, --callback-port, --callback-url, --redirect-uri. |
simplepost account remove <alias> | Remove a local account by alias. |
simplepost connect | Authorize the CLI against a Scheduler app. Supports --url, --token, --no-browser, --callback-port. |
simplepost disconnect | Revoke the Scheduler CLI token remotely, then remove the local copy. |
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.