Skip to content
52 changes: 49 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,55 @@

> NOTE: pywa follows the [semver](https://semver.org/) versioning standard.

#### 4.0.0 (2026-06-09) **Latest**

WORK IN PROGRESS
#### 4.1.0 (2026-06-16) **Latest**

- [client] add `archive_templates` and `unarchive_templates` methods for template archival management
- [client] add `force_transfer` option to `set_username` method for username management
- [client] add `request_contact_info` method to request customer contact information
- [listners] add `wait_for_contact_info` method to wait for contact info requests
- [filters] add `webhook_fields` filter for filtering raw updates by fields
- [cli] improve error handling during CLI command execution
- [handlers] improve handler typing and inline documentation examples
- [api] add internal utility methods for filtering `None` values and joining fields

#### 4.0.0 (2026-06-09)

- **User Identity & BSUID Readiness**:
- Full support for BSUIDs (Business-Scoped User IDs), parent BSUIDs, usernames, and `country_code`.
- `types.User.wa_id` is now optional (users who enable usernames may no longer expose a phone-number-based WhatsApp
ID).
- `types.User.preferred_id` resolves IDs using the new `WhatsApp(user_identifier_priority=...)` priority
configuration.
- `filters.from_users(...)` now accepts BSUIDs, parent BSUIDs, WA IDs, and formatted phone numbers.
- Phone number change updates now expose BSUID-related fields (`new_user_id`, `new_parent_id`).
- **Groups & Chat-Aware Updates**:
- Full group management support: create, update, delete, fetch groups, manage invite links, handle join requests,
and add/remove participants.
- Incoming messages now expose `msg.chat` (a `Chat` object with `id` and `type`) to distinguish private chats from
groups.
- Added `filters.private`, `filters.group`, and `filters.from_groups(...)`.
- Added `GroupMessageStatusesHandler` and `wa.on_group_message_statuses(...)` for group status updates.
- Sent messages now expose `sent.chat` and support pinning/unpinning.
- **Webhooks, CLI, & Local Development**:
- Added the built-in server workflow: `pywa dev` (auto-reload), `pywa run` (production-style), and
`WhatsApp.run()` (quick scripts).
- Added `utils.start_ngrok_tunnel(...)` for easy local webhook testing.
- Support for custom webhook subscription fields with `utils.WebhookFields` via `webhook_fields`.
- Refactored webhook validation and endpoint registration to work consistently across built-in Starlette app,
FastAPI, Flask, and manual integrations.
- Listeners now warn when no timeout is provided and prevent usage with multiple Uvicorn workers.
- **Messages, Media, Callbacks, & Account Updates**:
- Added `EditedMessage`, `DeletedMessage`, `OutgoingEditedMessage`, and `OutgoingDeletedMessage` updates for
coexistence support.
- Added `AccountUpdate` and related enums for account updates.
- Media objects now store their `caption`, and media upload internals support async pending uploads with
`PendingMedia`.
- Added `ContactInfoRequestButton`, `ContactList`, and carousel message support (`send_carousel`, `reply_carousel`).
- **Business Management & Templates**:
- Retrieve shared/owned WABAs, create/verify phone numbers, and manage usernames (`set_username`, etc.).
- Added WABA settings updates, including `degrees_of_freedom_spec`.
- Enhanced template validation, lookup, parameter introspection (`param_names`), and error reporting.
- Support for `target_waba_id` in `Template.duplicate(...)` and helper states in `CreativeFeaturesSpec`.

#### 4.0.0b7 (2026-04-30)

Expand Down
7 changes: 5 additions & 2 deletions docs/source/content/client/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Its **three main responsibilities** are:
3. **Managing resources** — templates, flows, profiles, and other business-related settings.

.. tip::
:class: note
:class: tip

Pywa provides **two types of clients**:

Expand Down Expand Up @@ -43,6 +43,9 @@ Its **three main responsibilities** are:
await msg.reply("Hello!")

For optimal type checking, ensure that **all** your imports come from the same package—either ``pywa`` or ``pywa_async``.

To help you navigate the API, the client's methods are grouped below by functionality. Most of these methods return type-safe objects representing API responses, which you can then manipulate or reply to directly.

.. autoclass:: WhatsApp()
:members: __init__

Expand Down Expand Up @@ -227,7 +230,7 @@ Create, update, and manage message templates:
* - :meth:`~WhatsApp.create_template`
- Create a new template
* - :meth:`~WhatsApp.upsert_authentication_template`
- Bulk create or update authentication templates
- Create or update multiple authentication templates in a single request
* - :meth:`~WhatsApp.get_templates`
- Retrieve all templates
* - :meth:`~WhatsApp.get_template`
Expand Down
76 changes: 19 additions & 57 deletions docs/source/content/examples/demo-bots.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
🤖 Demo Bots
============

This page contains some examples of bots you can create using pywa.
Every example is a complete working bot that you can run on your own server.
This page contains complete, working examples of bots you can create using pywa.

👋 Hello Bot
--------------
Expand All @@ -12,15 +11,11 @@ This is a simple bot that welcomes the user when they send a message.
.. code-block:: python
:linenos:

import flask # pip3 install flask
from pywa import WhatsApp, types

flask_app = flask.Flask(__name__)

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand All @@ -29,8 +24,7 @@ This is a simple bot that welcomes the user when they send a message.
msg.react('👋')
msg.reply(f'Hello {msg.from_user.name}!')

# Run the server
flask_app.run()
# Run the server with `pywa dev`


📝 Echo Bot
Expand All @@ -42,15 +36,11 @@ This is a simple bot that echoes back the user's message.
.. code-block:: python
:linenos:

import flask # pip3 install flask
from pywa import WhatsApp, types

flask_app = flask.Flask(__name__)

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand All @@ -59,11 +49,9 @@ This is a simple bot that echoes back the user's message.
try:
msg.copy(to=msg.sender, reply_to_message_id=msg.message_id_to_reply)
except ValueError:
msg.reply_text("I can't echo this message")

# Run the server
flask_app.run()
msg.reply("I can't echo this message")

# Run the server with `pywa dev`


⬆️ Url Uploader Bot
Expand All @@ -74,16 +62,11 @@ This is a simple bot that uploads files from URLs.
.. code-block:: python
:linenos:

import flask # pip3 install flask
from pywa import WhatsApp, types, filters, errors
from pywa.types import Message, MessageStatus

flask_app = flask.Flask(__name__)

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand All @@ -94,10 +77,9 @@ This is a simple bot that uploads files from URLs.
# When a file fails to download/upload, the bot will reply with an error message.
@wa.on_message_status(filters.failed_with(errors.MediaDownloadError, errors.MediaUploadError))
def on_media_download_error(_: WhatsApp, status: types.MessageStatus):
status.reply_text(f"I can't download/upload this file: {status.error.details}")
status.reply(f"I can't download/upload this file: {status.error.details}")

# Run the server
flask_app.run()
# Run the server with `pywa dev`


🔢 Calculator WhatsApp Bot
Expand All @@ -115,15 +97,11 @@ Usage:
.. code-block:: python

import re
import flask # pip3 install flask
from pywa import WhatsApp, types, filters

flask_app = flask.Flask(__name__)

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand Down Expand Up @@ -153,9 +131,7 @@ Usage:
return
msg.reply(f'{a} {op} {b} = *{result}*')

# Run the server
flask_app.run()

# Run the server with `pywa dev`

🌐 Translator Bot
-----------------
Expand All @@ -166,17 +142,14 @@ A simple WhatsApp bot that translates text messages to other languages.
:linenos:

import logging
import flask # pip3 install flask
import googletrans # pip3 install googletrans==4.0.0-rc1
from pywa import WhatsApp, types, filters

flask_app = flask.Flask(__name__)
translator = googletrans.Translator()

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand All @@ -199,7 +172,7 @@ A simple WhatsApp bot that translates text messages to other languages.

@wa.on_message(filters.text)
def offer_translation(_: WhatsApp, msg: types.Message):
msg_id = msg.reply_text(
msg_id = msg.reply(
text='Choose language to translate to:',
buttons=types.SectionList(
button_title='🌐 Choose Language',
Expand Down Expand Up @@ -238,27 +211,26 @@ A simple WhatsApp bot that translates text messages to other languages.
original_text = MESSAGE_ID_TO_TEXT[sel.reply_to_message.message_id]
except KeyError: # If the bot was restarted, the message ID is no longer valid.
sel.react('❌')
sel.reply_text(
sel.reply(
text='Original message not found. Please send a new message.'
)
return
try:
translated = translator.translate(original_text, dest=lang_code)
except Exception as e:
sel.react('❌')
sel.reply_text(
sel.reply(
text='An error occurred. Please try again.'
)
logging.exception(e)
return

sel.reply_text(
sel.reply(
text=f"Translated to {translated.dest}:\n{translated.text}"
)


# Run the server
flask_app.run()
# Run the server with `pywa dev`


🖼 Random image bot
Expand All @@ -270,16 +242,11 @@ This example shows how to create a simple bot that replies with a random image f
.. code-block:: python
:linenos:

import requests
import flask
from pywa import WhatsApp, types

flask_app = flask.Flask(__name__)

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand All @@ -291,8 +258,7 @@ This example shows how to create a simple bot that replies with a random image f
buttons=types.ButtonUrl(title='Unsplash', url='https://unsplash.com')
)

# Run the server
flask_app.run()
# Run the server with `pywa dev`


📸 Remove background from image
Expand All @@ -303,16 +269,13 @@ This example shows how to create a bot that removes the background from an image
.. code-block:: python
:linenos:

import requests
import flask
import logging
import httpx
from pywa import WhatsApp, types

flask_app = flask.Flask(__name__)

wa = WhatsApp(
phone_id='your_phone_number',
token='your_token',
server=flask_app,
verify_token='xyzxyz',
)

Expand All @@ -324,7 +287,7 @@ This example shows how to create a bot that removes the background from an image
files = {'image_file': original_img}
data = {'size': 'auto'}
headers = {'X-Api-Key': REMOVEBG_API_KEY}
response = requests.post(url, files=files, data=data, headers=headers)
response = httpx.post(url, files=files, data=data, headers=headers)
response.raise_for_status()
return response.content

Expand All @@ -334,8 +297,8 @@ This example shows how to create a bot that removes the background from an image
try:
original_img = msg.image.download(in_memory=True)
image = get_removed_bg_image(original_img)
except requests.HTTPError as e:
msg.reply_text(f"A error occurred")
except httpx.HTTPError as e:
msg.reply("An error occurred")
logging.exception(e)
return
msg.reply_image(
Expand All @@ -344,5 +307,4 @@ This example shows how to create a bot that removes the background from an image
mime_type='image/png', # when sending bytes, you must specify the mime type
)

# Run the server
flask_app.run()
# Run the server with `pywa dev`
Loading
Loading