Skip to main content

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.

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

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:
# 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:
export WSAPI_API_KEY="ws_live_..."
export WSAPI_INSTANCE_ID="ins_..."

wsapi session status
VariableRequiredDescriptionDefault
WSAPI_API_KEYYesWSAPI API key for authentication
WSAPI_INSTANCE_IDYesWhatsApp instance to control
WSAPI_BASE_URLNoCustom API endpointhttps://api.wsapi.chat
WSAPI_PROFILENoNamed profile to load from the config filedefaultProfile

Usage

Pair a device

# 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

# 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

# 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

wsapi media download <mediaId> --output incoming.jpg
Without --output, binary endpoints write to stdout. Pipe it into a file or another command.

Command catalog

GroupCommands
sessionstatus, qr, qr --text, pair-code <phone>, logout, flush-history
messagestext, image, video, audio, voice, document, sticker, contact, location, link, react, edit, read, star, pin, delete, delete-for-me
groupslist, 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
communitieslist, 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
contactslist, get, create, sync, blocklist, block, unblock
usersme, update-me, set-presence, privacy, set-privacy, check, check-bulk, profile
mediadownload <id> --output <file>
chatslist, get, delete, picture, business, set-presence, subscribe-presence, set-ephemeral, mute, pin, archive, read, request-history, clear
callsreject
newsletterslist, get, invite-info, create, subscribe, unsubscribe, mute, unmute
statusprivacy, 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.
# 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:
{
  "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

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.
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>.
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.
Use --data '{"newField":true}' on any body-bearing command, or --data @./body.json to load the entire body from a file.

GitHub

github.com/wsapi-chat/wsapi-cli