wacli drives WhatsApp as a linked device, and its own build instructions disagree
WhatsApp CLI: sync, search, send. Build from source wacli requires Go 1.26.5 or newer and uses go-sqlite3, so cgo + a C compiler are required.
At a glance
- What is it?
- A Go WhatsApp client for the terminal that pairs like a second phone, mirrors messages into two local SQLite databases, and hands write commands to whichever process holds the store lock. The build requirements are stated three different ways.
- Who is it for?
- Settle the service terms before anything else. wacli speaks the WhatsApp Web protocol rather than an official API, is not affiliated with WhatsApp or Meta, and its own README tells you to send only to recipients you are allowed to contact, so whether your use is permitted is a question for the account's owner and for WhatsApp, not for this tool. Assuming that is settled, the practical checks are three.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- openclaw/wacli2,929 ★ · +265
| Repository | 2026-07-28 | 2026-11-13 |
|---|---|---|
| openclaw/wacli | 2.7k | 2.9k |
265 Stars in the last 108 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
It is a WhatsApp linked device, not an official client
wacli speaks the WhatsApp Web protocol through the whatsmeow library, and the README says plainly that it is not affiliated with WhatsApp or Meta. That fact comes first because the rest follows from it: this is a client for an account you own, talking the same protocol the official app talks, pairing the way a second phone would. Pairing happens by scanning a QR code rendered in the terminal from WhatsApp's Linked devices screen, and auth performs the first sync once that scan completes.
wacli auth
wacli messages search "meeting"
wacli send text --to +15551234567 --message "hello"The README draws a matching line on the send side, stating that sending requires a recipient you are allowed to contact. Recipients can be phone numbers, WhatsApp JIDs, or synced contact, group, and chat names. The tool puts no gate of its own on either step, so both depend on the account and on WhatsApp's policy rather than on anything the CLI enforces.
The Go minimum is stated three ways and none of them match
The build requirement is given three times with three different numbers. The repository description says building from source requires Go 1.26.5 or newer. The install section of the README says Go 1.27.0 or newer plus a C compiler. The development section says the toolchain is Go 1.27.1 as selected by go.mod while the source minimum remains Go 1.27.0. The module file backs the last two, carrying a go directive of 1.27.0 and a toolchain line of go1.27.1. The Makefile then goes further than any prose does: its lint target compares the output of go env GOVERSION against go1.27.1 and fails on anything else. A contributor on 1.27.0 therefore satisfies the source minimum and the module directive while failing the lint gate, and the 1.26.5 in the repository description is a minor version behind what the module asks for.
Three build paths inject the same flag to silence a C diagnostic
Every documented way to build wacli sets the same C compiler flag, and the flag is a suppression rather than a fix. The README install command, the package.json build script, and the Dockerfile all put -Wno-error=missing-braces into CGO_CFLAGS. That option tells the C compiler to treat a missing-braces diagnostic as a warning instead of an error, which means whoever builds the project compiles C code through cgo with one class of error deliberately downgraded. None of the three places says what produces the diagnostic, so the instruction travels to every consumer without its cause. The build script spells out the shape of it, setting CGO_ENABLED=1 and the CGO_CFLAGS value before handing off to go build with the sqlite_fts5 tag.
cgo is the storage layer, and a test exists to keep it that way
cgo is not a convenience in this project, it is how storage works. go.mod requires mattn/go-sqlite3 directly, and the README attributes the C compiler requirement to that driver. The project then writes a check to keep the dependency from quietly becoming optional: the script test:cgo-required runs a build with CGO_ENABLED=0 and greps the output for wacli_requires_cgo_enabled_1_for_go_sqlite3, so a cgo-less build is expected to fail with that specific message rather than something vaguer. Full text search is a second build-time choice layered on top, since every build in the repository passes -tags sqlite_fts5 and the test suite is split to match, with test:go running go test untagged and test:fts running the same command with the tag. The module graph also carries a second SQLite family the documented build never uses: ncruces go-sqlite3, go-sqlite3-wasm, and julianday, all marked indirect.
Write commands are delegated to whichever process holds the store lock
Concurrent processes are handled by handing work over rather than by contending. Write commands take a per-store lock, and once a sync --follow process finishes startup, the supported send commands plus the chat state commands (mark-read, archive, pin, mute, and their inverses) are delegated to it for as long as it owns that lock. A second wacli invocation therefore does not write to the store directly while a follow-mode sync is running; it asks the process holding the lock to do it. Two things follow for anyone scripting this. Commands outside that delegated set take the store lock themselves, and so does anything run when no sync process owns it. And because the lock is per store rather than global, named accounts do not contend with one another, which is what makes several identities on one machine workable.
Search reads a local index, and history arrives best effort
The WhatsApp session and the searchable message index live in two separate SQLite databases, and that split is what makes the tool usable from a script. Search reads the local index, so it keeps working with no live WhatsApp connection at all. Filters belong to the same command surface, so narrowing to messages that carry media is part of the search call rather than a separate pass:
wacli messages search "invoice" --has-media
wacli --json messages list --limit 20The catch is history depth, and the README is direct about it: WhatsApp Web provides history on a best-effort basis. What the local store holds is therefore not the full record of an account, which is why history coverage exists as a command for inspecting what is available locally before anyone asks the primary phone for older messages. Treat the mirror as a cache that begins incomplete.
Read-only mode and the split between human and machine output
Two switches target automation, and they cover different risks. The first is write access, through --read-only or WACLI_READONLY=1, for integrations that must not change WhatsApp or the local store.
wacli --read-only --json messages search "invoice"
WACLI_READONLY=1 wacli --json doctorThe second is output shape. Human readable tables are the default, --json is meant for one-shot commands, and --events emits NDJSON lifecycle events from long-running commands, so a follow-mode sync has a machine format without every other command changing. Progress and errors stay on stderr, which keeps them from landing inside a piped JSON payload. Store location follows platform convention instead of one fixed path: ~/.local/state/wacli on Linux and ~/.wacli everywhere else, overridable with --store DIR or WACLI_STORE_DIR. The container image takes a third route, pointing WACLI_STORE_DIR at /data/store and moving XDG state, config, and cache under the same volume.
macOS ships through its own release config and a higher system floor
macOS and Linux/Windows are released through two separate GoReleaser configurations, .goreleaser.yaml and .goreleaser-linux-windows.yaml, and the split shows up in the release variables. make release refuses to run without TAG, COMMIT, CROSS_PLATFORM_DIR, CROSS_PLATFORM_MANIFEST_SHA256, and MAC_RELEASE, all five named in its own usage message, which is the shape of a notarized macOS path sitting beside a plainer cross-compiled one. Prebuilt archives cover macOS, Linux, and Windows, but official macOS binaries require macOS 15, Sequoia, or newer, so the archive route does not support the same systems as the Homebrew route. A snapshot target builds credential-free artifacts and passes --skip=publish. The container image draws a narrower line: Alpine 3.24 with ffmpeg and tzdata added, a non-root user at uid 10001, one /data volume, and CMD set to --help.
Editorial conclusion
Settle the service terms before anything else. wacli speaks the WhatsApp Web protocol rather than an official API, is not affiliated with WhatsApp or Meta, and its own README tells you to send only to recipients you are allowed to contact, so whether your use is permitted is a question for the account's owner and for WhatsApp, not for this tool. Assuming that is settled, the practical checks are three. Confirm your machine can build with cgo, since storage goes through go-sqlite3 and a cgo-less build is designed to fail. Confirm your Go toolchain matches what go.mod names rather than the version in the repository description. And read the store model before scripting it, because search reads a local index that starts out incomplete and write commands are delegated to a running sync process rather than executed where you typed them.
Frequently asked questions
Is there a WhatsApp CLI?
wacli is one. It pairs as a linked device, mirrors messages into a local SQLite store, and supports search, sending, and chat management from the command line. It uses the WhatsApp Web protocol through whatsmeow and is not affiliated with WhatsApp or Meta. The README credits whatsapp-cli by Vicente Reig as its inspiration.
how to use wacli
Pair it by scanning the terminal QR code from WhatsApp's Linked devices screen, then run wacli auth to perform the first sync. After that, wacli sync --follow keeps the local store current, and wacli messages search runs against the local index even with no live WhatsApp connection.
how to install wacli
Three routes are given. Homebrew on macOS or Linux uses brew install openclaw/tap/wacli, prebuilt archives for macOS, Linux, and Windows come from GitHub Releases, and a source build needs a C compiler plus Go. The version numbers for that Go requirement disagree across the repository, so go.mod is the one to read.
is wacli safe
The repository makes no safety claim, so judge the surfaces instead. Pairing registers the CLI as a WhatsApp linked device that WhatsApp can revoke, write commands take a per-store lock, and --read-only or WACLI_READONLY=1 stops an integration from changing WhatsApp or the local store. The project is MIT licensed, and its lint target runs govulncheck against the source and the built binary.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/openclaw-wacli)Star history chart for your README
[](https://hysenlabs.com/projects/openclaw-wacli)Was this useful?