From 08b5b97210d16560efde81ba335e09a0c3daebf9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 04:06:56 +0000 Subject: [PATCH 1/5] chore(internal): add missing files argument to base client --- src/zavudev/_base_client.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/zavudev/_base_client.py b/src/zavudev/_base_client.py index 58bfe10..4e4dccb 100644 --- a/src/zavudev/_base_client.py +++ b/src/zavudev/_base_client.py @@ -1247,9 +1247,12 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + ) return self.request(cast_to, opts) def put( @@ -1767,9 +1770,12 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + ) return await self.request(cast_to, opts) async def put( From d33336f7de3862a588decdd3d7ef15c9fadd90d2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 13:55:16 +0000 Subject: [PATCH 2/5] feat(api): api update --- .stats.yml | 4 ++-- src/zavudev/types/webhook_event.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 43af8ad..25b6e17 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 36 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-578694e0208b4129909ee690abbebfb5ac9e5738768a009c9bfb7aa638a3c984.yml -openapi_spec_hash: 842323c611aa7ea50f039e852d1594cf +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-28a2297c8057f54d2f62f2f69c43747da67e6dfee85dc2d0e5f51ef0cb2c8aeb.yml +openapi_spec_hash: cad7a02ff28c8173709057a2cfccf36a config_hash: ff624ca864b3c882a793f2af6b971127 diff --git a/src/zavudev/types/webhook_event.py b/src/zavudev/types/webhook_event.py index 5d606e6..2fc8f61 100644 --- a/src/zavudev/types/webhook_event.py +++ b/src/zavudev/types/webhook_event.py @@ -11,6 +11,7 @@ "message.failed", "message.inbound", "message.unsupported", + "message.reaction", "conversation.new", "template.status_changed", ] From f00a45c4fced1fa32e133ed21b55e23bde611522 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 16:43:34 +0000 Subject: [PATCH 3/5] feat(api): api update --- .stats.yml | 4 ++-- src/zavudev/types/webhook_event.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 25b6e17..3e98444 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 36 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-28a2297c8057f54d2f62f2f69c43747da67e6dfee85dc2d0e5f51ef0cb2c8aeb.yml -openapi_spec_hash: cad7a02ff28c8173709057a2cfccf36a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-064fa598a02a9f8529186ca4292253201abbc550c98ed1d2c44dd637e4977cba.yml +openapi_spec_hash: 76468e579e64c10eda3ce647c38482a6 config_hash: ff624ca864b3c882a793f2af6b971127 diff --git a/src/zavudev/types/webhook_event.py b/src/zavudev/types/webhook_event.py index 2fc8f61..5d606e6 100644 --- a/src/zavudev/types/webhook_event.py +++ b/src/zavudev/types/webhook_event.py @@ -11,7 +11,6 @@ "message.failed", "message.inbound", "message.unsupported", - "message.reaction", "conversation.new", "template.status_changed", ] From 32c5e78d0a44385429346db509d4ea5e345afb76 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 22:18:44 +0000 Subject: [PATCH 4/5] feat(api): add submit template --- .stats.yml | 4 +- api.md | 1 + src/zavudev/resources/templates.py | 112 ++++++++++++++++++- src/zavudev/types/__init__.py | 1 + src/zavudev/types/template_submit_params.py | 18 +++ tests/api_resources/test_templates.py | 116 +++++++++++++++++++- 6 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 src/zavudev/types/template_submit_params.py diff --git a/.stats.yml b/.stats.yml index 3e98444..2a2bfcd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 36 +configured_endpoints: 37 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-064fa598a02a9f8529186ca4292253201abbc550c98ed1d2c44dd637e4977cba.yml openapi_spec_hash: 76468e579e64c10eda3ce647c38482a6 -config_hash: ff624ca864b3c882a793f2af6b971127 +config_hash: 2c81a5597d0125c8a91faaddaadb8b9e diff --git a/api.md b/api.md index 1cf9fc1..632c0c8 100644 --- a/api.md +++ b/api.md @@ -34,6 +34,7 @@ Methods: - client.templates.retrieve(template_id) -> Template - client.templates.list(\*\*params) -> SyncCursor[Template] - client.templates.delete(template_id) -> None +- client.templates.submit(template_id, \*\*params) -> Template # Senders diff --git a/src/zavudev/resources/templates.py b/src/zavudev/resources/templates.py index b315d3c..1e822af 100644 --- a/src/zavudev/resources/templates.py +++ b/src/zavudev/resources/templates.py @@ -4,7 +4,7 @@ import httpx -from ..types import WhatsappCategory, template_list_params, template_create_params +from ..types import WhatsappCategory, template_list_params, template_create_params, template_submit_params from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -202,6 +202,55 @@ def delete( cast_to=NoneType, ) + def submit( + self, + template_id: str, + *, + sender_id: str, + category: WhatsappCategory | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Template: + """Submit a WhatsApp template to Meta for approval. + + The template must be in draft + status and associated with a sender that has a WhatsApp Business Account + configured. + + Args: + sender_id: The sender ID with the WhatsApp Business Account to submit the template to. + + category: Template category. If not provided, uses the category set on the template. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not template_id: + raise ValueError(f"Expected a non-empty value for `template_id` but received {template_id!r}") + return self._post( + f"/v1/templates/{template_id}/submit", + body=maybe_transform( + { + "sender_id": sender_id, + "category": category, + }, + template_submit_params.TemplateSubmitParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + class AsyncTemplatesResource(AsyncAPIResource): @cached_property @@ -382,6 +431,55 @@ async def delete( cast_to=NoneType, ) + async def submit( + self, + template_id: str, + *, + sender_id: str, + category: WhatsappCategory | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Template: + """Submit a WhatsApp template to Meta for approval. + + The template must be in draft + status and associated with a sender that has a WhatsApp Business Account + configured. + + Args: + sender_id: The sender ID with the WhatsApp Business Account to submit the template to. + + category: Template category. If not provided, uses the category set on the template. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not template_id: + raise ValueError(f"Expected a non-empty value for `template_id` but received {template_id!r}") + return await self._post( + f"/v1/templates/{template_id}/submit", + body=await async_maybe_transform( + { + "sender_id": sender_id, + "category": category, + }, + template_submit_params.TemplateSubmitParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + class TemplatesResourceWithRawResponse: def __init__(self, templates: TemplatesResource) -> None: @@ -399,6 +497,9 @@ def __init__(self, templates: TemplatesResource) -> None: self.delete = to_raw_response_wrapper( templates.delete, ) + self.submit = to_raw_response_wrapper( + templates.submit, + ) class AsyncTemplatesResourceWithRawResponse: @@ -417,6 +518,9 @@ def __init__(self, templates: AsyncTemplatesResource) -> None: self.delete = async_to_raw_response_wrapper( templates.delete, ) + self.submit = async_to_raw_response_wrapper( + templates.submit, + ) class TemplatesResourceWithStreamingResponse: @@ -435,6 +539,9 @@ def __init__(self, templates: TemplatesResource) -> None: self.delete = to_streamed_response_wrapper( templates.delete, ) + self.submit = to_streamed_response_wrapper( + templates.submit, + ) class AsyncTemplatesResourceWithStreamingResponse: @@ -453,3 +560,6 @@ def __init__(self, templates: AsyncTemplatesResource) -> None: self.delete = async_to_streamed_response_wrapper( templates.delete, ) + self.submit = async_to_streamed_response_wrapper( + templates.submit, + ) diff --git a/src/zavudev/types/__init__.py b/src/zavudev/types/__init__.py index 4e4ca73..619a18a 100644 --- a/src/zavudev/types/__init__.py +++ b/src/zavudev/types/__init__.py @@ -40,6 +40,7 @@ from .available_phone_number import AvailablePhoneNumber as AvailablePhoneNumber from .broadcast_message_type import BroadcastMessageType as BroadcastMessageType from .template_create_params import TemplateCreateParams as TemplateCreateParams +from .template_submit_params import TemplateSubmitParams as TemplateSubmitParams from .broadcast_content_param import BroadcastContentParam as BroadcastContentParam from .broadcast_create_params import BroadcastCreateParams as BroadcastCreateParams from .broadcast_send_response import BroadcastSendResponse as BroadcastSendResponse diff --git a/src/zavudev/types/template_submit_params.py b/src/zavudev/types/template_submit_params.py new file mode 100644 index 0000000..f4f606d --- /dev/null +++ b/src/zavudev/types/template_submit_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo +from .whatsapp_category import WhatsappCategory + +__all__ = ["TemplateSubmitParams"] + + +class TemplateSubmitParams(TypedDict, total=False): + sender_id: Required[Annotated[str, PropertyInfo(alias="senderId")]] + """The sender ID with the WhatsApp Business Account to submit the template to.""" + + category: WhatsappCategory + """Template category. If not provided, uses the category set on the template.""" diff --git a/tests/api_resources/test_templates.py b/tests/api_resources/test_templates.py index fea3c7b..a9e2aad 100644 --- a/tests/api_resources/test_templates.py +++ b/tests/api_resources/test_templates.py @@ -9,7 +9,9 @@ from zavudev import Zavudev, AsyncZavudev from tests.utils import assert_matches_type -from zavudev.types import Template +from zavudev.types import ( + Template, +) from zavudev.pagination import SyncCursor, AsyncCursor base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -191,6 +193,62 @@ def test_path_params_delete(self, client: Zavudev) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_submit(self, client: Zavudev) -> None: + template = client.templates.submit( + template_id="templateId", + sender_id="sender_abc123", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_submit_with_all_params(self, client: Zavudev) -> None: + template = client.templates.submit( + template_id="templateId", + sender_id="sender_abc123", + category="UTILITY", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_submit(self, client: Zavudev) -> None: + response = client.templates.with_raw_response.submit( + template_id="templateId", + sender_id="sender_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_submit(self, client: Zavudev) -> None: + with client.templates.with_streaming_response.submit( + template_id="templateId", + sender_id="sender_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_submit(self, client: Zavudev) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `template_id` but received ''"): + client.templates.with_raw_response.submit( + template_id="", + sender_id="sender_abc123", + ) + class TestAsyncTemplates: parametrize = pytest.mark.parametrize( @@ -369,3 +427,59 @@ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None: await async_client.templates.with_raw_response.delete( "", ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_submit(self, async_client: AsyncZavudev) -> None: + template = await async_client.templates.submit( + template_id="templateId", + sender_id="sender_abc123", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_submit_with_all_params(self, async_client: AsyncZavudev) -> None: + template = await async_client.templates.submit( + template_id="templateId", + sender_id="sender_abc123", + category="UTILITY", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_submit(self, async_client: AsyncZavudev) -> None: + response = await async_client.templates.with_raw_response.submit( + template_id="templateId", + sender_id="sender_abc123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_submit(self, async_client: AsyncZavudev) -> None: + async with async_client.templates.with_streaming_response.submit( + template_id="templateId", + sender_id="sender_abc123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_submit(self, async_client: AsyncZavudev) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `template_id` but received ''"): + await async_client.templates.with_raw_response.submit( + template_id="", + sender_id="sender_abc123", + ) From 7ecc59bf325bbbe2ffcf9f84afa21b6104f4b8fc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 22:19:00 +0000 Subject: [PATCH 5/5] release: 0.12.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/zavudev/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f7014c3..a713055 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.11.0" + ".": "0.12.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7726314..ba04902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.12.0 (2025-12-16) + +Full Changelog: [v0.11.0...v0.12.0](https://github.com/zavudev/sdk-python/compare/v0.11.0...v0.12.0) + +### Features + +* **api:** add submit template ([32c5e78](https://github.com/zavudev/sdk-python/commit/32c5e78d0a44385429346db509d4ea5e345afb76)) +* **api:** api update ([f00a45c](https://github.com/zavudev/sdk-python/commit/f00a45c4fced1fa32e133ed21b55e23bde611522)) +* **api:** api update ([d33336f](https://github.com/zavudev/sdk-python/commit/d33336f7de3862a588decdd3d7ef15c9fadd90d2)) + + +### Chores + +* **internal:** add missing files argument to base client ([08b5b97](https://github.com/zavudev/sdk-python/commit/08b5b97210d16560efde81ba335e09a0c3daebf9)) + ## 0.11.0 (2025-12-16) Full Changelog: [v0.10.1...v0.11.0](https://github.com/zavudev/sdk-python/compare/v0.10.1...v0.11.0) diff --git a/pyproject.toml b/pyproject.toml index efb74b1..2524838 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "zavudev" -version = "0.11.0" +version = "0.12.0" description = "The official Python library for the zavudev API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/zavudev/_version.py b/src/zavudev/_version.py index dd3c25c..06e8856 100644 --- a/src/zavudev/_version.py +++ b/src/zavudev/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "zavudev" -__version__ = "0.11.0" # x-release-please-version +__version__ = "0.12.0" # x-release-please-version