> ## 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.

# CLI

> Send and receive iMessages from your terminal

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

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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.

<Note>
  Pin a specific release with `MESSAGES_CLI_VERSION=v0.2.0` or change the
  install location with `MESSAGES_INSTALL=/opt/messages`.
</Note>

If you already have Node.js, you can install via npm instead:

<CodeGroup>
  ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  npm install -g @messages-dev/cli
  ```

  ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  pnpm add -g @messages-dev/cli
  ```

  ```bash bun theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  bun add -g @messages-dev/cli
  ```
</CodeGroup>

<Warning>
  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.
</Warning>

## Authenticate

Two options. Pick the one that fits your context.

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
# 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](https://app.messages.dev) under **API Keys**.

## Send your first message

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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:

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
echo "deploy finished in $(date +%T)" | messages-dev send +14155551234
```

## Commands

Run `messages-dev <command> --help` for full flag detail on any of these.

| Command                               | What it does                                                                                     |
| ------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `login` / `logout`                    | Sign 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 list`                          | List 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 --chat`     | Fetch a single message.                                                                          |
| `reactions list --message <id>`       | List reactions on a message.                                                                     |
| `receipts list --line --chat`         | List 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` / `delete` | Manage webhook subscriptions.                                                                    |
| `listen`                              | Stream 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 code | Meaning                               |
| --------- | ------------------------------------- |
| `0`       | OK                                    |
| `1`       | Generic failure                       |
| `2`       | Usage error (bad flags, missing args) |
| `3`       | Not authenticated                     |
| `4`       | Not found                             |
| `5`       | Validation error                      |

## Recipes

### Pipe stdin into a message

Anything that produces text on stdout can become an iMessage:

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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.

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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:

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
messages-dev listen --event message.received --event message.failed
messages-dev listen --line ln_abc123
```

### Upload an attachment, then send it

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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:

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
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](#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`:

```sh theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
curl -fsSL https://www.messages.dev/install.sh | MESSAGES_CLI_VERSION=v0.2.0 sh
```

## See also

<CardGroup cols={2}>
  <Card title="TypeScript SDK" icon="js" href="/docs/sdk">
    The same API as the CLI, in your code.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/docs/concepts/webhooks">
    Long-term event delivery once you outgrow `listen --forward-to`.
  </Card>
</CardGroup>
