Getting started¶
From nothing to an agent that answers you in WhatsApp. About five minutes.
1. Create the agent¶
In WhatsApp on your phone:
- Settings → Agents → Create an agent
- Give it a display name and an avatar
- Open the agent's chat → Chat info → API key
- Copy the key
That key is your API token. It identifies the agent and grants full control of it, so treat it like a password: keep it out of source control, and pass it through the environment.
Two ways to lose the token
Rotating it invalidates the old one, and reinstalling WhatsApp means generating a new one. A token that is present but no longer valid produces a 400 with code 100 — which looks like a bad request, not an auth problem. If working code suddenly fails that way, check the token first.
2. Install the library¶
Python 3.9 or newer. The only runtime dependency is requests. The package is whagent on PyPI.
3. Write the agent¶
Save this as mybot.py:
import os
from whagent import Agent, FileOffsetStore
agent = Agent(
os.environ["WHATSAPP_AGENT_TOKEN"],
# Remember where we stopped, so a restart does not skip messages.
offset_store=FileOffsetStore(".whagent-offset"),
)
@agent.on_text(r"^/start")
def start(ctx):
ctx.reply(f"Hi {ctx.profile_name or 'there'} — send me anything.")
@agent.on_text
def echo(ctx):
if ctx.text.startswith("/"):
return # handled above
ctx.reply(f"You said: {ctx.text}")
@agent.on_media
def describe(ctx):
ctx.reply(f"That is a {ctx.type} ({ctx.message.media.mime_type}).")
if __name__ == "__main__":
agent.run()
4. Run it¶
Now write to the agent in WhatsApp. You should see the message go blue-ticked, a typing… indicator, then the reply. All of that is run(): it polls for updates, marks each message read, shows the indicator while your function runs, and dispatches to whichever handlers match.
Stop it with Ctrl+C.
What just happened¶
flowchart LR
A["agent.run()"] --> B["GET /updates<br/>waits for you"]
B --> C["POST /statuses<br/>read + typing"]
C --> D["your handler<br/>runs"]
D --> E["POST /messages<br/>the reply"]
E --> B
Every handler receives a ctx — the message, plus everything you need to answer it:
@agent.on_text
def handle(ctx):
ctx.text # what they wrote
ctx.sender # who wrote it (send it back unchanged)
ctx.profile_name # their display name, when there is one
ctx.reply("...") # answer with text
ctx.reply("...", quote=True) # answer quoting their message
ctx.reply_image(file="chart.png", caption="here")
ctx.download("/tmp/file") # save media they sent
Send something without the loop¶
If you only want to push a message — from a cron job, a script, anywhere — skip the Agent layer:
from whagent import Client
with Client(os.environ["WHATSAPP_AGENT_TOKEN"]) as client:
result = client.send_text("Deploy finished ✅")
print(result.message_id)
You never name the recipient: an agent has exactly one, and the client finds it by itself before the first send. Don't hardcode an identifier — it can change.
Next¶
- How it works — polling, offsets, ids: the model in six short pieces
- Questions and answers — who it can message, what happens offline, running two agents
- Sending messages — every message type, captions, quotes, previews
- Recipes — persistence, slow work, threads, running it as a service