CLI

Commands

Every command, every flag. Run echoia <command> --help for the same reference in your terminal.

--json works everywhere

Any command accepts --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.

shell
echoia login
FlagDescription
--stdinRead the key from stdin: echo "$KEY" | echoia login --stdin. Never reaches argv.
--no-browserPrompt for a key instead of opening a browser. The prompt does not echo.
--browserForce 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.

shell
echoia logout

whoami

Which workspace the key opens, and what it is allowed to do.

shell
echoia whoami
output
Workspace   Acme Social
Token       Production key (eko_a1b2…)
Scopes      read, write, publish

accounts

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.

shell
echoia accounts
output
PLATFORM   ACCOUNT             FOLLOWERS  ID
instagram  @acme               12480      cmq…9wy5a
instagram  @acme.studio        1902       cms…b7hqp
x          @acme               8140       cmr…ffil3          needs reconnect
FlagDescription
--jsonRaw response.

post

Creates a post. Draft by default — it only goes out if you pass --at or --now.

shell
echoia post "Doors open Friday at 6pm." \
  --accounts instagram:@acme,facebook:@acme \
  --at 2d
FlagDescription
--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.
--nowPublish 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, -yAccepted for compatibility with older scripts; there is no longer a prompt to skip.
--jsonRaw 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:

output
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:

shell
echoia post "Same announcement, both brands." -a instagram:@acme,instagram:@acme.studio --now
output
✓ 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.

shell
echoia posts --status scheduled --limit 10
FlagDescription
--status <status>One of draft, scheduled, published, partial, failed.
--limit, -n <n>How many to return. Default 20, max 50.
--jsonRaw response.

stats

Daily snapshots for one post. Metrics appear a few hours after publishing, not immediately.

shell
echoia stats cmsab4nd4yz
FlagDescription
--jsonRaw response.

comments

Comments across connected accounts, with sentiment and tags.

shell
echoia comments --sentiment negative --limit 20
FlagDescription
--sentiment <s>positive, neutral or negative.
--platform <p>Restrict to one platform.
--unrepliedOnly comments that have no reply yet.
--limit, -n <n>Default 20, max 50.
--jsonRaw 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.

shell
echoia reply <commentId> --message "Thanks — we're on it."
FlagDescription
--message, -m <text>The reply body.required
--yes, -ySkip the confirmation.
--jsonRaw response.

This is public

A reply is visible to everyone on the platform and cannot be recalled by Echoia once sent. In a script, review before adding --yes.

raw

Calls any endpoint directly, with your stored key attached. Useful for endpoints the CLI has no command for yet.

shell
echoia raw /accounts
echoia raw /posts --method POST --body '{"content":"Hi","accounts":["instagram:@acme"]}'
FlagDescription
--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

CodeMeaning
0Success.
1Everything 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 --yes deliberately in CI.
Was this page helpful?