> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wsapi.chat/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

> Drive WSAPI from your terminal — built for shell pipelines and AI agents

The WSAPI CLI wraps every endpoint of the public WSAPI OpenAPI spec under a single `wsapi` binary. It's designed for shell pipelines, scripts, and AI agents that want to send messages or manage WhatsApp instances without writing HTTP code.

## What you get

* **Full REST coverage** — every endpoint across messages, groups, communities, contacts, users, chats, calls, newsletters, status, media, and session management
* **JSON in, JSON out** — every command emits JSON to stdout, ready to pipe into `jq`, `xargs`, or any other tool
* **Layered configuration** — flags, environment variables, and named profiles in `~/.wsapi/config.json`. Switch between staging and production with `--profile`
* **`--data` escape hatch** — every body-bearing command accepts a `--data <json>` flag that merges arbitrary fields on top of typed flags
* **Local file uploads** — pass `--data-file ./path` for any media command and the CLI will base64-encode it inline

## Requirements

* Node.js 18 or later
* Active WSAPI subscription with a paired instance
* WSAPI API key and instance ID

## Installation

<Tabs>
  <Tab title="Global install (Recommended)">
    ```bash theme={null}
    npm install -g @wsapichat/cli
    ```

    Verify the installation:

    ```bash theme={null}
    wsapi --version
    wsapi --help
    ```
  </Tab>

  <Tab title="npx">
    ```bash theme={null}
    npx @wsapichat/cli --help
    ```
  </Tab>

  <Tab title="Project dependency">
    ```bash theme={null}
    npm install @wsapichat/cli
    npx wsapi --help
    ```
  </Tab>
</Tabs>

## Configuration

`wsapi` resolves credentials in this order, first match wins:

1. CLI flags: `--api-key`, `--instance-id`, `--base-url`
2. Environment variables: `WSAPI_API_KEY`, `WSAPI_INSTANCE_ID`, `WSAPI_BASE_URL`
3. The named profile from `--profile <name>` or `WSAPI_PROFILE`
4. The `defaultProfile` in the config file

### Named profiles

Use the built-in `wsapi config` commands to manage profiles:

```bash theme={null}
# Create a profile and mark it as the default
wsapi config set-profile prod \
  --api-key "ws_live_..." \
  --instance-id "ins_..." \
  --default

wsapi config list                # list profile names
wsapi config use staging         # switch the default
wsapi config rm staging          # delete a profile
wsapi config path                # print the config file path
```

The config file is stored at `~/.wsapi/config.json` (or `$XDG_CONFIG_HOME/wsapi/config.json`) with mode `0600`.

### Environment variables

You can bypass the config file entirely:

```bash theme={null}
export WSAPI_API_KEY="ws_live_..."
export WSAPI_INSTANCE_ID="ins_..."

wsapi session status
```

| Variable            | Required | Description                                | Default                  |
| ------------------- | -------- | ------------------------------------------ | ------------------------ |
| `WSAPI_API_KEY`     | Yes      | WSAPI API key for authentication           | —                        |
| `WSAPI_INSTANCE_ID` | Yes      | WhatsApp instance to control               | —                        |
| `WSAPI_BASE_URL`    | No       | Custom API endpoint                        | `https://api.wsapi.chat` |
| `WSAPI_PROFILE`     | No       | Named profile to load from the config file | `defaultProfile`         |

## Usage

### Pair a device

```bash theme={null}
# Save the QR PNG to a file and scan it with WhatsApp
wsapi session qr --output qr.png

# Or get the raw QR string (useful for rendering yourself)
wsapi session qr --text

# Pair via phone-number code instead of QR
wsapi session pair-code 15551234567

# Confirm the device is paired
wsapi session status
```

### Send messages

```bash theme={null}
# Plain text
wsapi messages text \
  --to 15551234567@s.whatsapp.net \
  --text "Hello from the CLI"

# Image by URL with a caption
wsapi messages image \
  --to 15551234567@s.whatsapp.net \
  --url https://example.com/cat.jpg \
  --caption "cat"

# Image from a local file
wsapi messages image \
  --to 15551234567@s.whatsapp.net \
  --data-file ./photo.jpg \
  --view-once

# Document with a custom filename
wsapi messages document \
  --to 15551234567@s.whatsapp.net \
  --data-file ./report.pdf \
  --filename "Q1 report.pdf" \
  --caption "this quarter"

# React to a message
wsapi messages react 3EB0... --to 15551234567@s.whatsapp.net --reaction "👍"
```

### Group operations

```bash theme={null}
# List joined groups, then print just their names
wsapi groups list | jq -r '.[].name'

# Get group info
wsapi groups get 1234567890@g.us

# Add a participant
wsapi groups update-participants 1234567890@g.us \
  --action add \
  --participants 15551234567@s.whatsapp.net
```

### Download inbound media

```bash theme={null}
wsapi media download <mediaId> --output incoming.jpg
```

Without `--output`, binary endpoints write to stdout. Pipe it into a file or another command.

## Command catalog

| Group             | Commands                                                                                                                                                                                                                                                                                                 |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`session`**     | `status`, `qr`, `qr --text`, `pair-code <phone>`, `logout`, `flush-history`                                                                                                                                                                                                                              |
| **`messages`**    | `text`, `image`, `video`, `audio`, `voice`, `document`, `sticker`, `contact`, `location`, `link`, `react`, `edit`, `read`, `star`, `pin`, `delete`, `delete-for-me`                                                                                                                                      |
| **`groups`**      | `list`, `get`, `create`, `set-name`, `set-description`, `set-picture`, `leave`, `participants`, `update-participants`, `invite-link`, `reset-invite`, `set-announce`, `set-locked`, `set-join-approval`, `set-member-add-mode`, `join-link`, `join-invite`, `invite-info`, `requests`, `update-requests` |
| **`communities`** | `list`, `get`, `create`, `leave`, `set-name`, `set-description`, `set-picture`, `set-locked`, `participants`, `update-participants`, `invite-link`, `reset-invite`, `subgroups`, `create-subgroup`, `link-subgroup`, `unlink-subgroup`                                                                   |
| **`contacts`**    | `list`, `get`, `create`, `sync`, `blocklist`, `block`, `unblock`                                                                                                                                                                                                                                         |
| **`users`**       | `me`, `update-me`, `set-presence`, `privacy`, `set-privacy`, `check`, `check-bulk`, `profile`                                                                                                                                                                                                            |
| **`media`**       | `download <id> --output <file>`                                                                                                                                                                                                                                                                          |
| **`chats`**       | `list`, `get`, `delete`, `picture`, `business`, `set-presence`, `subscribe-presence`, `set-ephemeral`, `mute`, `pin`, `archive`, `read`, `request-history`, `clear`                                                                                                                                      |
| **`calls`**       | `reject`                                                                                                                                                                                                                                                                                                 |
| **`newsletters`** | `list`, `get`, `invite-info`, `create`, `subscribe`, `unsubscribe`, `mute`, `unmute`                                                                                                                                                                                                                     |
| **`status`**      | `privacy`, `text`, `image`, `video`, `delete`                                                                                                                                                                                                                                                            |

Run `wsapi <group> --help` and `wsapi <group> <command> --help` for the full list of options on each command.

## Escape hatch: `--data`

Every body-bearing command supports a `--data <json>` flag whose JSON object is merged on top of the typed flags. Keys in `--data` win over flags, so it's both an extension point (for fields the CLI doesn't expose explicitly) and a full-body override.

```bash theme={null}
# Add a field the CLI doesn't expose
wsapi messages text \
  --to 15551234567@s.whatsapp.net \
  --text "hi" \
  --data '{"someNewField":true}'

# Load the body from a file
wsapi messages text \
  --to 15551234567@s.whatsapp.net \
  --text "ignored" \
  --data @./body.json
```

## Exit codes & error format

* `0` — success
* `1` — any error (config, API, network, validation)

API errors are emitted to stderr as JSON:

```json theme={null}
{
  "error": "device not paired",
  "status": 403,
  "body": { "status": 403, "detail": "device not paired" }
}
```

CLI/config errors are plain text on stderr:

```
error: missing API key. Set WSAPI_API_KEY, pass --api-key, or run `wsapi config set-profile`.
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Missing API key or instance ID">
    Make sure at least one of the resolution sources is populated: `--api-key`/`--instance-id` flags, `WSAPI_API_KEY`/`WSAPI_INSTANCE_ID` environment variables, or a profile saved with `wsapi config set-profile`.
  </Accordion>

  <Accordion title="`device not paired` (403)">
    Run `wsapi session status` to confirm the device state, then pair again with `wsapi session qr --output qr.png` or `wsapi session pair-code <phone>`.
  </Accordion>

  <Accordion title="Switching between staging and production">
    Save one profile per environment and select with `--profile staging` (or `WSAPI_PROFILE=staging`). Use `wsapi config use <name>` to change the default profile permanently.
  </Accordion>

  <Accordion title="Need to send a field the CLI doesn't expose?">
    Use `--data '{"newField":true}'` on any body-bearing command, or `--data @./body.json` to load the entire body from a file.
  </Accordion>
</AccordionGroup>

## GitHub

[github.com/wsapi-chat/wsapi-cli](https://github.com/wsapi-chat/wsapi-cli)
