Skip to main content

Documentation Index

Fetch the complete documentation index at: https://messages.dev/docs/llms.txt

Use this file to discover all available pages before exploring further.

The messages-dev CLI is the fastest way to poke at the API by hand, pipe a shell pipeline into a message, or stream live events into a local dev server. It is a single self-contained binary built from the same SDK that powers the TypeScript client.

Install

curl -fsSL https://www.messages.dev/install.sh | sh
The script detects your OS and architecture (macOS arm64/x64, Linux arm64/x64), downloads the matching binary from the latest GitHub release, verifies its SHA-256 checksum, and installs it to ~/.messages/bin/. It prints the line you need to add to your shell rc.
Pin a specific release with MESSAGES_CLI_VERSION=v0.2.0 or change the install location with MESSAGES_INSTALL=/opt/messages.
If you already have Node.js, you can install via npm instead:
npm install -g @messages-dev/cli
On macOS the curl-installed binary is not yet signed with an Apple Developer ID. The first time you run it, Gatekeeper may block it; right-click the binary in Finder and choose Open once, or run xattr -d com.apple.quarantine ~/.messages/bin/messages-dev. The npm install does not have this issue.

Authenticate

Two options. Pick the one that fits your context.
# Interactive: stores credentials in ~/.messages/config.json
messages-dev login

# Headless / CI: short-circuit login by exporting an API key
export MESSAGES_API_KEY=sk_live_...
MESSAGES_API_KEY always wins over the config file. Create a key in the dashboard under API Keys.

Send your first message

messages-dev login
messages-dev lines list
messages-dev send +14155551234 "hi from the terminal"
If you have exactly one active line, --from is optional. Otherwise pass --from <handle> to disambiguate. You can also pipe text in over stdin:
echo "deploy finished in $(date +%T)" | messages-dev send +14155551234

Commands

Run messages-dev <command> --help for full flag detail on any of these.
CommandWhat it does
login / logoutSign in interactively; remove stored credentials
send <to> [text]Send a text, attachment, or audio message. Reads stdin when [text] is omitted.
react <msg-id> <emoji>Send a tapback. love, like, dislike, laugh, emphasize, question, or a literal emoji.
typing <to>Send a typing indicator. --off clears it.
read <to>Mark a chat as read.
lines listList the lines on your account.
chats list --line <handle>List chats on a line.
messages list --line <h> --chat <r>List messages in a chat. Cursor-paginated.
messages get <id> --line --chatFetch a single message.
reactions list --message <id>List reactions on a message.
receipts list --line --chatList read receipts in a chat.
outbox get <id>Inspect an outbound item by obx_… id.
files upload <path>Upload a file, print its file_… id.
files get <id>Print a download URL, or stream bytes with --download.
webhooks list / create / deleteManage webhook subscriptions.
listenStream events live; with --forward-to, post HMAC-signed webhooks to a local URL.

Output and exit codes

By default every command renders human-readable output. Pass --json (or set MESSAGES_OUTPUT=json once for the whole shell) to switch to structured output on stdout. Errors always go to stderr; with --json they take the shape {"error":{"code":"…","message":"…"}}.
Exit codeMeaning
0OK
1Generic failure
2Usage error (bad flags, missing args)
3Not authenticated
4Not found
5Validation error

Recipes

Pipe stdin into a message

Anything that produces text on stdout can become an iMessage:
git log -1 --pretty=%B | messages-dev send +14155551234
tail -n 50 /var/log/app.log | messages-dev send oncall@icloud.com

Forward live events to a local webhook handler

listen --forward-to turns the CLI into a poor-man’s tunnel for webhook development. It subscribes to your account’s event stream and POSTs each event to your local URL with the same HMAC headers production webhooks use, so you can develop and test webhook handlers without ngrok or a tunnel.
messages-dev listen --forward-to http://localhost:3000/webhooks
The CLI prints a session HMAC secret on first run; verify deliveries against that, or pin one with MESSAGES_LISTEN_SECRET=…. Filter to specific events or scope to a single line:
messages-dev listen --event message.received --event message.failed
messages-dev listen --line ln_abc123

Upload an attachment, then send it

file_id=$(messages-dev files upload ./photo.jpg --json | jq -r .id)
messages-dev send +14155551234 "look at this" --attach "$file_id"
Or in one go without jq by upgrading the recipient flow to your code’s needs:
messages-dev send +14155551234 "look at this" \
  --attach "$(messages-dev files upload ./photo.jpg)"
(The non-JSON files upload output prints the bare file_… id, suitable for direct substitution.)

Tapback the latest message in a thread

last_id=$(messages-dev messages list \
  --line +14155551234 --chat +14150000000 --limit 1 --json \
  | jq -r '.data[0].id')
messages-dev react "$last_id" love

Troubleshooting

command not found: messages-dev — the install directory is not on your PATH. Add export PATH="$HOME/.messages/bin:$PATH" to your shell rc, or run the binary by full path. macOS blocks the binary on first run — Gatekeeper. See the warning under Install. The npm-installed CLI does not hit this. not_authenticated errors — you signed in on a different machine, or your API key was revoked. MESSAGES_API_KEY takes precedence over ~/.messages/config.json; if you have a stale key exported, the file is ignored. Run messages-dev login or unset MESSAGES_API_KEY. Pin or roll back a version — the install script honors MESSAGES_CLI_VERSION:
curl -fsSL https://www.messages.dev/install.sh | MESSAGES_CLI_VERSION=v0.2.0 sh

See also

TypeScript SDK

The same API as the CLI, in your code.

Webhooks

Long-term event delivery once you outgrow listen --forward-to.