Skip to main content

whatsapp-agent-cli

A client for the WhatsApp Agent Platform — a library first, with a command-line tool on top.

PyPI Python Tests License

Send and receive WhatsApp messages from a script, a cron job, a git hook, or a coding agent with shell access. It handles the parts that are tedious to get right — the long-poll and its cursor, per-method rate limits, the 4,096-character send cap, the two-hop media fetch, and an error table where one code means "back off" and another means "this token is dead, stop".

wa-agent send "deploy finished, 3 tests failing"

It knows nothing about coding agents, folders, permissions or models. It moves messages.

First product built on it: Hisab — a ledger that texts back. Double-entry bookkeeping for small businesses, run entirely from WhatsApp. More below.

Contents

Install

pip install wa-agent

The package, the command and the module are all wa-agent / wa_agent. (This repository is named whatsapp-agent-cli; that name and whatsapp-agent both belong to unrelated projects on PyPI.)

Transcription needs no extra install — only a key, because it is an ordinary HTTP call:

export GEMINI_API_KEY='…'
wa-agent transcribe voice-note.ogg

Get a token

In WhatsApp: Settings → Agents → Create an agent → Chat info → API key. An agent may only message its own creator — you — which is why there is no recipient management here.

export WHATSAPP_AGENT_TOKEN='…'        # or: wa-agent --token-file ~/.wa-token …

Use it

# say something to yourself
wa-agent send "the backup finished"

# read what arrives, one JSON object per line, until you stop it
wa-agent recv --follow --json

# attach a file; the text becomes its caption
wa-agent send "this week's numbers" --file chart.png

# fetch something someone texted you, and open it
open "$(wa-agent media get <media-id>)"

The first recv records who you are, after which send needs no --to.

A voice note keeps its shape and gains the words, so code that reads text.body finds them and nothing about the message is lost:

{"id": "wamid.A", "type": "audio", "audio": {"id": "media-1", "voice": true},
 "text": {"body": "call me back at six"}, "transcribed": true}

Without a key, recv --transcribe warns once and delivers voice notes marked transcribed: false rather than stopping.

recv --download fetches each photo, document and voice note into the state directory as it arrives and adds a path to the message. It is opt-in because it puts a fetch inside the delivery loop; a download that fails is delivered marked with download_error, never dropped. With --transcribe as well, a voice note is fetched once, kept, and transcribed from that copy.

Command Does
send <text> Send a message. Splits a long body on paragraph boundaries, numbers the parts (i/n), converts markdown to WhatsApp formatting
send --file <path> Upload and attach. --media <id> attaches something already uploaded
send --dry-run Print exactly what would be sent, send nothing, need no token
recv Messages since the last run. --json for one object per line, --follow to stream, --typing to show a typing indicator while you work, --transcribe to add words to voice notes, --download to keep photos and files as they arrive
transcribe <file> Audio in, text out. Gemini today; offline is #20
media get <id> Download to the state directory, or --out DIR. Prints the path and nothing else
media put <path> Upload, print the media id
errors The exit-code table

Global options — --token-file, --state-dir, --profile — go before the subcommand, as in git:

wa-agent --profile work recv --follow     # yes
wa-agent recv --follow --profile work     # no: unrecognized argument

Use it from Python

from wa_agent import WhatsApp, Store, WhatsAppError

client = WhatsApp(token)
for sent in client.send_iter("user:123", "**done** in 40s"):
    print(sent.id, sent.text)       # one per part, as each leaves

messages, cursor = client.poll(offset=None)
for message in messages:
    print(message["from"], message.get("text", {}).get("body"))

send_iter yields each part as it is delivered, so a failure halfway never hides what already arrived. Store is the message log and the cursor, keyed by the platform's own message ids.

Where it keeps things

Nothing is written into your working directory. State lives at $XDG_STATE_HOME/wa-agent/<profile>/ (or ~/.local/state/…), holding the poll cursor, the message log and downloaded media. --state-dir moves it; --profile keeps two agents apart.

One poller per token. The platform allows a single long-poll per agent and answers 409 when a second one takes the cursor, so recv exits rather than silently competing for your messages.

When something fails

Every failure names its code and exits with a number a script can branch on:

error [platform_rejected]: the platform refused this request; retrying will not help
detail: POST /messages: HTTP 400 error.code 131009 …

wa-agent errors lists them all. docs/errors.md says what to do about each and which are worth retrying — the short version is that exit 7 is, and 4 and 6 never are.

Built on it: Hisab

Hisab — a ledger that texts back

Hisab is a plain-language ledger you keep by texting WhatsApp — "2500 coffee" posts an entry, "how much do I owe Metro?" gets an answer — in English, Urdu or Roman Urdu, by voice, photo or text, with every entry checked by hledger before it is written.

It is where this package came from. The cursor that only advances after a batch, the dedup, the per-method rate limits and the dead-token exit were all learned running Hisab against real messages, then extracted here so nothing else has to learn them again. Hisab is the first product on this transport, and moves onto the published wa-agent package next.

The two repositories split the work cleanly:

whatsapp-agent-cli (this) hisab-whatsapp
Is the transport: messages, media, transcription a product: a ledger with a model and six tools
Knows about tokens, cursors, rate limits accounts, entries, hledger
You use it from a script, a cron job, or your own agent by texting it

Coming next: the relay

The transport moves messages. The relay is what makes it an agent in your pocket.

wa-agent relay --folder ~/code/my-project     # coming soon

Text it from your phone — "why is the deploy failing?", a screenshot of an error, a voice note describing a bug — and it runs a coding agent such as Claude Code over that folder and sends back what it says. The design is settled; the code starts once this release is out:

  • Read-only by default. The agent can read the folder and nothing else. Writable paths are declared, never assumed, and a folder created later is denied until you say otherwise.
  • The relay owns the session. Starting fresh, switching models and compacting a long conversation happen in the relay, before the agent is called, because none of them survive a non-interactive run otherwise.
  • Everything it hears, it can use. Voice notes arrive as words, photos and files arrive by path, and a quoted reply arrives with the message it quoted — all from this package, underneath.
  • One command in this package, optional. pip install wa-agent never makes you run it. The transport stays usable on its own, and the relay uses it exactly as your own scripts would.

Claude Code comes first, Codex after. Follow along on the issues.

Contributing

cp .env.example .env    # fill in your agent token, and a Gemini key if you want transcription
make dev                # a virtualenv with this checkout installed
make check              # the check: no network, no token, a couple of seconds
make up                 # a live inbox: text your agent and watch it land, until Ctrl-C
make live               # a scripted round trip: send, wait for your reply, read it back

make up and make live keep their state in .live-state/, never your real one, and make clean removes it. develop is the trunk and PRs target it; main is what is published. Conventions live in AGENTS.md.

License

MIT.

Release files for wa-agent 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wa-agent 0.1.0
File Size Uploaded
wa_agent-0.1.0.tar.gz 44.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wa-agent 0.1.0
File Interpreter ABI Platform
wa_agent-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 74.5 kB

Release files / wa_agent-0.1.0.tar.gz

Download URL wa_agent-0.1.0.tar.gz
Size 44.3 kB
Tags Source
SHA-256 checksum
How to use checksums
5b8db95a64d2210113913236f3fdecd1525c5915430edba78197c4074f0bd92f
BLAKE2b-256 checksum
How to use checksums
2a2812a4cb136a6c0e902a95332aea76c310e7de5b5463c36c773714e1a75846
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / wa_agent-0.1.0-py3-none-any.whl

Download URL wa_agent-0.1.0-py3-none-any.whl
Size 30.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1d07ba59be1f8d471a7cf0b60951594fd33e7df79d36ba2cd2c3dd4f554632d0
BLAKE2b-256 checksum
How to use checksums
d352f696b9bf800d61fc3fc964ab4bedf2235c9b4a8a01f035d3925a9219ef95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page