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/.stats.yml b/.stats.yml
index 43af8ad..2a2bfcd 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
-config_hash: ff624ca864b3c882a793f2af6b971127
+configured_endpoints: 37
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu%2Fzavudev-064fa598a02a9f8529186ca4292253201abbc550c98ed1d2c44dd637e4977cba.yml
+openapi_spec_hash: 76468e579e64c10eda3ce647c38482a6
+config_hash: 2c81a5597d0125c8a91faaddaadb8b9e
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/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/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/_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(
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
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",
+ )