Skip to content

Commit d099323

Browse files
committed
[version] new version 3.0.0
1 parent 7097136 commit d099323

3 files changed

Lines changed: 74 additions & 174 deletions

File tree

CHANGELOG.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,42 @@
33

44
> NOTE: pywa follows the [semver](https://semver.org/) versioning standard.
55
6+
7+
#### 3.0.0 (2025-08-22) **Latest**
8+
9+
This release is a major update with significant improvements, new features, and some breaking changes.
10+
Please review the migration guide before upgrading.
11+
12+
- [templates] Complete refactor of template support: creation, retrieval, sending, deleting, and status updates
13+
- [calls] Added full support for calls: making and receiving calls, call status updates, and call settings management
14+
- [user_preferences] Added full support for user preferences
15+
- [listeners] Unified listener handling with better safety checks
16+
- [system] Restructured system updates into `PhoneNumberChange` and `IdentityChange`
17+
- [client]
18+
- `upload_media` now returns a `Media` object
19+
- `mark_message_as_read` and other `update...` methods now return `SuccessResult` instead of `bool`
20+
- New methods: `delete_media`, `get_business_phone_number_settings`, `update_business_phone_number_settings`, `update_display_name`, `get_business_account`, `deregister_phone_number`
21+
- Support for mm-lite-api when sending templates
22+
- Support for getting and setting `StorageConfiguration`
23+
- [callback]
24+
- Added `is_quick_reply` to `CallbackButton`
25+
- Stricter dataclass validation (no `kw_only`)
26+
- [updates]
27+
- All user updates now include `waba_id`
28+
- `Message` include new `referral` field
29+
- [types]
30+
- Added `is_on_biz_app` to `BusinessPhoneNumber`
31+
- `SuccessResult` introduced for extensibility
32+
- [security] Fixed XSS vulnerability
33+
- [api] `httpx.RequestError` now suggests providing a custom `httpx.Client`
34+
- [errors] Error messages are now more descriptive
35+
- [utils]
36+
- Enum values handled case-sensitively
37+
- Return `FlowRequestDecryptedMedia` object instead of raw tuple
38+
- New `APIObject` helper for dataclasses
39+
- [deprecations] All previously deprecated attributes and types have been removed
40+
- [docs] New pywa logo 🚀
41+
642
#### 3.0.0-rc.3 (2025-08-06)
743

844
- [templates] `params` is now can be called on class level
@@ -52,7 +88,7 @@
5288
- [deprecations] removed attrs and types marked as deprecated
5389

5490

55-
### 2.11.0 (2025-06-17) **Latest**
91+
### 2.11.0 (2025-06-17)
5692

5793
- [flows] adding support for `ImageCarousel`
5894
- [flows] adding `with_params` to `FlowPreview` to configure the interactive web preview

MIGRATION.md

Lines changed: 36 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,47 @@
1616

1717
### New features
1818

19-
- [templates] Refactored and improved templates support. The templates are now flexible, reusable, extensible, easier to use and more powerful.
20-
- [calls] Added full support for calls. You can now make and receive calls, manage calls, handle call events, configure call settings, and more.
21-
- [user_preferences] Added full support for user preferences. You can now listen to user marketing preferences - if the user has opted in or out of marketing messages, and act accordingly.
22-
- [server] Continued handling if listener is not using the update. If a listener does not use the update (not filtered or not cancelled), the server will continue to handle the update and pass it to the handlers.
23-
- [system] Moved `system` messages to `PhoneNumberChange` and `IdentityChange` updates. The `system` messages are now handled as separate updates, allowing you to focus on real user messages and events.
24-
- [client] Forced keyword-only for context args in `send_message`, `send_image`, and other `send_...` methods. This change improves readability and consistency of the API.
25-
- [types] Returned `SuccessResult` instead of `bool` to allow future extension with other attributes. The `SuccessResult` object acts as boolean, but can also contain additional information about the success of the operation.
26-
- [client] `upload_media` returns `Media` object. The `Media` object contains the media ID so you can use it later to send the media or perform other operations.
27-
- [client] Added `update_display_name` method to update the WhatsApp display name. This method allows you to change the display name of your WhatsApp account.
28-
- [handlers] Added `on_completion` decorator to flow request callback wrapper. This decorator allows you to register a callback that will be called when the flow request is completed.
29-
- [errors] Show more descriptive error messages. The error messages now provide more context and information about the error, making it easier to debug and fix issues.
30-
- [base_update] Added `waba_id` for all user updates. The `waba_id` is the WhatsApp Business Account ID that received the update, allowing you to identify the account that the update belongs to.
31-
- [message] Added `referral` field. When a customer clicks an ad that redirects to WhatsApp and starts a chat, the `referral` field contains information about the ad that was clicked.
32-
- [client] Added `delete_media` method. This method allows you to delete media files from WhatsApp servers.
19+
- [templates] Fully redesigned template system — now more flexible, reusable, and powerful.
20+
- [calls] Full support for calls: make/receive calls, manage call state, handle events, and configure settings.
21+
- [user_preferences] Full support for user marketing preferences (opt-in/out).
22+
- [listeners] Listeners can now wait for non-user updates (e.g., template approval, account events).
23+
- [system] System messages are now separate updates (`PhoneNumberChange`, `IdentityChange`).
24+
- [client]
25+
- All `send_...` methods enforce keyword-only context args for clarity and consistency.
26+
- `upload_media` now returns a `Media` object (with media ID).
27+
- Added `delete_media` and `update_display_name` methods.
28+
- [handlers] Added `on_completion` decorator for flow request callbacks.
29+
- [types] `SuccessResult` replaces `bool`, extendable with extra attributes.
30+
- [base_update] All user updates now include `waba_id` (WhatsApp Business Account ID).
31+
- [message] New `referral` field (e.g., when users click ads leading to WhatsApp).
32+
- [errors] More descriptive error messages.
3333

3434

3535
### Breaking changes
3636

37-
- [templates] The templates system has been completely redesigned. The old templates are no longer supported, and you need to update your code to use the new templates system (create and send)
38-
- [listeners] Listeners can now be used to handle any update, not just user updates. This means that you can now use listeners to wait for template to be approved and to other account related upadtes. If you are using `wa.listen` directly (and not using one of the shortcuts like `wait_for_reply`, `wait_for_click` etc.), you will need to update your code to use the new listeners system.
39-
- [server] The server now continues to handle updates even if the listener does not use the update. This means that if a listener does not filter or cancel the update, the server will pass it to the handlers (unless you call `update.stop_handling()` inside the listener filters/cancelers).
40-
- [client] The `upload_media` method now returns a `Media` object instead of a string (media ID). The `Media` object contains the media ID and allows you to perform actions on the media, such as downloading or deleting it. If you using the media ID directly to send media - you don't need to change anything, just use the `Media` object instead of the string. if you stored the media ID in a database or somewhere else, you will need to update your code to use the `Media.id` attribute instead of the string media ID.
41-
- [client] The `send_message`, `send_image`, and other `send_...` methods now require keyword-only arguments for context. This change improves readability and consistency of the API. This context arguments are `reply_to_message_id`, `sender` etc. Most of users don't need to change anything, but if you are using positional arguments for these context args (they are located at the end of the method signature, so the chance for breaking is low), you will need to update your code to use keyword arguments.
42-
- [types] The `SuccessResult` object is now returned instead of a boolean value. This change allows for future extension of the `SuccessResult` object with additional attributes, such as error messages or additional information about the success of the operation. If you are using the return value of methods like `mark_as_read`, `indicate_typing`, etc., if you just checking if the operation was successful, you can continue to use it as a boolean. but if you storing the result in a database or forcing the result to be a boolean, you will need to update your code to use the `SuccessResult.success` attribute instead of the boolean value.
43-
- [system] The `system` messages are now handled as separate updates, and the `PhoneNumberChange` and `IdentityChange` updates are used instead. This change allows you to focus on real user messages and events, and handle system messages separately. If you were accessing the `system` attribute of the `Message` object, you will need to update your code to start listening to the `PhoneNumberChange` and `IdentityChange` updates instead.
44-
- [utils] The `FlowRequestDecryptedMedia` object is now returned instead of a tuple `(media_id, filename, data)`. This change allows you to access the media ID, filename, and data in a more structured way. If you were using the tuple to access the media ID, filename, and data, you will need to update your code to use the `FlowRequestDecryptedMedia` object instead.
37+
- [templates] Old template system removed. Update code to the new template APIs.
38+
- [listeners]
39+
- Listeners apply to **all update types**, not just user messages.
40+
- Legacy `to` parameter type updated.
41+
- If using `wa.listen` directly, update to the new listener API (shortcuts like `wait_for_reply`, `wait_for_click` are unchanged).
42+
- [server] Updates now continue through the pipeline unless a listener explicitly cancels with `update.stop_handling()`.
43+
- [client]
44+
- `upload_media` returns a `Media` object instead of a raw media ID string.
45+
- If you only pass the `upload_media` result to `send_*` methods, no changes needed.
46+
- If you store media IDs, update code to use `Media.id`.
47+
- `send_message`, `send_image`, and other `send_...` methods now require keyword-only context args (`reply_to_message_id`, `sender`, etc.).
48+
- Most users unaffected, but if you used positional args for these fields, switch to keywords.
49+
- [types] Methods like `mark_as_read`, `indicate_typing`, etc. now return `SuccessResult` instead of `bool`.
50+
- Still usable in boolean checks (`if result:`).
51+
- If you persist results or explicitly cast to `bool`, switch to `result.success`.
52+
- [system] `system` messages removed from `Message`. Listen to `PhoneNumberChange` and `IdentityChange` updates instead.
53+
- [utils] `FlowRequestDecryptedMedia` replaces raw `(media_id, filename, data)` tuple. Update code to use object attributes.
54+
55+
---
4556

4657
### Migration steps
4758

48-
1. If you are using the templates system, you need to update your code to use the new templates system. You can find the documentation for the new templates system [here](https://pywa.readthedocs.io/en/latest/content/templates/overview.html).
59+
1. Update all template usage to the new system ([docs here](https://pywa.readthedocs.io/en/latest/content/templates/overview.html)).
4960

5061
```python
5162
########################## OLD CODE ##########################
@@ -139,57 +150,7 @@ wa.send_template(
139150
)
140151
```
141152

142-
2. If you are using the listeners system, you need to update your code to use the new listeners system. You can find the documentation for the new listeners system [here](https://pywa.readthedocs.io/en/latest/content/listeners/overview.html).
143-
144-
```python
145-
########################## OLD CODE ##########################
146-
147-
from pywa import WhatsApp, types, filters
148-
149-
wa = WhatsApp(...)
150-
151-
@wa.on_message(filters.text)
152-
def on_first_message(_: WhatsApp, msg: types.Message):
153-
age = msg.reply(f"Hi {msg.from_user.name}! What's your age?").wait_for_reply(
154-
# In the old code, if the update is not filtered, it will NOT be passed to the handlers (another_text_handler)
155-
filters=filters.text & filters.new(lambda _, m: m.text.isdigit())
156-
)
157-
msg.reply(f"You are {age.text} years old!")
158-
159-
160-
@wa.on_message(filters.text)
161-
def another_text_handler(_: WhatsApp, msg: types.Message):
162-
update = wa.listen(to="123456789", ...) # listen for updates from the user
163-
...
164-
165-
########################## NEW CODE ##########################
166-
167-
from pywa import WhatsApp, types, filters, listeners
168-
169-
wa = WhatsApp(...)
170-
171-
def filter_age(_: WhatsApp, msg: types.Message) -> bool:
172-
if msg.text.isdigit():
173-
return True # the update is filtered
174-
msg.reply("Please enter a valid age (number).")
175-
msg.stop_handling() # stop handling this update (will not be passed to the handlers)
176-
177-
178-
@wa.on_message(filters.text)
179-
def on_first_message(_: WhatsApp, msg: types.Message):
180-
age = msg.reply(f"Hi {msg.from_user.name}! What's your age?").wait_for_reply(
181-
# In the new code, if the update is not filtered, it will be passed to the handlers (another_text_handler), so we need to stop handling it if the age is not valid
182-
filters=filters.text & filters.new(filter_age)
183-
)
184-
msg.reply(f"You are {age.text} years old!")
185-
186-
@wa.on_message(filters.text)
187-
def another_text_handler(_: WhatsApp, msg: types.Message):
188-
update = wa.listen(to=listeners.UserUpdateListenerIdentifier(sender="123456789", recipient="your_phone_id"), ...) # listen for updates from the user
189-
...
190-
```
191-
192-
3. If you are using the `upload_media` method, you need to update your code to use the `Media` object instead of a string (media ID):
153+
2. If you are using the `upload_media` method, you need to update your code to use the `Media` object instead of a string (media ID):
193154

194155
```python
195156
########################## OLD CODE ##########################
@@ -218,73 +179,7 @@ cursor.execute("CREATE TABLE IF NOT EXISTS media (id INTEGER PRIMARY KEY AUTOINC
218179
cursor.execute("INSERT INTO media (media_id) VALUES (?)", (media.id,))
219180
```
220181

221-
4. If you are using the `send_message`, `send_image`, or other `send_...` methods, you need to update your code to use keyword-only arguments for context:
222-
223-
```python
224-
########################## OLD CODE ##########################
225-
226-
from pywa import WhatsApp, types
227-
228-
wa = WhatsApp(...)
229-
230-
wa.send_message(
231-
"97234567890",
232-
"Hello, World!",
233-
"header text",
234-
"footer text",
235-
[types.Button(title="x", callback_data="y")],
236-
True, # preview_url
237-
"message_id", # reply_to_message_id
238-
"phone_id", # sender
239-
)
240-
241-
########################## NEW CODE ##########################
242-
243-
from pywa import WhatsApp, types
244-
245-
wa = WhatsApp(...)
246-
247-
wa.send_message(
248-
"97234567890",
249-
"Hello, World!",
250-
"header text",
251-
"footer text",
252-
[types.Button(title="x", callback_data="y")],
253-
preview_url=True, # kw only argument
254-
reply_to_message_id="message_id", # kw only argument
255-
sender="phone_id", # kw only argument
256-
)
257-
```
258-
259-
5. If you are using the `SuccessResult` object, you need to update your code to use the `SuccessResult.success` attribute instead of a boolean value:
260-
261-
```python
262-
########################## OLD CODE ##########################
263-
264-
from pywa import WhatsApp, types
265-
266-
wa = WhatsApp(...)
267-
268-
result = wa.mark_as_read("97234567890", "message_id")
269-
270-
# running sql query to store result
271-
cursor.execute("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, message_id VARCHAR UNIQUE NOT NULL, is_read BOOLEAN NOT NULL)")
272-
cursor.execute("INSERT INTO messages (message_id, is_read) VALUES (?, ?)", ("msg_id", result))
273-
274-
########################## NEW CODE ##########################
275-
276-
from pywa import WhatsApp, types
277-
278-
wa = WhatsApp(...)
279-
280-
result = wa.mark_as_read("97234567890", "message_id")
281-
282-
# running sql query to store result.success
283-
cursor.execute("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, message_id VARCHAR UNIQUE NOT NULL, is_read BOOLEAN NOT NULL)")
284-
cursor.execute("INSERT INTO messages (message_id, is_read) VALUES (?, ?)", ("msg_id", result.success))
285-
```
286-
287-
6. If you are using the `system` messages, you need to update your code to start listening to the `PhoneNumberChange` and `IdentityChange` updates instead:
182+
3. If you are using the `system` messages, you need to update your code to start listening to the `PhoneNumberChange` and `IdentityChange` updates instead:
288183

289184
```python
290185
########################## OLD CODE ##########################
@@ -317,37 +212,6 @@ def on_identity_change(_: WhatsApp, update: types.IdentityChange):
317212
repository.log_out_user(wa_id=update.sender) # secure the user
318213
```
319214

320-
7. If you are using the `FlowRequestDecryptedMedia` object, you need to update your code to use the `FlowRequestDecryptedMedia` object instead of a tuple:
321-
322-
```python
323-
########################## OLD CODE ##########################
324-
325-
from pywa import WhatsApp, types
326-
327-
wa = WhatsApp(...)
328-
329-
@wa.on_flow_request("/my-flow-endpoint")
330-
def my_flow_endpoint(_: WhatsApp, req: types.FlowRequest):
331-
media_id, filename, decrypted_data = req.decrypt_media(key="driver_license", index=0)
332-
with open(filename, "wb") as file:
333-
file.write(decrypted_data)
334-
return req.respond(...)
335-
336-
########################## NEW CODE ##########################
337-
338-
from pywa import WhatsApp, types
339-
340-
wa = WhatsApp(...)
341-
342-
@wa.on_flow_request("/my-flow-endpoint")
343-
def my_flow_endpoint(_: WhatsApp, req: types.FlowRequest):
344-
decrypted_media = req.decrypt_media(key="driver_license", index=0)
345-
with open(decrypted_media.filename, "wb") as file:
346-
file.write(decrypted_media.data)
347-
return req.respond(...)
348-
```
349-
350-
351215
## Migration from 1.x to 2.x
352216

353217

pywa/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
from pywa.client import WhatsApp
1010
from pywa.utils import Version
1111

12-
__version__ = "3.0.0-rc.3"
12+
__version__ = "3.0.0"
1313
__author__ = "David Lev"
1414
__license__ = "MIT"

0 commit comments

Comments
 (0)