Usage Guide
This document walks you through the most common operations you’ll perform
once you have an astra.Client instance. It is not a reference list of every
method (that lives in the API reference), but rather a narrative tour
showing how to send messages, respond to events, manage groups and accounts,
and build more complex workflows. Think of it as the “daily driver” guide.
Start here if you’re trying to accomplish something with Astra and you just need to know which methods to call and how they fit together.
Client Configuration
Before you can do anything with Astra you must instantiate a Client.
This object represents a single WhatsApp session and holds the browser,
protocol bridge, event dispatcher and convenience method mixins. Most
applications will create exactly one client; if you need multiple bots on
the same machine you can simply create multiple clients with different
session_id values.
The constructor takes a handful of optional arguments that control how the browser launches and how authentication works. You can also override these values with environment variables, which is handy for deployment.
The following table summarises the most important parameters:
Example with explicit configuration:
from astra import Client
client = Client(
session_id="bot123",
phone="919988776655", # trigger pairing code
headless=False,
browser_type="firefox"
)
Chat operations
Once the client is running (await client.start() or inside an
async with block) you can read and write to any chat by using the
client.chat mixin. All methods are asynchronous and raise
AstraError subclasses on failure; you can catch these errors if you
need to retry or log issues.
Note: WhatsApp identifies chats by a string called a JID (Jabber
ID). The library doesn’t try to guess human‑friendly names; you obtain
JIDs from incoming messages via msg.chat_id or by resolving contacts
with await client.get_entity(...).
Send a message to a specific chat:
Use chat.send_message when you know the recipient’s JID. This method
works whether the target is a private one‑on‑one chat or a group, and it
returns a Message object representing the message you just sent.
await client.chat.send_message("911234567890@c.us", "Hello from Astra!")
You can also omit the return value if you don’t care about the sent message id.
await client.chat.send_message("911234567890@c.us", "Hello from Astra!")
Send a text reply inside a handler:
Inside an event handler the msg argument is an instance of
astra.models.message.Message (see Models (astra.models)). It has
convenience methods such as respond and reply that automatically
send the reply to the current chat, and reply quotes the original
message for you.
@client.on_message(Filters.command(".greet"))
async def greet(msg):
await msg.respond("Hey there!") # same chat, no quote
await msg.reply("Quoting you!") # quote-reply
This is the form you’ll use in the vast majority of handlers.
@client.on_message(Filters.command(".greet"))
async def greet(msg):
await msg.respond("Hey there!") # same chat, no quote
await msg.reply("Quoting you!") # quote-reply
React to a message:
Emoji reactions are part of the chat model. Call the react method on
a Message object and supply any Unicode emoji.
await msg.react("👍")
If the bridge is temporarily disconnected the call will raise a
ConnectionLostError; catching and retrying is a reasonable pattern.
await msg.react("👍")
Create a poll:
WhatsApp allows simple multi‑choice polls. send_poll accepts a title and
an array of option strings. The returned Message object can be used to
read poll results later.
await client.chat.send_poll(
chat_id="911234567890@c.us",
title="Lunch?",
options=["Pizza", "Sushi", "Burgers"]
)
You can only create polls in groups.
await client.chat.send_poll(
chat_id="911234567890@c.us",
title="Lunch?",
options=["Pizza", "Sushi", "Burgers"]
)
Delete a message:
The Message.delete helper removes a message. By default it deletes for
everyone in the chat; pass for_everyone=False to delete only for the
current account (useful for cleaning up bot chatter).
await msg.delete(for_everyone=True)
If WhatsApp’s rate limiter is hit, this call may raise a RateLimitedError
with retryable=True.
await msg.delete(for_everyone=True)
Edit a sent message:
Editing is supported for up to a short window after sending (WhatsApp policy). Astra introduces an automatic 0.5 s delay when editing to avoid triggering server rate limits; you don’t need to add your own sleep unless you are performing many edits back‑to‑back.
sent = await client.chat.send_message(jid, "Helo")
await client.chat.edit_message(sent.id, "Hello")
The original text will be replaced and the message will be marked “edited” in the chat.
sent = await client.chat.send_message(jid, "Helo")
await client.chat.edit_message(sent.id, "Hello")
Note
As of v0.0.1b4, Astra includes a mandatory 0.5s stability delay for all message editing operations to prevent WhatsApp rate limiting. This delay is handled automatically by the library.
Mark a chat as read:
Sometimes you want to clear the unread badge without actually fetching or
sending a message. mark_read tells WhatsApp you’ve seen the latest
message in a chat.
await client.chat.mark_read(msg.chat_id)
await client.chat.mark_read(msg.chat_id)
Archive / unarchive:
Archives are a per‑account feature; archiving a chat simply hides it from the main list. The API mirrors the WhatsApp UI flag via a boolean argument.
await client.chat.archive(chat_id, True) # archive
await client.chat.archive(chat_id, False) # unarchive
This is useful for bots that need to clean up after themselves or implement “snooze” behaviour.
await client.chat.archive(chat_id, True) # archive
await client.chat.archive(chat_id, False) # unarchive
Mute a chat:
Mute prevents notifications for a period. The library takes a duration in
seconds (use 0 to unmute).
await client.chat.mute(chat_id, duration_seconds=3600)
Note that WhatsApp enforces a minimum mute interval; attempting to mute for less than a minute may be ignored.
await client.chat.mute(chat_id, duration_seconds=3600)
Group operations
Groups are very common on WhatsApp. Astra’s client.group mixin exposes
all the usual administrative actions. You must be a group admin to modify
participants or change metadata; attempting an unauthorized action will
raise a GroupError with code E3XXX (see Error Codes).
Create a group:
Pass a list of initial participants (their JIDs). The returned object has
id and title attributes.
group = await client.group.create(
name="Study Group",
participants=["911111111111@c.us", "912222222222@c.us"]
)
The library will automatically handle the invitation messages that WhatsApp sends to the participants.
group = await client.group.create(
name="Study Group",
participants=["911111111111@c.us", "912222222222@c.us"]
)
Add or remove members:
Admins can invite or kick users by specifying their phone JID.
await client.group.add_member(group_id, "913333333333@c.us")
await client.group.remove_member(group_id, "913333333333@c.us")
These methods return True on success. If the user is already in the
group, add_member raises an error.
await client.group.add_member(group_id, "913333333333@c.us")
await client.group.remove_member(group_id, "913333333333@c.us")
Promote / demote admins:
Use these helpers to change a member’s role.
await client.group.promote(group_id, "911111111111@c.us")
await client.group.demote(group_id, "911111111111@c.us")
They behave just like the UI; the target must already be a member of the group.
await client.group.promote(group_id, "911111111111@c.us")
await client.group.demote(group_id, "911111111111@c.us")
Update group info:
You can update the subject (group name) and description. These actions are throttled by WhatsApp, so avoid calling them more than once every few seconds.
await client.group.set_subject(group_id, "New Group Name")
await client.group.set_description(group_id, "Updated description")
await client.group.set_subject(group_id, "New Group Name")
await client.group.set_description(group_id, "Updated description")
Get group info:
Retrieve a snapshot of the group’s metadata, including participant
objects. The Participant model has attributes like id and
is_admin.
info = await client.group.get_info(group_id)
print(info.title, len(info.participants))
info = await client.group.get_info(group_id)
print(info.title, len(info.participants))
Join a group via invite link:
If you have a valid invite URL, the bot can join automatically.
await client.group.join("https://chat.whatsapp.com/ABC123...")
This is handy for onboarding a bot to many groups at once, e.g. by scanning QR codes.
await client.group.join("https://chat.whatsapp.com/ABC123...")
Account operations
The client.account mixin exposes settings and actions that affect the
bot’s own WhatsApp account rather than individual chats. These calls map
closely to the options you see in the mobile app’s “Settings” screen.
Get your profile:
Fetches the authenticated user’s profile. Useful when you need the full JID or display name programmatically.
me = await client.get_me()
print(me.name, me.id.serialized)
me = await client.get_me()
print(me.name, me.id.serialized)
Update display name:
await client.account.set_display_name("Astra Bot")
This change is propagated to all of your contacts.
await client.account.set_display_name("Astra Bot")
Update about text:
await client.account.set_about("Powered by Astra Engine")
About text is often used by bots to show status or version information.
await client.account.set_about("Powered by Astra Engine")
Block / unblock contacts:
block prevents messages from the specified JID; unblock reverses it.
Be aware that blocking is account‑wide and persists across restarts.
await client.account.block("911234567890@c.us")
await client.account.unblock("911234567890@c.us")
await client.account.block("911234567890@c.us")
await client.account.unblock("911234567890@c.us")
Advanced patterns
Once you’re comfortable with the basic operations, you can combine them to build more interesting, stateful behavior. These recipes are the sort of things you find yourself writing once your bot is past the “hello world” stage.
Once you’re comfortable with the basic operations, Astra lets you build stateful workflows and integrate with external systems.
Conversations
Use Client.conversation() to create a short-lived inbox where you can
wait_for specific replies or collect a series of messages.
async def handle_survey(client, chat_id):
conv = client.conversation(chat_id, timeout=30)
await conv.send("What is your name?")
name_msg = await conv.wait_for("message")
await conv.send("How old are you?")
age_msg = await conv.wait_for("message")
await conv.send(f"Thanks {name_msg.text}, age {age_msg.text}")
@client.on_message(Filters.command(“.survey”)) async def survey_handler(msg):
await handle_survey(client, msg.chat_id)
Batch operations
You can iterate over chats or contacts by querying the session store directly:
# print the titles of every known chat
for chat in await client.store.iter_chats():
print(chat.id, chat.title)
# bulk-delete the last 20 messages from a chat
msgs = await client.chat.fetch_messages(chat_id, limit=20)
for m in msgs:
await m.delete()
Error handling
Every network or bridge failure surfaces as an AstraError subclass. Use
retryable to decide whether to retry automatically, or log and ignore:
from astra.errors import AstraError
async def safe_send(jid, text):
try:
return await client.chat.send_message(jid, text)
except AstraError as exc:
if exc.retryable:
await asyncio.sleep(1)
return await safe_send(jid, text)
else:
logger.error("permanent send failure: %s", exc)
raise
Set privacy settings:
You can programmatically adjust privacy options such as last‑seen visibility or read receipts, just like in the mobile app.
await client.account.set_last_seen("contacts") # contacts, all, none
await client.account.set_read_receipts(False)
await client.account.set_last_seen("contacts") # contacts, all, none
await client.account.set_read_receipts(False)
Message Attributes
Every Message object exposes a handful of useful boolean flags and
helpers that let you inspect the contents without parsing raw payloads. The
properties listed below are the ones you’ll check most often when writing
filters and handlers.
The Message object contains several properties for quick content inspection:
Checking for media:
@client.on_message(Filters.all)
async def check_media(msg):
if msg.is_media:
print(f"Message {msg.id} contains media of type: {msg.type}")
Media operations
WhatsApp supports sending images, video, audio, documents, stickers, and
more. Astra wraps all of the underlying complexity (uploading to the WA
server, handling types, etc.) in convenient helper methods. Media
operations return a Message object just like text messages.
Send media with caption:
Any file path supported by WhatsApp can be uploaded via
chat.send_media. The method auto-detects the type by file extension,
but you can also force a specific kind (e.g. file vs image) with
keyword arguments.
await client.chat.send_media(
chat_id="911234567890@c.us",
file_path="/path/to/photo.jpg",
caption="Check this out!"
)
If the upload fails due to a transient network error, the underlying
retry logic will automatically attempt a few times before raising.
await client.chat.send_media(
chat_id="911234567890@c.us",
file_path="/path/to/photo.jpg",
caption="Check this out!"
)
Post a status/story:
Astra can also update your account’s status (the feature known as “stories”
on mobile). Text statuses are posted via post_text_status; media
statuses behave like normal media uploads but are visible only for 24 hours.
# Text status
await client.account.post_text_status("Hello from Astra!")
# Media status
await client.chat.send_media_status(
file_path="/path/to/image.png",
caption="My status update"
)
# Text status
await client.account.post_text_status("Hello from Astra!")
# Media status
await client.chat.send_media_status(
file_path="/path/to/image.png",
caption="My status update"
)
Get a contact’s profile picture URL:
You can request the URL of a contact’s profile picture; this is useful if you want to download or display it in an external application.
url = await client.account.get_profile_pic("911234567890@c.us")
The URL is temporary and may expire after a few minutes.
url = await client.account.get_profile_pic("911234567890@c.us")
Diagnostics
Astra exposes several introspection helpers that are useful when debugging or collecting metrics. These calls do not require the bridge to be active – they use local state when possible.
Get engine diagnostics:
This returns a dictionary with information such as uptime, WhatsApp Web version, heartbeats sent/received, and cache statistics. It’s the first thing you should log when investigating connectivity issues.
diag = client.sync_engine.get_diagnostics()
print(f"Uptime: {diag['uptime_s']}s")
print(f"WA Version: {diag['wa_version']}")
print(f"Heartbeats: {diag['total_heartbeats']}")
diag = client.sync_engine.get_diagnostics()
print(f"Uptime: {diag['uptime_s']}s")
print(f"WA Version: {diag['wa_version']}")
print(f"Heartbeats: {diag['total_heartbeats']}")
Get cache statistics:
The session store keeps simple counts of how many chats, contacts and messages it knows about. This is handy for monitoring or throttle logic.
stats = client.store.get_stats()
print(f"{stats['chats']} chats, {stats['contacts']} contacts")
stats = client.store.get_stats()
print(f"{stats['chats']} chats, {stats['contacts']} contacts")
Force a sync:
Normally Astra synchronizes automatically when events arrive. If you need to
manually push a sync (for example, after modifying the local cache), call
client.sync(). This resets the internal stall timer as well.
await client.sync()
await client.sync()