diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e75629345..0c2ecec68 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.19.0" + ".": "0.20.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 0eb3dc018..d944dfe3d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 40 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sent%2Fsent-dm-f35f1b37bbee180194767c8644de479a1ce7166e863c4cc22f76115a7adac08f.yml -openapi_spec_hash: b62ddef06e729720dbae93de3efa0898 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sent%2Fsent-dm-896acb6fa28beb028545f1950f0b9b05c6269e9970ab47339e7722d72c62caee.yml +openapi_spec_hash: 1a0fb1b91891b2102228b3fb217e0412 config_hash: 405ade725d72d542c60de821a127411b diff --git a/CHANGELOG.md b/CHANGELOG.md index 57967fc4e..327339bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.20.0 (2026-04-21) + +Full Changelog: [v0.19.0...v0.20.0](https://github.com/sentdm/sent-dm-python/compare/v0.19.0...v0.20.0) + +### Features + +* **api:** api update ([3525281](https://github.com/sentdm/sent-dm-python/commit/3525281751a4ae6b2e310ed9cedd6b75c13d6875)) + ## 0.19.0 (2026-04-20) Full Changelog: [v0.18.3...v0.19.0](https://github.com/sentdm/sent-dm-python/compare/v0.18.3...v0.19.0) diff --git a/pyproject.toml b/pyproject.toml index 84fa3bdff..e83ff97af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sentdm" -version = "0.19.0" +version = "0.20.0" description = "The official Python library for the Sent API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/sent_dm/_version.py b/src/sent_dm/_version.py index 9643a1ab5..0595d4f76 100644 --- a/src/sent_dm/_version.py +++ b/src/sent_dm/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "sent_dm" -__version__ = "0.19.0" # x-release-please-version +__version__ = "0.20.0" # x-release-please-version diff --git a/src/sent_dm/resources/contacts.py b/src/sent_dm/resources/contacts.py index 62d75d209..59d0961e1 100644 --- a/src/sent_dm/resources/contacts.py +++ b/src/sent_dm/resources/contacts.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional import httpx @@ -144,6 +144,7 @@ def update( self, id: str, *, + channel_consent: Optional[Dict[str, str]] | Omit = omit, default_channel: Optional[str] | Omit = omit, opt_out: Optional[bool] | Omit = omit, sandbox: bool | Omit = omit, @@ -162,6 +163,13 @@ def update( cannot be updated. Args: + channel_consent: Consent status by channel. Keys: "sms", "whatsapp". Values: "opted_in", + "opted_out". All entries must have the same status — mixed values (e.g., sms: + opted_out + whatsapp: opted_in) are rejected with 400. The provided status is + applied to ALL channels regardless of which keys are specified, because consent + is global across channels. When provided, takes precedence over the opt_out + field. + default_channel: Default messaging channel: "sms" or "whatsapp" opt_out: Whether the contact has opted out of messaging @@ -192,6 +200,7 @@ def update( path_template("/v3/contacts/{id}", id=id), body=maybe_transform( { + "channel_consent": channel_consent, "default_channel": default_channel, "opt_out": opt_out, "sandbox": sandbox, @@ -429,6 +438,7 @@ async def update( self, id: str, *, + channel_consent: Optional[Dict[str, str]] | Omit = omit, default_channel: Optional[str] | Omit = omit, opt_out: Optional[bool] | Omit = omit, sandbox: bool | Omit = omit, @@ -447,6 +457,13 @@ async def update( cannot be updated. Args: + channel_consent: Consent status by channel. Keys: "sms", "whatsapp". Values: "opted_in", + "opted_out". All entries must have the same status — mixed values (e.g., sms: + opted_out + whatsapp: opted_in) are rejected with 400. The provided status is + applied to ALL channels regardless of which keys are specified, because consent + is global across channels. When provided, takes precedence over the opt_out + field. + default_channel: Default messaging channel: "sms" or "whatsapp" opt_out: Whether the contact has opted out of messaging @@ -477,6 +494,7 @@ async def update( path_template("/v3/contacts/{id}", id=id), body=await async_maybe_transform( { + "channel_consent": channel_consent, "default_channel": default_channel, "opt_out": opt_out, "sandbox": sandbox, diff --git a/src/sent_dm/types/contact_response.py b/src/sent_dm/types/contact_response.py index dd05af113..dd7d9c162 100644 --- a/src/sent_dm/types/contact_response.py +++ b/src/sent_dm/types/contact_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from .._models import BaseModel @@ -20,6 +20,14 @@ class ContactResponse(BaseModel): available_channels: Optional[str] = None """Comma-separated list of available messaging channels (e.g., "sms,whatsapp")""" + channel_consent: Optional[Dict[str, str]] = None + """Consent status by channel. + + Keys: "sms", "whatsapp". Values: "opted_in", "opted_out". All channels will have + the same status because consent is global across channels. A STOP on any channel + opts out of all channels; a START opts in to all. + """ + country_code: Optional[str] = None """Country calling code (e.g., 1 for US/Canada)""" diff --git a/src/sent_dm/types/contact_update_params.py b/src/sent_dm/types/contact_update_params.py index 14dcc4e23..b4e9bea60 100644 --- a/src/sent_dm/types/contact_update_params.py +++ b/src/sent_dm/types/contact_update_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Dict, Optional from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo @@ -11,6 +11,16 @@ class ContactUpdateParams(TypedDict, total=False): + channel_consent: Optional[Dict[str, str]] + """Consent status by channel. + + Keys: "sms", "whatsapp". Values: "opted_in", "opted_out". All entries must have + the same status — mixed values (e.g., sms: opted_out + whatsapp: opted_in) are + rejected with 400. The provided status is applied to ALL channels regardless of + which keys are specified, because consent is global across channels. When + provided, takes precedence over the opt_out field. + """ + default_channel: Optional[str] """Default messaging channel: "sms" or "whatsapp" """ diff --git a/src/sent_dm/types/message_retrieve_status_response.py b/src/sent_dm/types/message_retrieve_status_response.py index 59589e672..7469f6a56 100644 --- a/src/sent_dm/types/message_retrieve_status_response.py +++ b/src/sent_dm/types/message_retrieve_status_response.py @@ -56,6 +56,8 @@ class Data(BaseModel): customer_id: Optional[str] = None + direction: Optional[str] = None + events: Optional[List[DataEvent]] = None message_body: Optional[DataMessageBody] = None diff --git a/tests/api_resources/test_contacts.py b/tests/api_resources/test_contacts.py index f827ce097..7cc3bdf68 100644 --- a/tests/api_resources/test_contacts.py +++ b/tests/api_resources/test_contacts.py @@ -123,6 +123,7 @@ def test_method_update(self, client: Sent) -> None: def test_method_update_with_all_params(self, client: Sent) -> None: contact = client.contacts.update( id="6ba7b810-9dad-11d1-80b4-00c04fd430c8", + channel_consent={"foo": "string"}, default_channel="whatsapp", opt_out=False, sandbox=False, @@ -380,6 +381,7 @@ async def test_method_update(self, async_client: AsyncSent) -> None: async def test_method_update_with_all_params(self, async_client: AsyncSent) -> None: contact = await async_client.contacts.update( id="6ba7b810-9dad-11d1-80b4-00c04fd430c8", + channel_consent={"foo": "string"}, default_channel="whatsapp", opt_out=False, sandbox=False,