CLI
Commands
Every command, every flag. Run echoia <command> --help for the same reference in your terminal.
--json works everywhere
--json and prints the raw API response instead of a table. That is the form to pipe into jq — see Scripting & CI.login
Opens your browser to approve this terminal, then stores the key in ~/.config/echoia/config.json with 0600 permissions.
echoia login| Flag | Description |
|---|---|
--stdin | Read the key from stdin: echo "$KEY" | echoia login --stdin. Never reaches argv. |
--no-browser | Prompt for a key instead of opening a browser. The prompt does not echo. |
--browser | Force the browser flow where TTY detection is unreliable (tmux, wrappers). |
--scope <list> | Permissions to request: read,write,publish,engage. Defaults to all four. |
--key <key> | Discouraged. Readable by every other user on the machine via ps, and kept in shell history. It warns. |
--base-url <url> | Point at another API host — https, or a localhost address. Refuses to send a key over plain http. |
logout
Deletes the stored key. Does not revoke it — do that in Settings → Developers.
echoia logoutwhoami
Which workspace the key opens, and what it is allowed to do.
echoia whoamiWorkspace Acme Social
Token Production key (eko_a1b2…)
Scopes read, write, publishaccounts
Every connected account. A workspace can hold several accounts on the same platform, so the account — not the platform — is the address. Name one by handle (instagram:@acme) or by the id shown here.
echoia accountsPLATFORM ACCOUNT FOLLOWERS ID
instagram @acme 12480 cmq…9wy5a
instagram @acme.studio 1902 cms…b7hqp
x @acme 8140 cmr…ffil3 needs reconnect| Flag | Description |
|---|---|
--json | Raw response. |
post
Creates a post. Draft by default — it only goes out if you pass --at or --now.
echoia post "Doors open Friday at 6pm." \
--accounts instagram:@acme,facebook:@acme \
--at 2d| Flag | Description |
|---|---|
--accounts, -a <list> | Who publishes. Each entry is a handle qualified by platform (instagram:@acme), a bare handle when it is connected on one platform only (@acme), or an id from echoia accounts.required |
--platforms, -p <list> | Shorthand: x,instagram. Only resolves while the platform has exactly one connected account — otherwise the call is refused and names the candidates. |
--at <when> | Schedule it. ISO datetime, or shorthand: 30m, 2h, 3d, 1w. |
--now | Publish immediately, without asking again — the flag is the confirmation. Needs the publish scope. |
--media <urls> | Comma-separated public https:// image or video URLs. |
--first-comment <text> | Posted under the post once it is live. |
--yes, -y | Accepted for compatibility with older scripts; there is no longer a prompt to skip. |
--json | Raw response, including per-account results. |
Targeting the right account
--platforms instagram is not an address when two Instagram accounts are connected. Rather than pick one, the API refuses and tells you what it found:
Error This workspace has 2 instagram accounts — name the one you mean
instead of the platform: @acme (cmq…9wy5a), @acme.studio (cms…b7hqp).Use --accounts. Publishing the same text from two accounts is one call and produces one result per account:
echoia post "Same announcement, both brands." -a instagram:@acme,instagram:@acme.studio --now✓ published cmsab…4nd4yz
→ @acme (instagram)
→ @acme.studio (instagram)
✓ instagram @acme https://instagram.com/p/…
✓ instagram @acme.studio https://instagram.com/p/…posts
Lists posts, most recent first.
echoia posts --status scheduled --limit 10| Flag | Description |
|---|---|
--status <status> | One of draft, scheduled, published, partial, failed. |
--limit, -n <n> | How many to return. Default 20, max 50. |
--json | Raw response. |
stats
Daily snapshots for one post. Metrics appear a few hours after publishing, not immediately.
echoia stats cmsab4nd4yz| Flag | Description |
|---|---|
--json | Raw response. |
comments
Comments across connected accounts, with sentiment and tags.
echoia comments --sentiment negative --limit 20| Flag | Description |
|---|---|
--sentiment <s> | positive, neutral or negative. |
--platform <p> | Restrict to one platform. |
--unreplied | Only comments that have no reply yet. |
--limit, -n <n> | Default 20, max 50. |
--json | Raw response. |
reply
Replies publicly to a comment. Needs the engage scope, and confirms first unless --yes — unlike --now, since a reply goes out to one particular person under your name.
echoia reply <commentId> --message "Thanks — we're on it."| Flag | Description |
|---|---|
--message, -m <text> | The reply body.required |
--yes, -y | Skip the confirmation. |
--json | Raw response. |
This is public
--yes.raw
Calls any endpoint directly, with your stored key attached. Useful for endpoints the CLI has no command for yet.
echoia raw /accounts
echoia raw /posts --method POST --body '{"content":"Hi","accounts":["instagram:@acme"]}'| Flag | Description |
|---|---|
--method <verb> | HTTP method. Defaults to GET. |
--body <json> | Request body as a JSON string. |
Output is always the pretty-printed JSON response — there is no table form to opt out of.
Exit codes
| Code | Meaning |
|---|---|
0 | Success. |
1 | Everything else. The message goes to stderr with a hint — a 403 names the missing scope, a 429 says whether waiting helps. |
- Confirmation prompts are skipped entirely when stdout is not a terminal, so a piped command never hangs waiting for input — pass
--yesdeliberately in CI.