diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index c996453..26b1ce2 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.49.1"
+ ".": "0.50.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 0b60fcc..d9f73e8 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 138
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-e4987140138afb7287e25f31dc8cff94c6b11975e460e0c19d54ec457d009a1b.yml
-openapi_spec_hash: d20e1b8d0a9a7a65dc8a21e3a151111e
-config_hash: 7fca08d9dae18513f5d7451cea11e34c
+configured_endpoints: 183
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/zavu/zavudev-fc96dae99e9649b4cfb99002b7dfee6480114b8dda4ff136541533c9c9069118.yml
+openapi_spec_hash: 3c2c8da8c73cdd7e656b7f2c06cc01b4
+config_hash: 261e1b852ca7f8364e4a283a3edd350a
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 589ae05..31d33dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## 0.50.0 (2026-08-31)
+
+Full Changelog: [v0.49.1...v0.50.0](https://github.com/zavudev/sdk-python/compare/v0.49.1...v0.50.0)
+
+### Features
+
+* **api:** api update ([74ba1bd](https://github.com/zavudev/sdk-python/commit/74ba1bd0d02ccd482128b278bedf41fe0547de63))
+* **api:** api update ([ac86298](https://github.com/zavudev/sdk-python/commit/ac8629802c7447f0f37b89463067341f984a9807))
+* **api:** api update ([cf02598](https://github.com/zavudev/sdk-python/commit/cf02598e58f80c78e157d9d688815752c8c0214d))
+* **api:** api update ([4c99b11](https://github.com/zavudev/sdk-python/commit/4c99b11a34de2da4247d07b9f45d6194ea61bbe2))
+* **api:** api update ([9c57b04](https://github.com/zavudev/sdk-python/commit/9c57b04dee1ece5ec215a3f0db297f5864426437))
+* **api:** api update ([2da63e1](https://github.com/zavudev/sdk-python/commit/2da63e13774e3aeb06fa6a8ce3f38481eea88a16))
+* **api:** api update ([72b8e74](https://github.com/zavudev/sdk-python/commit/72b8e742d932db9b0422509cee1f6465202bee45))
+* **api:** manual updates ([772e899](https://github.com/zavudev/sdk-python/commit/772e899aea67cee60a68b7a82059925a65d81d77))
+
## 0.49.1 (2026-08-11)
Full Changelog: [v0.49.0...v0.49.1](https://github.com/zavudev/sdk-python/compare/v0.49.0...v0.49.1)
diff --git a/api.md b/api.md
index e0192e1..37119ec 100644
--- a/api.md
+++ b/api.md
@@ -10,6 +10,7 @@ from zavudev.types import (
MessageResponse,
MessageStatus,
MessageType,
+ MessageListAttachmentsResponse,
MessageShowTypingResponse,
)
```
@@ -18,6 +19,7 @@ Methods:
- client.messages.retrieve(message_id) -> MessageResponse
- client.messages.list(\*\*params) -> SyncCursor[Message]
+- client.messages.list_attachments(message_id) -> MessageListAttachmentsResponse
- client.messages.react(message_id, \*\*params) -> MessageResponse
- client.messages.send(\*\*params) -> MessageResponse
- client.messages.show_typing(message_id) -> MessageShowTypingResponse
@@ -27,7 +29,7 @@ Methods:
Types:
```python
-from zavudev.types import Template, WhatsappCategory
+from zavudev.types import Template, WhatsappCategory, TemplateSyncResponse
```
Methods:
@@ -37,6 +39,7 @@ Methods:
- client.templates.list(\*\*params) -> SyncCursor[Template]
- client.templates.delete(template_id) -> None
- client.templates.submit(template_id, \*\*params) -> Template
+- client.templates.sync(\*\*params) -> TemplateSyncResponse
# Senders
@@ -140,18 +143,26 @@ from zavudev.types.senders.agent import (
ToolCreateResponse,
ToolRetrieveResponse,
ToolUpdateResponse,
+ ToolListTestRunsResponse,
ToolTestResponse,
)
```
Methods:
-- client.senders.agent.tools.create(sender_id, \*\*params) -> ToolCreateResponse
-- client.senders.agent.tools.retrieve(tool_id, \*, sender_id) -> ToolRetrieveResponse
-- client.senders.agent.tools.update(tool_id, \*, sender_id, \*\*params) -> ToolUpdateResponse
-- client.senders.agent.tools.list(sender_id, \*\*params) -> SyncCursor[AgentTool]
-- client.senders.agent.tools.delete(tool_id, \*, sender_id) -> None
-- client.senders.agent.tools.test(tool_id, \*, sender_id, \*\*params) -> ToolTestResponse
+- client.senders.agent.tools.create(sender_id, \*\*params) -> ToolCreateResponse
+- client.senders.agent.tools.retrieve(tool_id, \*, sender_id) -> ToolRetrieveResponse
+- client.senders.agent.tools.update(tool_id, \*, sender_id, \*\*params) -> ToolUpdateResponse
+- client.senders.agent.tools.list(sender_id, \*\*params) -> SyncCursor[AgentTool]
+- client.senders.agent.tools.delete(tool_id, \*, sender_id) -> None
+- client.senders.agent.tools.list_test_runs(tool_id, \*, sender_id, \*\*params) -> ToolListTestRunsResponse
+- client.senders.agent.tools.test(tool_id, \*, sender_id, \*\*params) -> ToolTestResponse
+
+#### Webhook
+
+Methods:
+
+- client.senders.agent.tools.webhook.rotate_secret(tool_id, \*, sender_id) -> WebhookSecretResponse
### KnowledgeBases
@@ -180,7 +191,11 @@ Methods:
Types:
```python
-from zavudev.types.senders.agent.knowledge_bases import DocumentCreateResponse
+from zavudev.types.senders.agent.knowledge_bases import (
+ DocumentCreateResponse,
+ DocumentRetrieveDocumentResponse,
+ DocumentUpdateDocumentResponse,
+)
```
Methods:
@@ -188,6 +203,8 @@ Methods:
- client.senders.agent.knowledge_bases.documents.create(kb_id, \*, sender_id, \*\*params) -> DocumentCreateResponse
- client.senders.agent.knowledge_bases.documents.list(kb_id, \*, sender_id, \*\*params) -> SyncCursor[AgentDocument]
- client.senders.agent.knowledge_bases.documents.delete(doc_id, \*, sender_id, kb_id) -> None
+- client.senders.agent.knowledge_bases.documents.retrieve_document(doc_id, \*, sender_id, kb_id) -> DocumentRetrieveDocumentResponse
+- client.senders.agent.knowledge_bases.documents.update_document(doc_id, \*, sender_id, kb_id, \*\*params) -> DocumentUpdateDocumentResponse
## WhatsappSync
@@ -210,6 +227,19 @@ Methods:
- client.senders.whatsapp_sync.start_contacts_sync(sender_id) -> WhatsappSyncStartContactsSyncResponse
- client.senders.whatsapp_sync.start_history_sync(sender_id) -> WhatsappSyncStartHistorySyncResponse
+## Telegram
+
+Types:
+
+```python
+from zavudev.types.senders import TelegramConnectResponse
+```
+
+Methods:
+
+- client.senders.telegram.connect(sender_id, \*\*params) -> TelegramConnectResponse
+- client.senders.telegram.disconnect(sender_id) -> None
+
# Contacts
Types:
@@ -225,7 +255,6 @@ Methods:
- client.contacts.update(contact_id, \*\*params) -> Contact
- client.contacts.list(\*\*params) -> SyncCursor[Contact]
- client.contacts.delete(contact_id) -> None
-- client.contacts.dismiss_merge_suggestion(contact_id) -> None
- client.contacts.merge(contact_id, \*\*params) -> Contact
- client.contacts.retrieve_by_phone(phone_number) -> Contact
@@ -306,11 +335,12 @@ Methods:
Types:
```python
-from zavudev.types import LineType, IntrospectValidatePhoneResponse
+from zavudev.types import LineType, IntrospectValidateEmailResponse, IntrospectValidatePhoneResponse
```
Methods:
+- client.introspect.validate_email(\*\*params) -> IntrospectValidateEmailResponse
- client.introspect.validate_phone(\*\*params) -> IntrospectValidatePhoneResponse
# PhoneNumbers
@@ -409,11 +439,17 @@ Methods:
Types:
```python
-from zavudev.types import VerifiedURL, URLRetrieveDetailsResponse, URLSubmitForVerificationResponse
+from zavudev.types import (
+ VerifiedURL,
+ URLEscalateResponse,
+ URLRetrieveDetailsResponse,
+ URLSubmitForVerificationResponse,
+)
```
Methods:
+- client.urls.escalate(url_id, \*\*params) -> URLEscalateResponse
- client.urls.list_verified(\*\*params) -> SyncCursor[VerifiedURL]
- client.urls.retrieve_details(url_id) -> URLRetrieveDetailsResponse
- client.urls.submit_for_verification(\*\*params) -> URLSubmitForVerificationResponse
@@ -564,6 +600,9 @@ from zavudev.types import (
FunctionDeleteResponse,
FunctionDeployResponse,
FunctionGetDeploymentResponse,
+ FunctionListDeploymentsResponse,
+ FunctionListEventTypesResponse,
+ FunctionRollbackDeploymentResponse,
FunctionTailLogsResponse,
)
```
@@ -576,6 +615,9 @@ Methods:
- client.functions.delete(function_id) -> FunctionDeleteResponse
- client.functions.deploy(function_id, \*\*params) -> FunctionDeployResponse
- client.functions.get_deployment(deployment_id) -> FunctionGetDeploymentResponse
+- client.functions.list_deployments(function_id, \*\*params) -> FunctionListDeploymentsResponse
+- client.functions.list_event_types() -> FunctionListEventTypesResponse
+- client.functions.rollback_deployment(function_id, \*\*params) -> FunctionRollbackDeploymentResponse
- client.functions.tail_logs(function_id, \*\*params) -> FunctionTailLogsResponse
## Secrets
@@ -591,3 +633,153 @@ Methods:
- client.functions.secrets.list(function_id) -> SecretListResponse
- client.functions.secrets.set(key, \*, function_id, \*\*params) -> object
- client.functions.secrets.unset(key, \*, function_id) -> None
+
+## Triggers
+
+Types:
+
+```python
+from zavudev.types.functions import (
+ TriggerCreateResponse,
+ TriggerUpdateResponse,
+ TriggerListResponse,
+)
+```
+
+Methods:
+
+- client.functions.triggers.create(function_id, \*\*params) -> TriggerCreateResponse
+- client.functions.triggers.update(trigger_id, \*\*params) -> TriggerUpdateResponse
+- client.functions.triggers.list(function_id) -> TriggerListResponse
+- client.functions.triggers.delete(trigger_id) -> None
+
+## GitLink
+
+Types:
+
+```python
+from zavudev.types.functions import (
+ GitLinkRetrieveResponse,
+ GitLinkUpdateResponse,
+ GitLinkDeployNowResponse,
+ GitLinkLinkResponse,
+)
+```
+
+Methods:
+
+- client.functions.git_link.retrieve(function_id) -> GitLinkRetrieveResponse
+- client.functions.git_link.update(function_id, \*\*params) -> GitLinkUpdateResponse
+- client.functions.git_link.deploy_now(function_id) -> GitLinkDeployNowResponse
+- client.functions.git_link.link(function_id, \*\*params) -> GitLinkLinkResponse
+- client.functions.git_link.unlink(function_id) -> None
+
+# Conversations
+
+Types:
+
+```python
+from zavudev.types import (
+ ConversationRetrieveResponse,
+ ConversationListResponse,
+ ConversationMarkAsReadResponse,
+)
+```
+
+Methods:
+
+- client.conversations.retrieve(conversation_id) -> ConversationRetrieveResponse
+- client.conversations.list(\*\*params) -> SyncCursor[ConversationListResponse]
+- client.conversations.list_messages(conversation_id, \*\*params) -> SyncCursor[Message]
+- client.conversations.mark_as_read(conversation_id) -> ConversationMarkAsReadResponse
+
+# Calls
+
+Types:
+
+```python
+from zavudev.types import (
+ CallCreateResponse,
+ CallRetrieveResponse,
+ CallListResponse,
+ CallHangupResponse,
+)
+```
+
+Methods:
+
+- client.calls.create(\*\*params) -> CallCreateResponse
+- client.calls.retrieve(call_id) -> CallRetrieveResponse
+- client.calls.list(\*\*params) -> SyncCursor[CallListResponse]
+- client.calls.hangup(call_id) -> CallHangupResponse
+
+# AgentTemplates
+
+Types:
+
+```python
+from zavudev.types import AgentTemplateRetrieveResponse, AgentTemplateListResponse
+```
+
+Methods:
+
+- client.agent_templates.retrieve(template_id) -> AgentTemplateRetrieveResponse
+- client.agent_templates.list() -> AgentTemplateListResponse
+
+# EmailDomains
+
+Types:
+
+```python
+from zavudev.types import (
+ EmailDomainCreateResponse,
+ EmailDomainRetrieveResponse,
+ EmailDomainListResponse,
+ EmailDomainVerifyResponse,
+)
+```
+
+Methods:
+
+- client.email_domains.create(\*\*params) -> EmailDomainCreateResponse
+- client.email_domains.retrieve(domain_id) -> EmailDomainRetrieveResponse
+- client.email_domains.list() -> EmailDomainListResponse
+- client.email_domains.delete(domain_id) -> None
+- client.email_domains.verify(domain_id) -> EmailDomainVerifyResponse
+
+# Agents
+
+Types:
+
+```python
+from zavudev.types import (
+ AgentCreateResponse,
+ AgentRetrieveResponse,
+ AgentUpdateResponse,
+ AgentListVoicesResponse,
+ AgentTestResponse,
+)
+```
+
+Methods:
+
+- client.agents.create(\*\*params) -> AgentCreateResponse
+- client.agents.retrieve(agent_id) -> AgentRetrieveResponse
+- client.agents.update(agent_id, \*\*params) -> AgentUpdateResponse
+- client.agents.list(\*\*params) -> SyncCursor[Agent]
+- client.agents.delete(agent_id) -> None
+- client.agents.list_voices(\*\*params) -> AgentListVoicesResponse
+- client.agents.test(agent_id, \*\*params) -> AgentTestResponse
+
+## Senders
+
+Types:
+
+```python
+from zavudev.types.agents import SenderConnectResponse
+```
+
+Methods:
+
+- client.agents.senders.connect(agent_id, \*\*params) -> SenderConnectResponse
+- client.agents.senders.disconnect(sender_id, \*, agent_id) -> None
diff --git a/pyproject.toml b/pyproject.toml
index a5242ee..fb774b6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "zavudev"
-version = "0.49.1"
+version = "0.50.0"
description = "The official Python library for the zavudev API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/zavudev/_client.py b/src/zavudev/_client.py
index d93ccc7..1f0bc30 100644
--- a/src/zavudev/_client.py
+++ b/src/zavudev/_client.py
@@ -38,6 +38,8 @@
from .resources import (
me,
urls,
+ calls,
+ agents,
balance,
senders,
contacts,
@@ -50,18 +52,26 @@
invitations,
number_10dlc,
sub_accounts,
+ conversations,
+ email_domains,
phone_numbers,
+ agent_templates,
regulatory_documents,
)
from .resources.me import MeResource, AsyncMeResource
from .resources.urls import URLsResource, AsyncURLsResource
+ from .resources.calls import CallsResource, AsyncCallsResource
from .resources.balance import BalanceResource, AsyncBalanceResource
from .resources.messages import MessagesResource, AsyncMessagesResource
from .resources.addresses import AddressesResource, AsyncAddressesResource
from .resources.templates import TemplatesResource, AsyncTemplatesResource
from .resources.introspect import IntrospectResource, AsyncIntrospectResource
from .resources.invitations import InvitationsResource, AsyncInvitationsResource
+ from .resources.agents.agents import AgentsResource, AsyncAgentsResource
+ from .resources.conversations import ConversationsResource, AsyncConversationsResource
+ from .resources.email_domains import EmailDomainsResource, AsyncEmailDomainsResource
from .resources.phone_numbers import PhoneNumbersResource, AsyncPhoneNumbersResource
+ from .resources.agent_templates import AgentTemplatesResource, AsyncAgentTemplatesResource
from .resources.senders.senders import SendersResource, AsyncSendersResource
from .resources.contacts.contacts import ContactsResource, AsyncContactsResource
from .resources.functions.functions import FunctionsResource, AsyncFunctionsResource
@@ -233,6 +243,36 @@ def functions(self) -> FunctionsResource:
return FunctionsResource(self)
+ @cached_property
+ def conversations(self) -> ConversationsResource:
+ from .resources.conversations import ConversationsResource
+
+ return ConversationsResource(self)
+
+ @cached_property
+ def calls(self) -> CallsResource:
+ from .resources.calls import CallsResource
+
+ return CallsResource(self)
+
+ @cached_property
+ def agent_templates(self) -> AgentTemplatesResource:
+ from .resources.agent_templates import AgentTemplatesResource
+
+ return AgentTemplatesResource(self)
+
+ @cached_property
+ def email_domains(self) -> EmailDomainsResource:
+ from .resources.email_domains import EmailDomainsResource
+
+ return EmailDomainsResource(self)
+
+ @cached_property
+ def agents(self) -> AgentsResource:
+ from .resources.agents import AgentsResource
+
+ return AgentsResource(self)
+
@cached_property
def with_raw_response(self) -> ZavudevWithRawResponse:
return ZavudevWithRawResponse(self)
@@ -506,6 +546,36 @@ def functions(self) -> AsyncFunctionsResource:
return AsyncFunctionsResource(self)
+ @cached_property
+ def conversations(self) -> AsyncConversationsResource:
+ from .resources.conversations import AsyncConversationsResource
+
+ return AsyncConversationsResource(self)
+
+ @cached_property
+ def calls(self) -> AsyncCallsResource:
+ from .resources.calls import AsyncCallsResource
+
+ return AsyncCallsResource(self)
+
+ @cached_property
+ def agent_templates(self) -> AsyncAgentTemplatesResource:
+ from .resources.agent_templates import AsyncAgentTemplatesResource
+
+ return AsyncAgentTemplatesResource(self)
+
+ @cached_property
+ def email_domains(self) -> AsyncEmailDomainsResource:
+ from .resources.email_domains import AsyncEmailDomainsResource
+
+ return AsyncEmailDomainsResource(self)
+
+ @cached_property
+ def agents(self) -> AsyncAgentsResource:
+ from .resources.agents import AsyncAgentsResource
+
+ return AsyncAgentsResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncZavudevWithRawResponse:
return AsyncZavudevWithRawResponse(self)
@@ -721,6 +791,36 @@ def functions(self) -> functions.FunctionsResourceWithRawResponse:
return FunctionsResourceWithRawResponse(self._client.functions)
+ @cached_property
+ def conversations(self) -> conversations.ConversationsResourceWithRawResponse:
+ from .resources.conversations import ConversationsResourceWithRawResponse
+
+ return ConversationsResourceWithRawResponse(self._client.conversations)
+
+ @cached_property
+ def calls(self) -> calls.CallsResourceWithRawResponse:
+ from .resources.calls import CallsResourceWithRawResponse
+
+ return CallsResourceWithRawResponse(self._client.calls)
+
+ @cached_property
+ def agent_templates(self) -> agent_templates.AgentTemplatesResourceWithRawResponse:
+ from .resources.agent_templates import AgentTemplatesResourceWithRawResponse
+
+ return AgentTemplatesResourceWithRawResponse(self._client.agent_templates)
+
+ @cached_property
+ def email_domains(self) -> email_domains.EmailDomainsResourceWithRawResponse:
+ from .resources.email_domains import EmailDomainsResourceWithRawResponse
+
+ return EmailDomainsResourceWithRawResponse(self._client.email_domains)
+
+ @cached_property
+ def agents(self) -> agents.AgentsResourceWithRawResponse:
+ from .resources.agents import AgentsResourceWithRawResponse
+
+ return AgentsResourceWithRawResponse(self._client.agents)
+
class AsyncZavudevWithRawResponse:
_client: AsyncZavudev
@@ -824,6 +924,36 @@ def functions(self) -> functions.AsyncFunctionsResourceWithRawResponse:
return AsyncFunctionsResourceWithRawResponse(self._client.functions)
+ @cached_property
+ def conversations(self) -> conversations.AsyncConversationsResourceWithRawResponse:
+ from .resources.conversations import AsyncConversationsResourceWithRawResponse
+
+ return AsyncConversationsResourceWithRawResponse(self._client.conversations)
+
+ @cached_property
+ def calls(self) -> calls.AsyncCallsResourceWithRawResponse:
+ from .resources.calls import AsyncCallsResourceWithRawResponse
+
+ return AsyncCallsResourceWithRawResponse(self._client.calls)
+
+ @cached_property
+ def agent_templates(self) -> agent_templates.AsyncAgentTemplatesResourceWithRawResponse:
+ from .resources.agent_templates import AsyncAgentTemplatesResourceWithRawResponse
+
+ return AsyncAgentTemplatesResourceWithRawResponse(self._client.agent_templates)
+
+ @cached_property
+ def email_domains(self) -> email_domains.AsyncEmailDomainsResourceWithRawResponse:
+ from .resources.email_domains import AsyncEmailDomainsResourceWithRawResponse
+
+ return AsyncEmailDomainsResourceWithRawResponse(self._client.email_domains)
+
+ @cached_property
+ def agents(self) -> agents.AsyncAgentsResourceWithRawResponse:
+ from .resources.agents import AsyncAgentsResourceWithRawResponse
+
+ return AsyncAgentsResourceWithRawResponse(self._client.agents)
+
class ZavudevWithStreamedResponse:
_client: Zavudev
@@ -927,6 +1057,36 @@ def functions(self) -> functions.FunctionsResourceWithStreamingResponse:
return FunctionsResourceWithStreamingResponse(self._client.functions)
+ @cached_property
+ def conversations(self) -> conversations.ConversationsResourceWithStreamingResponse:
+ from .resources.conversations import ConversationsResourceWithStreamingResponse
+
+ return ConversationsResourceWithStreamingResponse(self._client.conversations)
+
+ @cached_property
+ def calls(self) -> calls.CallsResourceWithStreamingResponse:
+ from .resources.calls import CallsResourceWithStreamingResponse
+
+ return CallsResourceWithStreamingResponse(self._client.calls)
+
+ @cached_property
+ def agent_templates(self) -> agent_templates.AgentTemplatesResourceWithStreamingResponse:
+ from .resources.agent_templates import AgentTemplatesResourceWithStreamingResponse
+
+ return AgentTemplatesResourceWithStreamingResponse(self._client.agent_templates)
+
+ @cached_property
+ def email_domains(self) -> email_domains.EmailDomainsResourceWithStreamingResponse:
+ from .resources.email_domains import EmailDomainsResourceWithStreamingResponse
+
+ return EmailDomainsResourceWithStreamingResponse(self._client.email_domains)
+
+ @cached_property
+ def agents(self) -> agents.AgentsResourceWithStreamingResponse:
+ from .resources.agents import AgentsResourceWithStreamingResponse
+
+ return AgentsResourceWithStreamingResponse(self._client.agents)
+
class AsyncZavudevWithStreamedResponse:
_client: AsyncZavudev
@@ -1030,6 +1190,36 @@ def functions(self) -> functions.AsyncFunctionsResourceWithStreamingResponse:
return AsyncFunctionsResourceWithStreamingResponse(self._client.functions)
+ @cached_property
+ def conversations(self) -> conversations.AsyncConversationsResourceWithStreamingResponse:
+ from .resources.conversations import AsyncConversationsResourceWithStreamingResponse
+
+ return AsyncConversationsResourceWithStreamingResponse(self._client.conversations)
+
+ @cached_property
+ def calls(self) -> calls.AsyncCallsResourceWithStreamingResponse:
+ from .resources.calls import AsyncCallsResourceWithStreamingResponse
+
+ return AsyncCallsResourceWithStreamingResponse(self._client.calls)
+
+ @cached_property
+ def agent_templates(self) -> agent_templates.AsyncAgentTemplatesResourceWithStreamingResponse:
+ from .resources.agent_templates import AsyncAgentTemplatesResourceWithStreamingResponse
+
+ return AsyncAgentTemplatesResourceWithStreamingResponse(self._client.agent_templates)
+
+ @cached_property
+ def email_domains(self) -> email_domains.AsyncEmailDomainsResourceWithStreamingResponse:
+ from .resources.email_domains import AsyncEmailDomainsResourceWithStreamingResponse
+
+ return AsyncEmailDomainsResourceWithStreamingResponse(self._client.email_domains)
+
+ @cached_property
+ def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse:
+ from .resources.agents import AsyncAgentsResourceWithStreamingResponse
+
+ return AsyncAgentsResourceWithStreamingResponse(self._client.agents)
+
Client = Zavudev
diff --git a/src/zavudev/_version.py b/src/zavudev/_version.py
index b121248..df55d3f 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.49.1" # x-release-please-version
+__version__ = "0.50.0" # x-release-please-version
diff --git a/src/zavudev/resources/__init__.py b/src/zavudev/resources/__init__.py
index d613dc1..6bbbd27 100644
--- a/src/zavudev/resources/__init__.py
+++ b/src/zavudev/resources/__init__.py
@@ -16,6 +16,22 @@
URLsResourceWithStreamingResponse,
AsyncURLsResourceWithStreamingResponse,
)
+from .calls import (
+ CallsResource,
+ AsyncCallsResource,
+ CallsResourceWithRawResponse,
+ AsyncCallsResourceWithRawResponse,
+ CallsResourceWithStreamingResponse,
+ AsyncCallsResourceWithStreamingResponse,
+)
+from .agents import (
+ AgentsResource,
+ AsyncAgentsResource,
+ AgentsResourceWithRawResponse,
+ AsyncAgentsResourceWithRawResponse,
+ AgentsResourceWithStreamingResponse,
+ AsyncAgentsResourceWithStreamingResponse,
+)
from .balance import (
BalanceResource,
AsyncBalanceResource,
@@ -112,6 +128,22 @@
SubAccountsResourceWithStreamingResponse,
AsyncSubAccountsResourceWithStreamingResponse,
)
+from .conversations import (
+ ConversationsResource,
+ AsyncConversationsResource,
+ ConversationsResourceWithRawResponse,
+ AsyncConversationsResourceWithRawResponse,
+ ConversationsResourceWithStreamingResponse,
+ AsyncConversationsResourceWithStreamingResponse,
+)
+from .email_domains import (
+ EmailDomainsResource,
+ AsyncEmailDomainsResource,
+ EmailDomainsResourceWithRawResponse,
+ AsyncEmailDomainsResourceWithRawResponse,
+ EmailDomainsResourceWithStreamingResponse,
+ AsyncEmailDomainsResourceWithStreamingResponse,
+)
from .phone_numbers import (
PhoneNumbersResource,
AsyncPhoneNumbersResource,
@@ -120,6 +152,14 @@
PhoneNumbersResourceWithStreamingResponse,
AsyncPhoneNumbersResourceWithStreamingResponse,
)
+from .agent_templates import (
+ AgentTemplatesResource,
+ AsyncAgentTemplatesResource,
+ AgentTemplatesResourceWithRawResponse,
+ AsyncAgentTemplatesResourceWithRawResponse,
+ AgentTemplatesResourceWithStreamingResponse,
+ AsyncAgentTemplatesResourceWithStreamingResponse,
+)
from .regulatory_documents import (
RegulatoryDocumentsResource,
AsyncRegulatoryDocumentsResource,
@@ -226,4 +266,34 @@
"AsyncFunctionsResourceWithRawResponse",
"FunctionsResourceWithStreamingResponse",
"AsyncFunctionsResourceWithStreamingResponse",
+ "ConversationsResource",
+ "AsyncConversationsResource",
+ "ConversationsResourceWithRawResponse",
+ "AsyncConversationsResourceWithRawResponse",
+ "ConversationsResourceWithStreamingResponse",
+ "AsyncConversationsResourceWithStreamingResponse",
+ "CallsResource",
+ "AsyncCallsResource",
+ "CallsResourceWithRawResponse",
+ "AsyncCallsResourceWithRawResponse",
+ "CallsResourceWithStreamingResponse",
+ "AsyncCallsResourceWithStreamingResponse",
+ "AgentTemplatesResource",
+ "AsyncAgentTemplatesResource",
+ "AgentTemplatesResourceWithRawResponse",
+ "AsyncAgentTemplatesResourceWithRawResponse",
+ "AgentTemplatesResourceWithStreamingResponse",
+ "AsyncAgentTemplatesResourceWithStreamingResponse",
+ "EmailDomainsResource",
+ "AsyncEmailDomainsResource",
+ "EmailDomainsResourceWithRawResponse",
+ "AsyncEmailDomainsResourceWithRawResponse",
+ "EmailDomainsResourceWithStreamingResponse",
+ "AsyncEmailDomainsResourceWithStreamingResponse",
+ "AgentsResource",
+ "AsyncAgentsResource",
+ "AgentsResourceWithRawResponse",
+ "AsyncAgentsResourceWithRawResponse",
+ "AgentsResourceWithStreamingResponse",
+ "AsyncAgentsResourceWithStreamingResponse",
]
diff --git a/src/zavudev/resources/agent_templates.py b/src/zavudev/resources/agent_templates.py
new file mode 100644
index 0000000..0824125
--- /dev/null
+++ b/src/zavudev/resources/agent_templates.py
@@ -0,0 +1,229 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from .._types import Body, Query, Headers, NotGiven, not_given
+from .._utils import path_template
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.agent_template_list_response import AgentTemplateListResponse
+from ..types.agent_template_retrieve_response import AgentTemplateRetrieveResponse
+
+__all__ = ["AgentTemplatesResource", "AsyncAgentTemplatesResource"]
+
+
+class AgentTemplatesResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AgentTemplatesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AgentTemplatesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AgentTemplatesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AgentTemplatesResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ template_id: str,
+ *,
+ # 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,
+ ) -> AgentTemplateRetrieveResponse:
+ """
+ Fetch a single factory agent fully rendered: the function files to scaffold (an
+ `index.ts` that declares the agent with `defineAgent` and its skills with
+ `defineTool`) plus the secrets it needs. This is what
+ `npx zavudev agents pull ` writes to disk before `npx zavudev deploy`.
+
+ Args:
+ 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._get(
+ path_template("/v1/agent-templates/{template_id}", template_id=template_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentTemplateRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ # 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,
+ ) -> AgentTemplateListResponse:
+ """
+ List the factory agents available to scaffold with `npx zavudev agents pull`.
+ Each entry is a ready-made voice or text agent (system prompt, skills, and — for
+ voice agents — a co-located voice config).
+ """
+ return self._get(
+ "/v1/agent-templates",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentTemplateListResponse,
+ )
+
+
+class AsyncAgentTemplatesResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncAgentTemplatesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAgentTemplatesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAgentTemplatesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncAgentTemplatesResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ template_id: str,
+ *,
+ # 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,
+ ) -> AgentTemplateRetrieveResponse:
+ """
+ Fetch a single factory agent fully rendered: the function files to scaffold (an
+ `index.ts` that declares the agent with `defineAgent` and its skills with
+ `defineTool`) plus the secrets it needs. This is what
+ `npx zavudev agents pull ` writes to disk before `npx zavudev deploy`.
+
+ Args:
+ 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._get(
+ path_template("/v1/agent-templates/{template_id}", template_id=template_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentTemplateRetrieveResponse,
+ )
+
+ async def list(
+ self,
+ *,
+ # 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,
+ ) -> AgentTemplateListResponse:
+ """
+ List the factory agents available to scaffold with `npx zavudev agents pull`.
+ Each entry is a ready-made voice or text agent (system prompt, skills, and — for
+ voice agents — a co-located voice config).
+ """
+ return await self._get(
+ "/v1/agent-templates",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentTemplateListResponse,
+ )
+
+
+class AgentTemplatesResourceWithRawResponse:
+ def __init__(self, agent_templates: AgentTemplatesResource) -> None:
+ self._agent_templates = agent_templates
+
+ self.retrieve = to_raw_response_wrapper(
+ agent_templates.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ agent_templates.list,
+ )
+
+
+class AsyncAgentTemplatesResourceWithRawResponse:
+ def __init__(self, agent_templates: AsyncAgentTemplatesResource) -> None:
+ self._agent_templates = agent_templates
+
+ self.retrieve = async_to_raw_response_wrapper(
+ agent_templates.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ agent_templates.list,
+ )
+
+
+class AgentTemplatesResourceWithStreamingResponse:
+ def __init__(self, agent_templates: AgentTemplatesResource) -> None:
+ self._agent_templates = agent_templates
+
+ self.retrieve = to_streamed_response_wrapper(
+ agent_templates.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ agent_templates.list,
+ )
+
+
+class AsyncAgentTemplatesResourceWithStreamingResponse:
+ def __init__(self, agent_templates: AsyncAgentTemplatesResource) -> None:
+ self._agent_templates = agent_templates
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ agent_templates.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ agent_templates.list,
+ )
diff --git a/src/zavudev/resources/agents/__init__.py b/src/zavudev/resources/agents/__init__.py
new file mode 100644
index 0000000..5c514e1
--- /dev/null
+++ b/src/zavudev/resources/agents/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .agents import (
+ AgentsResource,
+ AsyncAgentsResource,
+ AgentsResourceWithRawResponse,
+ AsyncAgentsResourceWithRawResponse,
+ AgentsResourceWithStreamingResponse,
+ AsyncAgentsResourceWithStreamingResponse,
+)
+from .senders import (
+ SendersResource,
+ AsyncSendersResource,
+ SendersResourceWithRawResponse,
+ AsyncSendersResourceWithRawResponse,
+ SendersResourceWithStreamingResponse,
+ AsyncSendersResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "SendersResource",
+ "AsyncSendersResource",
+ "SendersResourceWithRawResponse",
+ "AsyncSendersResourceWithRawResponse",
+ "SendersResourceWithStreamingResponse",
+ "AsyncSendersResourceWithStreamingResponse",
+ "AgentsResource",
+ "AsyncAgentsResource",
+ "AgentsResourceWithRawResponse",
+ "AsyncAgentsResourceWithRawResponse",
+ "AgentsResourceWithStreamingResponse",
+ "AsyncAgentsResourceWithStreamingResponse",
+]
diff --git a/src/zavudev/resources/agents/agents.py b/src/zavudev/resources/agents/agents.py
new file mode 100644
index 0000000..5ba27d8
--- /dev/null
+++ b/src/zavudev/resources/agents/agents.py
@@ -0,0 +1,951 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+
+import httpx
+
+from ...types import (
+ agent_list_params,
+ agent_test_params,
+ agent_create_params,
+ agent_update_params,
+ agent_list_voices_params,
+)
+from .senders import (
+ SendersResource,
+ AsyncSendersResource,
+ SendersResourceWithRawResponse,
+ AsyncSendersResourceWithRawResponse,
+ SendersResourceWithStreamingResponse,
+ AsyncSendersResourceWithStreamingResponse,
+)
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncCursor, AsyncCursor
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.senders import AgentProvider
+from ...types.agent_test_response import AgentTestResponse
+from ...types.senders.agent.agent import Agent
+from ...types.agent_create_response import AgentCreateResponse
+from ...types.agent_update_response import AgentUpdateResponse
+from ...types.senders.agent_provider import AgentProvider
+from ...types.agent_retrieve_response import AgentRetrieveResponse
+from ...types.agent_list_voices_response import AgentListVoicesResponse
+
+__all__ = ["AgentsResource", "AsyncAgentsResource"]
+
+
+class AgentsResource(SyncAPIResource):
+ @cached_property
+ def senders(self) -> SendersResource:
+ return SendersResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AgentsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AgentsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AgentsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AgentsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ model: str,
+ name: str,
+ provider: AgentProvider,
+ system_prompt: str,
+ context_window_messages: int | Omit = omit,
+ include_contact_metadata: bool | Omit = omit,
+ max_tokens: int | Omit = omit,
+ temperature: float | Omit = omit,
+ trigger_on_channels: SequenceNotStr[str] | Omit = omit,
+ trigger_on_message_types: SequenceNotStr[str] | Omit = omit,
+ voice: agent_create_params.Voice | 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,
+ ) -> AgentCreateResponse:
+ """Create an agent without a sender.
+
+ It is created disabled; connect a sender and
+ enable it when you are ready for it to answer.
+
+ **Sub-resources.** An agent's tools, flows and knowledge bases are reachable at
+ `/v1/agents/{agentId}/tools`, `/v1/agents/{agentId}/flows` and
+ `/v1/agents/{agentId}/knowledge-bases`, mirroring the sender-scoped routes
+ documented under `/v1/senders/{senderId}/agent/...` exactly. Use the
+ agent-scoped form while the agent has no sender: the sender-scoped one cannot
+ address it.
+
+ Args:
+ provider: LLM provider for the AI agent.
+
+ voice: Voice Agent configuration on a sender's AI agent. Controls how the agent behaves
+ on inbound and outbound phone calls through Zavu's managed voice pipeline
+ (speech recognition, the agent's LLM, and speech synthesis, with real-time
+ interruption handling). Requires the Voice Agents feature to be enabled for your
+ team.
+
+ 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
+ """
+ return self._post(
+ "/v1/agents",
+ body=maybe_transform(
+ {
+ "model": model,
+ "name": name,
+ "provider": provider,
+ "system_prompt": system_prompt,
+ "context_window_messages": context_window_messages,
+ "include_contact_metadata": include_contact_metadata,
+ "max_tokens": max_tokens,
+ "temperature": temperature,
+ "trigger_on_channels": trigger_on_channels,
+ "trigger_on_message_types": trigger_on_message_types,
+ "voice": voice,
+ },
+ agent_create_params.AgentCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ agent_id: str,
+ *,
+ # 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,
+ ) -> AgentRetrieveResponse:
+ """
+ Get an agent
+
+ Args:
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return self._get(
+ path_template("/v1/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentRetrieveResponse,
+ )
+
+ def update(
+ self,
+ agent_id: str,
+ *,
+ api_key: str | Omit = omit,
+ context_window_messages: int | Omit = omit,
+ enabled: bool | Omit = omit,
+ include_contact_metadata: bool | Omit = omit,
+ max_tokens: Optional[int] | Omit = omit,
+ model: str | Omit = omit,
+ name: str | Omit = omit,
+ provider: AgentProvider | Omit = omit,
+ system_prompt: str | Omit = omit,
+ temperature: Optional[float] | Omit = omit,
+ trigger_on_channels: SequenceNotStr[str] | Omit = omit,
+ trigger_on_message_types: SequenceNotStr[str] | Omit = omit,
+ voice: agent_update_params.Voice | 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,
+ ) -> AgentUpdateResponse:
+ """
+ Update an agent
+
+ Args:
+ provider: LLM provider for the AI agent.
+
+ voice: Voice Agent configuration. Patch this object to enable voice, change the
+ greeting, or adjust call limits. Requires the Voice Agents feature to be enabled
+ for your team.
+
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return self._patch(
+ path_template("/v1/agents/{agent_id}", agent_id=agent_id),
+ body=maybe_transform(
+ {
+ "api_key": api_key,
+ "context_window_messages": context_window_messages,
+ "enabled": enabled,
+ "include_contact_metadata": include_contact_metadata,
+ "max_tokens": max_tokens,
+ "model": model,
+ "name": name,
+ "provider": provider,
+ "system_prompt": system_prompt,
+ "temperature": temperature,
+ "trigger_on_channels": trigger_on_channels,
+ "trigger_on_message_types": trigger_on_message_types,
+ "voice": voice,
+ },
+ agent_update_params.AgentUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentUpdateResponse,
+ )
+
+ def list(
+ self,
+ *,
+ cursor: str | Omit = omit,
+ limit: int | 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,
+ ) -> SyncCursor[Agent]:
+ """
+ Every agent in the project, newest first — including agents that are not
+ connected to any sender yet, which the sender-scoped routes cannot reach. Each
+ item carries `senderIds`, the senders the agent answers on.
+
+ Args:
+ 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
+ """
+ return self._get_api_list(
+ "/v1/agents",
+ page=SyncCursor[Agent],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "cursor": cursor,
+ "limit": limit,
+ },
+ agent_list_params.AgentListParams,
+ ),
+ ),
+ model=Agent,
+ )
+
+ def delete(
+ self,
+ agent_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Delete an agent
+
+ Args:
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/v1/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def list_voices(
+ self,
+ *,
+ language: str | 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,
+ ) -> AgentListVoicesResponse:
+ """The voices an agent can speak with, for `voice.ttsVoiceId`.
+
+ Filter by `language`
+ to get the ones that speak it; a voice can still be used with `language: auto`,
+ where the agent follows the caller and keeps the chosen voice.
+
+ Args:
+ language: BCP-47 tag (`en`, `es`, `pt-BR`). Omit, or pass `auto`, for every voice.
+
+ 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
+ """
+ return self._get(
+ "/v1/agents/voices",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"language": language}, agent_list_voices_params.AgentListVoicesParams),
+ ),
+ cast_to=AgentListVoicesResponse,
+ )
+
+ def test(
+ self,
+ agent_id: str,
+ *,
+ message: str,
+ execute_tools: bool | Omit = omit,
+ history: Iterable[agent_test_params.History] | Omit = omit,
+ use_knowledge_base: bool | 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,
+ ) -> AgentTestResponse:
+ """
+ Run the agent's prompt, model and knowledge base against a message and return
+ the reply instead of delivering it. Writes nothing and charges nothing, so it is
+ safe to call repeatedly while iterating on a prompt.
+
+ Note that a dry run never **executes** tools — running them would cause real
+ side effects. Live conversations on every channel do call them. When the agent
+ has enabled tools, that gap is reported in `warnings` rather than silently
+ producing an answer that looks like a tool call happened.
+
+ Args:
+ message: What to say to the agent.
+
+ execute_tools: Run the tools the agent calls instead of reporting the choice and stopping.
+
+ Off by default because a tool handler talks to the outside world: a rehearsal
+ that charges a card is not a rehearsal. Turn it on to exercise the loop that
+ actually matters — the model picks a tool, the handler answers, the model
+ replies with the result — without sending a message to anyone. What ran comes
+ back in `executedToolCalls`.
+
+ history: Prior turns, oldest first, to exercise multi-turn behaviour without persisting a
+ thread. Trimmed to the agent's context window.
+
+ use_knowledge_base: Set false to skip retrieval and isolate prompt behaviour from the knowledge
+ base.
+
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return self._post(
+ path_template("/v1/agents/{agent_id}/test", agent_id=agent_id),
+ body=maybe_transform(
+ {
+ "message": message,
+ "execute_tools": execute_tools,
+ "history": history,
+ "use_knowledge_base": use_knowledge_base,
+ },
+ agent_test_params.AgentTestParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentTestResponse,
+ )
+
+
+class AsyncAgentsResource(AsyncAPIResource):
+ @cached_property
+ def senders(self) -> AsyncSendersResource:
+ return AsyncSendersResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAgentsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncAgentsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ model: str,
+ name: str,
+ provider: AgentProvider,
+ system_prompt: str,
+ context_window_messages: int | Omit = omit,
+ include_contact_metadata: bool | Omit = omit,
+ max_tokens: int | Omit = omit,
+ temperature: float | Omit = omit,
+ trigger_on_channels: SequenceNotStr[str] | Omit = omit,
+ trigger_on_message_types: SequenceNotStr[str] | Omit = omit,
+ voice: agent_create_params.Voice | 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,
+ ) -> AgentCreateResponse:
+ """Create an agent without a sender.
+
+ It is created disabled; connect a sender and
+ enable it when you are ready for it to answer.
+
+ **Sub-resources.** An agent's tools, flows and knowledge bases are reachable at
+ `/v1/agents/{agentId}/tools`, `/v1/agents/{agentId}/flows` and
+ `/v1/agents/{agentId}/knowledge-bases`, mirroring the sender-scoped routes
+ documented under `/v1/senders/{senderId}/agent/...` exactly. Use the
+ agent-scoped form while the agent has no sender: the sender-scoped one cannot
+ address it.
+
+ Args:
+ provider: LLM provider for the AI agent.
+
+ voice: Voice Agent configuration on a sender's AI agent. Controls how the agent behaves
+ on inbound and outbound phone calls through Zavu's managed voice pipeline
+ (speech recognition, the agent's LLM, and speech synthesis, with real-time
+ interruption handling). Requires the Voice Agents feature to be enabled for your
+ team.
+
+ 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
+ """
+ return await self._post(
+ "/v1/agents",
+ body=await async_maybe_transform(
+ {
+ "model": model,
+ "name": name,
+ "provider": provider,
+ "system_prompt": system_prompt,
+ "context_window_messages": context_window_messages,
+ "include_contact_metadata": include_contact_metadata,
+ "max_tokens": max_tokens,
+ "temperature": temperature,
+ "trigger_on_channels": trigger_on_channels,
+ "trigger_on_message_types": trigger_on_message_types,
+ "voice": voice,
+ },
+ agent_create_params.AgentCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ agent_id: str,
+ *,
+ # 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,
+ ) -> AgentRetrieveResponse:
+ """
+ Get an agent
+
+ Args:
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return await self._get(
+ path_template("/v1/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ agent_id: str,
+ *,
+ api_key: str | Omit = omit,
+ context_window_messages: int | Omit = omit,
+ enabled: bool | Omit = omit,
+ include_contact_metadata: bool | Omit = omit,
+ max_tokens: Optional[int] | Omit = omit,
+ model: str | Omit = omit,
+ name: str | Omit = omit,
+ provider: AgentProvider | Omit = omit,
+ system_prompt: str | Omit = omit,
+ temperature: Optional[float] | Omit = omit,
+ trigger_on_channels: SequenceNotStr[str] | Omit = omit,
+ trigger_on_message_types: SequenceNotStr[str] | Omit = omit,
+ voice: agent_update_params.Voice | 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,
+ ) -> AgentUpdateResponse:
+ """
+ Update an agent
+
+ Args:
+ provider: LLM provider for the AI agent.
+
+ voice: Voice Agent configuration. Patch this object to enable voice, change the
+ greeting, or adjust call limits. Requires the Voice Agents feature to be enabled
+ for your team.
+
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return await self._patch(
+ path_template("/v1/agents/{agent_id}", agent_id=agent_id),
+ body=await async_maybe_transform(
+ {
+ "api_key": api_key,
+ "context_window_messages": context_window_messages,
+ "enabled": enabled,
+ "include_contact_metadata": include_contact_metadata,
+ "max_tokens": max_tokens,
+ "model": model,
+ "name": name,
+ "provider": provider,
+ "system_prompt": system_prompt,
+ "temperature": temperature,
+ "trigger_on_channels": trigger_on_channels,
+ "trigger_on_message_types": trigger_on_message_types,
+ "voice": voice,
+ },
+ agent_update_params.AgentUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentUpdateResponse,
+ )
+
+ def list(
+ self,
+ *,
+ cursor: str | Omit = omit,
+ limit: int | 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,
+ ) -> AsyncPaginator[Agent, AsyncCursor[Agent]]:
+ """
+ Every agent in the project, newest first — including agents that are not
+ connected to any sender yet, which the sender-scoped routes cannot reach. Each
+ item carries `senderIds`, the senders the agent answers on.
+
+ Args:
+ 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
+ """
+ return self._get_api_list(
+ "/v1/agents",
+ page=AsyncCursor[Agent],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "cursor": cursor,
+ "limit": limit,
+ },
+ agent_list_params.AgentListParams,
+ ),
+ ),
+ model=Agent,
+ )
+
+ async def delete(
+ self,
+ agent_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Delete an agent
+
+ Args:
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/v1/agents/{agent_id}", agent_id=agent_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def list_voices(
+ self,
+ *,
+ language: str | 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,
+ ) -> AgentListVoicesResponse:
+ """The voices an agent can speak with, for `voice.ttsVoiceId`.
+
+ Filter by `language`
+ to get the ones that speak it; a voice can still be used with `language: auto`,
+ where the agent follows the caller and keeps the chosen voice.
+
+ Args:
+ language: BCP-47 tag (`en`, `es`, `pt-BR`). Omit, or pass `auto`, for every voice.
+
+ 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
+ """
+ return await self._get(
+ "/v1/agents/voices",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"language": language}, agent_list_voices_params.AgentListVoicesParams
+ ),
+ ),
+ cast_to=AgentListVoicesResponse,
+ )
+
+ async def test(
+ self,
+ agent_id: str,
+ *,
+ message: str,
+ execute_tools: bool | Omit = omit,
+ history: Iterable[agent_test_params.History] | Omit = omit,
+ use_knowledge_base: bool | 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,
+ ) -> AgentTestResponse:
+ """
+ Run the agent's prompt, model and knowledge base against a message and return
+ the reply instead of delivering it. Writes nothing and charges nothing, so it is
+ safe to call repeatedly while iterating on a prompt.
+
+ Note that a dry run never **executes** tools — running them would cause real
+ side effects. Live conversations on every channel do call them. When the agent
+ has enabled tools, that gap is reported in `warnings` rather than silently
+ producing an answer that looks like a tool call happened.
+
+ Args:
+ message: What to say to the agent.
+
+ execute_tools: Run the tools the agent calls instead of reporting the choice and stopping.
+
+ Off by default because a tool handler talks to the outside world: a rehearsal
+ that charges a card is not a rehearsal. Turn it on to exercise the loop that
+ actually matters — the model picks a tool, the handler answers, the model
+ replies with the result — without sending a message to anyone. What ran comes
+ back in `executedToolCalls`.
+
+ history: Prior turns, oldest first, to exercise multi-turn behaviour without persisting a
+ thread. Trimmed to the agent's context window.
+
+ use_knowledge_base: Set false to skip retrieval and isolate prompt behaviour from the knowledge
+ base.
+
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return await self._post(
+ path_template("/v1/agents/{agent_id}/test", agent_id=agent_id),
+ body=await async_maybe_transform(
+ {
+ "message": message,
+ "execute_tools": execute_tools,
+ "history": history,
+ "use_knowledge_base": use_knowledge_base,
+ },
+ agent_test_params.AgentTestParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AgentTestResponse,
+ )
+
+
+class AgentsResourceWithRawResponse:
+ def __init__(self, agents: AgentsResource) -> None:
+ self._agents = agents
+
+ self.create = to_raw_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ agents.update,
+ )
+ self.list = to_raw_response_wrapper(
+ agents.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ agents.delete,
+ )
+ self.list_voices = to_raw_response_wrapper(
+ agents.list_voices,
+ )
+ self.test = to_raw_response_wrapper(
+ agents.test,
+ )
+
+ @cached_property
+ def senders(self) -> SendersResourceWithRawResponse:
+ return SendersResourceWithRawResponse(self._agents.senders)
+
+
+class AsyncAgentsResourceWithRawResponse:
+ def __init__(self, agents: AsyncAgentsResource) -> None:
+ self._agents = agents
+
+ self.create = async_to_raw_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ agents.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ agents.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ agents.delete,
+ )
+ self.list_voices = async_to_raw_response_wrapper(
+ agents.list_voices,
+ )
+ self.test = async_to_raw_response_wrapper(
+ agents.test,
+ )
+
+ @cached_property
+ def senders(self) -> AsyncSendersResourceWithRawResponse:
+ return AsyncSendersResourceWithRawResponse(self._agents.senders)
+
+
+class AgentsResourceWithStreamingResponse:
+ def __init__(self, agents: AgentsResource) -> None:
+ self._agents = agents
+
+ self.create = to_streamed_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ agents.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ agents.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ agents.delete,
+ )
+ self.list_voices = to_streamed_response_wrapper(
+ agents.list_voices,
+ )
+ self.test = to_streamed_response_wrapper(
+ agents.test,
+ )
+
+ @cached_property
+ def senders(self) -> SendersResourceWithStreamingResponse:
+ return SendersResourceWithStreamingResponse(self._agents.senders)
+
+
+class AsyncAgentsResourceWithStreamingResponse:
+ def __init__(self, agents: AsyncAgentsResource) -> None:
+ self._agents = agents
+
+ self.create = async_to_streamed_response_wrapper(
+ agents.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ agents.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ agents.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ agents.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ agents.delete,
+ )
+ self.list_voices = async_to_streamed_response_wrapper(
+ agents.list_voices,
+ )
+ self.test = async_to_streamed_response_wrapper(
+ agents.test,
+ )
+
+ @cached_property
+ def senders(self) -> AsyncSendersResourceWithStreamingResponse:
+ return AsyncSendersResourceWithStreamingResponse(self._agents.senders)
diff --git a/src/zavudev/resources/agents/senders.py b/src/zavudev/resources/agents/senders.py
new file mode 100644
index 0000000..faacff9
--- /dev/null
+++ b/src/zavudev/resources/agents/senders.py
@@ -0,0 +1,269 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Query, Headers, NoneType, NotGiven, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.agents import sender_connect_params
+from ...types.agents.sender_connect_response import SenderConnectResponse
+
+__all__ = ["SendersResource", "AsyncSendersResource"]
+
+
+class SendersResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> SendersResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return SendersResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SendersResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return SendersResourceWithStreamingResponse(self)
+
+ def connect(
+ self,
+ agent_id: str,
+ *,
+ sender_id: str,
+ # 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,
+ ) -> SenderConnectResponse:
+ """Make the agent answer on this sender.
+
+ An agent can serve several senders; a
+ sender answers with at most one agent, so connecting one that is already in use
+ returns `400` naming the agent that holds it.
+
+ Args:
+ sender_id: Sender to connect.
+
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return self._post(
+ path_template("/v1/agents/{agent_id}/senders", agent_id=agent_id),
+ body=maybe_transform({"sender_id": sender_id}, sender_connect_params.SenderConnectParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SenderConnectResponse,
+ )
+
+ def disconnect(
+ self,
+ sender_id: str,
+ *,
+ agent_id: str,
+ # 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,
+ ) -> None:
+ """Stop the agent answering on this sender.
+
+ The agent's primary sender is part of
+ the agent itself and cannot be disconnected here.
+
+ Args:
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ if not sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/v1/agents/{agent_id}/senders/{sender_id}", agent_id=agent_id, sender_id=sender_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncSendersResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncSendersResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSendersResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSendersResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncSendersResourceWithStreamingResponse(self)
+
+ async def connect(
+ self,
+ agent_id: str,
+ *,
+ sender_id: str,
+ # 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,
+ ) -> SenderConnectResponse:
+ """Make the agent answer on this sender.
+
+ An agent can serve several senders; a
+ sender answers with at most one agent, so connecting one that is already in use
+ returns `400` naming the agent that holds it.
+
+ Args:
+ sender_id: Sender to connect.
+
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ return await self._post(
+ path_template("/v1/agents/{agent_id}/senders", agent_id=agent_id),
+ body=await async_maybe_transform({"sender_id": sender_id}, sender_connect_params.SenderConnectParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SenderConnectResponse,
+ )
+
+ async def disconnect(
+ self,
+ sender_id: str,
+ *,
+ agent_id: str,
+ # 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,
+ ) -> None:
+ """Stop the agent answering on this sender.
+
+ The agent's primary sender is part of
+ the agent itself and cannot be disconnected here.
+
+ Args:
+ 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 agent_id:
+ raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}")
+ if not sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/v1/agents/{agent_id}/senders/{sender_id}", agent_id=agent_id, sender_id=sender_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class SendersResourceWithRawResponse:
+ def __init__(self, senders: SendersResource) -> None:
+ self._senders = senders
+
+ self.connect = to_raw_response_wrapper(
+ senders.connect,
+ )
+ self.disconnect = to_raw_response_wrapper(
+ senders.disconnect,
+ )
+
+
+class AsyncSendersResourceWithRawResponse:
+ def __init__(self, senders: AsyncSendersResource) -> None:
+ self._senders = senders
+
+ self.connect = async_to_raw_response_wrapper(
+ senders.connect,
+ )
+ self.disconnect = async_to_raw_response_wrapper(
+ senders.disconnect,
+ )
+
+
+class SendersResourceWithStreamingResponse:
+ def __init__(self, senders: SendersResource) -> None:
+ self._senders = senders
+
+ self.connect = to_streamed_response_wrapper(
+ senders.connect,
+ )
+ self.disconnect = to_streamed_response_wrapper(
+ senders.disconnect,
+ )
+
+
+class AsyncSendersResourceWithStreamingResponse:
+ def __init__(self, senders: AsyncSendersResource) -> None:
+ self._senders = senders
+
+ self.connect = async_to_streamed_response_wrapper(
+ senders.connect,
+ )
+ self.disconnect = async_to_streamed_response_wrapper(
+ senders.disconnect,
+ )
diff --git a/src/zavudev/resources/broadcasts/broadcasts.py b/src/zavudev/resources/broadcasts/broadcasts.py
index 4f2b2bd..b97067e 100644
--- a/src/zavudev/resources/broadcasts/broadcasts.py
+++ b/src/zavudev/resources/broadcasts/broadcasts.py
@@ -516,12 +516,22 @@ def send(
"""
Start sending the broadcast immediately or schedule for later.
- **Verification is required to send, and there are two of them.** The team must
- have completed both identity verification (KYC) and business verification (KYB);
- passing one is not enough. Drafts can be created, edited and kept without
- either. Every send path — dashboard, API and CLI alike — enforces both,
- returning `403` with code `kyc_required` or `kyb_required` for whichever is
- outstanding.
+ **The account must be past the unverified level to send, except on WhatsApp.**
+ An account that has verified nothing is refused with `403` and code
+ `kyc_required` on every channel other than `whatsapp`. Any one of these lifts
+ it: identity verification (KYC), a saved payment method, a settled deposit, or a
+ paid plan. Business verification (KYB) is not required to broadcast; it gates
+ 10DLC registration only. A `whatsapp` broadcast is exempt: it can only be built
+ on a template, and Meta vets the business and the content when it approves that
+ template, so an unapproved template is refused instead. `smart` is not exempt,
+ since it can route a contact to SMS or email. Drafts can be created, edited and
+ kept without any check. Every send path (dashboard, API and CLI) enforces the
+ same rule.
+
+ **Daily ceilings apply per recipient.** Each message a broadcast sends counts
+ against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling
+ is reached, the remaining recipients are marked `failed` with `errorCode`
+ `DAILY_LIMIT_EXCEEDED`; they are not retried the next day.
**Review depends on the channel, and cannot be bypassed.** A draft is submitted
to automated content review here; it does not go straight out. A WhatsApp
@@ -1021,12 +1031,22 @@ async def send(
"""
Start sending the broadcast immediately or schedule for later.
- **Verification is required to send, and there are two of them.** The team must
- have completed both identity verification (KYC) and business verification (KYB);
- passing one is not enough. Drafts can be created, edited and kept without
- either. Every send path — dashboard, API and CLI alike — enforces both,
- returning `403` with code `kyc_required` or `kyb_required` for whichever is
- outstanding.
+ **The account must be past the unverified level to send, except on WhatsApp.**
+ An account that has verified nothing is refused with `403` and code
+ `kyc_required` on every channel other than `whatsapp`. Any one of these lifts
+ it: identity verification (KYC), a saved payment method, a settled deposit, or a
+ paid plan. Business verification (KYB) is not required to broadcast; it gates
+ 10DLC registration only. A `whatsapp` broadcast is exempt: it can only be built
+ on a template, and Meta vets the business and the content when it approves that
+ template, so an unapproved template is refused instead. `smart` is not exempt,
+ since it can route a contact to SMS or email. Drafts can be created, edited and
+ kept without any check. Every send path (dashboard, API and CLI) enforces the
+ same rule.
+
+ **Daily ceilings apply per recipient.** Each message a broadcast sends counts
+ against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling
+ is reached, the remaining recipients are marked `failed` with `errorCode`
+ `DAILY_LIMIT_EXCEEDED`; they are not retried the next day.
**Review depends on the channel, and cannot be bypassed.** A draft is submitted
to automated content review here; it does not go straight out. A WhatsApp
diff --git a/src/zavudev/resources/calls.py b/src/zavudev/resources/calls.py
new file mode 100644
index 0000000..6e14482
--- /dev/null
+++ b/src/zavudev/resources/calls.py
@@ -0,0 +1,584 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Literal
+
+import httpx
+
+from ..types import call_list_params, call_create_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import path_template, maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..pagination import SyncCursor, AsyncCursor
+from .._base_client import AsyncPaginator, make_request_options
+from ..types.call_list_response import CallListResponse
+from ..types.call_create_response import CallCreateResponse
+from ..types.call_hangup_response import CallHangupResponse
+from ..types.call_retrieve_response import CallRetrieveResponse
+
+__all__ = ["CallsResource", "AsyncCallsResource"]
+
+
+class CallsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> CallsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return CallsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> CallsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return CallsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ to: str,
+ greeting: str | Omit = omit,
+ language: str | Omit = omit,
+ max_duration_minutes: int | Omit = omit,
+ metadata: Dict[str, str] | Omit = omit,
+ sender_id: str | 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,
+ ) -> CallCreateResponse:
+ """
+ Place an outbound voice call answered by the voice agent configured on the
+ sender. Zavu dials the recipient and runs the conversation through its managed
+ voice pipeline (speech recognition, the agent's LLM, and speech synthesis, with
+ real-time interruption handling).
+
+ **Requirements:**
+
+ - The Voice Agents feature must be enabled for your team (otherwise `403`).
+ - An account that has verified nothing may only call the phone numbers the
+ project has verified (`403` with code `destination_not_verified`, and
+ `details.verifiedNumbers` lists them), and at most 5 calls a day (`429` with
+ code `daily_limit_exceeded`). A number is verified from the dashboard's
+ Sandbox screen by sending the pre-filled WhatsApp message from that phone; the
+ same verification covers SMS and calls. Verify your identity, add a payment
+ method, settle a deposit or subscribe to call any destination. That raises the
+ ceiling to 50 calls a day on Free; paid plans have no daily call ceiling. Full
+ reference: https://docs.zavu.dev/concepts/sending-limits
+ - The sender's agent must have `voice.enabled` set to `true`.
+ - Not available with test-mode API keys.
+
+ **Billing:** Voice calls are billed per minute of connected time plus telephony,
+ deducted from your prepaid balance. A short-duration estimate is reserved when
+ the call is placed; you are charged for the actual duration when the call ends.
+
+ Args:
+ to: Recipient phone number in E.164 format.
+
+ greeting: Overrides the agent's configured greeting for this call only.
+
+ language: Language the agent speaks on this call only, as a BCP-47 tag (`en`, `es`,
+ `es-ES`, `pt-BR`), or `auto` to detect the caller's language and follow it.
+ Overrides the agent's configured language for speech recognition, the agent's
+ replies, and the synthesized voice. If the agent uses a custom voice you
+ supplied, that voice is kept and only the language changes. When omitted, the
+ agent's configured language is used.
+
+ max_duration_minutes: Overrides the agent's maximum call duration for this call only.
+
+ metadata: Arbitrary metadata to associate with the call. Returned on the call object and
+ included in voice webhooks.
+
+ sender_id: Sender profile that places the call. Uses the project's default sender if
+ omitted. The sender's agent must have voice enabled.
+
+ 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
+ """
+ return self._post(
+ "/v1/calls",
+ body=maybe_transform(
+ {
+ "to": to,
+ "greeting": greeting,
+ "language": language,
+ "max_duration_minutes": max_duration_minutes,
+ "metadata": metadata,
+ "sender_id": sender_id,
+ },
+ call_create_params.CallCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CallCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ call_id: str,
+ *,
+ # 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,
+ ) -> CallRetrieveResponse:
+ """
+ Retrieve a single voice call, including its full transcript once the
+ conversation has produced turns.
+
+ Args:
+ 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 call_id:
+ raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}")
+ return self._get(
+ path_template("/v1/calls/{call_id}", call_id=call_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CallRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ cursor: str | Omit = omit,
+ direction: Literal["inbound", "outbound"] | Omit = omit,
+ limit: int | Omit = omit,
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ | 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,
+ ) -> SyncCursor[CallListResponse]:
+ """List voice calls for this project, most recent first.
+
+ Transcripts are omitted
+ from the list; fetch a single call to get its transcript.
+
+ Args:
+ direction: Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+
+ status: Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/calls",
+ page=SyncCursor[CallListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "cursor": cursor,
+ "direction": direction,
+ "limit": limit,
+ "status": status,
+ },
+ call_list_params.CallListParams,
+ ),
+ ),
+ model=CallListResponse,
+ )
+
+ def hangup(
+ self,
+ call_id: str,
+ *,
+ # 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,
+ ) -> CallHangupResponse:
+ """End an active voice call.
+
+ The call must still be ringing or in progress. Not
+ available with test-mode API keys.
+
+ Args:
+ 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 call_id:
+ raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}")
+ return self._post(
+ path_template("/v1/calls/{call_id}/hangup", call_id=call_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CallHangupResponse,
+ )
+
+
+class AsyncCallsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncCallsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncCallsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncCallsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncCallsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ to: str,
+ greeting: str | Omit = omit,
+ language: str | Omit = omit,
+ max_duration_minutes: int | Omit = omit,
+ metadata: Dict[str, str] | Omit = omit,
+ sender_id: str | 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,
+ ) -> CallCreateResponse:
+ """
+ Place an outbound voice call answered by the voice agent configured on the
+ sender. Zavu dials the recipient and runs the conversation through its managed
+ voice pipeline (speech recognition, the agent's LLM, and speech synthesis, with
+ real-time interruption handling).
+
+ **Requirements:**
+
+ - The Voice Agents feature must be enabled for your team (otherwise `403`).
+ - An account that has verified nothing may only call the phone numbers the
+ project has verified (`403` with code `destination_not_verified`, and
+ `details.verifiedNumbers` lists them), and at most 5 calls a day (`429` with
+ code `daily_limit_exceeded`). A number is verified from the dashboard's
+ Sandbox screen by sending the pre-filled WhatsApp message from that phone; the
+ same verification covers SMS and calls. Verify your identity, add a payment
+ method, settle a deposit or subscribe to call any destination. That raises the
+ ceiling to 50 calls a day on Free; paid plans have no daily call ceiling. Full
+ reference: https://docs.zavu.dev/concepts/sending-limits
+ - The sender's agent must have `voice.enabled` set to `true`.
+ - Not available with test-mode API keys.
+
+ **Billing:** Voice calls are billed per minute of connected time plus telephony,
+ deducted from your prepaid balance. A short-duration estimate is reserved when
+ the call is placed; you are charged for the actual duration when the call ends.
+
+ Args:
+ to: Recipient phone number in E.164 format.
+
+ greeting: Overrides the agent's configured greeting for this call only.
+
+ language: Language the agent speaks on this call only, as a BCP-47 tag (`en`, `es`,
+ `es-ES`, `pt-BR`), or `auto` to detect the caller's language and follow it.
+ Overrides the agent's configured language for speech recognition, the agent's
+ replies, and the synthesized voice. If the agent uses a custom voice you
+ supplied, that voice is kept and only the language changes. When omitted, the
+ agent's configured language is used.
+
+ max_duration_minutes: Overrides the agent's maximum call duration for this call only.
+
+ metadata: Arbitrary metadata to associate with the call. Returned on the call object and
+ included in voice webhooks.
+
+ sender_id: Sender profile that places the call. Uses the project's default sender if
+ omitted. The sender's agent must have voice enabled.
+
+ 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
+ """
+ return await self._post(
+ "/v1/calls",
+ body=await async_maybe_transform(
+ {
+ "to": to,
+ "greeting": greeting,
+ "language": language,
+ "max_duration_minutes": max_duration_minutes,
+ "metadata": metadata,
+ "sender_id": sender_id,
+ },
+ call_create_params.CallCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CallCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ call_id: str,
+ *,
+ # 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,
+ ) -> CallRetrieveResponse:
+ """
+ Retrieve a single voice call, including its full transcript once the
+ conversation has produced turns.
+
+ Args:
+ 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 call_id:
+ raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}")
+ return await self._get(
+ path_template("/v1/calls/{call_id}", call_id=call_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CallRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ cursor: str | Omit = omit,
+ direction: Literal["inbound", "outbound"] | Omit = omit,
+ limit: int | Omit = omit,
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ | 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,
+ ) -> AsyncPaginator[CallListResponse, AsyncCursor[CallListResponse]]:
+ """List voice calls for this project, most recent first.
+
+ Transcripts are omitted
+ from the list; fetch a single call to get its transcript.
+
+ Args:
+ direction: Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+
+ status: Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/calls",
+ page=AsyncCursor[CallListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "cursor": cursor,
+ "direction": direction,
+ "limit": limit,
+ "status": status,
+ },
+ call_list_params.CallListParams,
+ ),
+ ),
+ model=CallListResponse,
+ )
+
+ async def hangup(
+ self,
+ call_id: str,
+ *,
+ # 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,
+ ) -> CallHangupResponse:
+ """End an active voice call.
+
+ The call must still be ringing or in progress. Not
+ available with test-mode API keys.
+
+ Args:
+ 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 call_id:
+ raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}")
+ return await self._post(
+ path_template("/v1/calls/{call_id}/hangup", call_id=call_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CallHangupResponse,
+ )
+
+
+class CallsResourceWithRawResponse:
+ def __init__(self, calls: CallsResource) -> None:
+ self._calls = calls
+
+ self.create = to_raw_response_wrapper(
+ calls.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ calls.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ calls.list,
+ )
+ self.hangup = to_raw_response_wrapper(
+ calls.hangup,
+ )
+
+
+class AsyncCallsResourceWithRawResponse:
+ def __init__(self, calls: AsyncCallsResource) -> None:
+ self._calls = calls
+
+ self.create = async_to_raw_response_wrapper(
+ calls.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ calls.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ calls.list,
+ )
+ self.hangup = async_to_raw_response_wrapper(
+ calls.hangup,
+ )
+
+
+class CallsResourceWithStreamingResponse:
+ def __init__(self, calls: CallsResource) -> None:
+ self._calls = calls
+
+ self.create = to_streamed_response_wrapper(
+ calls.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ calls.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ calls.list,
+ )
+ self.hangup = to_streamed_response_wrapper(
+ calls.hangup,
+ )
+
+
+class AsyncCallsResourceWithStreamingResponse:
+ def __init__(self, calls: AsyncCallsResource) -> None:
+ self._calls = calls
+
+ self.create = async_to_streamed_response_wrapper(
+ calls.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ calls.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ calls.list,
+ )
+ self.hangup = async_to_streamed_response_wrapper(
+ calls.hangup,
+ )
diff --git a/src/zavudev/resources/contacts/contacts.py b/src/zavudev/resources/contacts/contacts.py
index bd707cc..37a1e6c 100644
--- a/src/zavudev/resources/contacts/contacts.py
+++ b/src/zavudev/resources/contacts/contacts.py
@@ -8,7 +8,7 @@
import httpx
from ...types import contact_list_params, contact_merge_params, contact_create_params, contact_update_params
-from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
from ..._utils import path_template, maybe_transform, async_maybe_transform
from .channels import (
ChannelsResource,
@@ -143,6 +143,7 @@ def update(
*,
default_channel: Optional[Literal["sms", "whatsapp", "telegram", "email", "instagram", "messenger", "voice"]]
| Omit = omit,
+ display_name: Optional[str] | Omit = omit,
metadata: Dict[str, str] | 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.
@@ -158,6 +159,10 @@ def update(
Set to null to clear.
+ display_name: Human-readable name for this contact. Set to null to clear it and fall back to
+ the contact's identifier. Contacts created automatically from an inbound message
+ have no display name until you set one.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -173,6 +178,7 @@ def update(
body=maybe_transform(
{
"default_channel": default_channel,
+ "display_name": display_name,
"metadata": metadata,
},
contact_update_params.ContactUpdateParams,
@@ -189,6 +195,8 @@ def list(
cursor: str | Omit = omit,
limit: int | Omit = omit,
phone_number: str | Omit = omit,
+ search: str | Omit = omit,
+ tag: SequenceNotStr[str] | 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,
@@ -200,6 +208,29 @@ def list(
List contacts with their communication channels.
Args:
+ cursor: Opaque cursor from a previous response's `nextCursor`. Do not construct it.
+
+ phone_number: Exact match on the contact's primary phone number, in E.164.
+
+ search: Free-text match over the contact's name (`displayName` and the WhatsApp profile
+ name), phone numbers and email addresses. Case- and accent-insensitive. A phone
+ number matches on a trailing fragment too, so `5551234` finds `+14155551234`.
+
+ Contacts created automatically from an inbound message have no `displayName` —
+ they are matched by their identifier until you set one with
+ `PATCH /v1/contacts/{contactId}`.
+
+ Results come back in relevance order rather than newest-first. `cursor` is
+ opaque in both modes; pass back exactly what the previous response returned, and
+ start a new pagination run when the search term changes.
+
+ tag: Tag name. Repeatable: `?tag=vip&tag=chile` returns contacts carrying **every**
+ tag given, not any of them — the same rule the dashboard filter applies.
+
+ Tags are matched by name, case-insensitively. An unknown tag returns 400 rather
+ than being ignored, because a typo that silently matched every contact would be
+ a worse answer than an error.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -221,6 +252,8 @@ def list(
"cursor": cursor,
"limit": limit,
"phone_number": phone_number,
+ "search": search,
+ "tag": tag,
},
contact_list_params.ContactListParams,
),
@@ -268,40 +301,6 @@ def delete(
cast_to=NoneType,
)
- def dismiss_merge_suggestion(
- self,
- contact_id: str,
- *,
- # 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,
- ) -> None:
- """
- Dismiss the merge suggestion for a contact.
-
- Args:
- 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 contact_id:
- raise ValueError(f"Expected a non-empty value for `contact_id` but received {contact_id!r}")
- extra_headers = {"Accept": "*/*", **(extra_headers or {})}
- return self._delete(
- path_template("/v1/contacts/{contact_id}/merge-suggestion", contact_id=contact_id),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=NoneType,
- )
-
def merge(
self,
contact_id: str,
@@ -487,6 +486,7 @@ async def update(
*,
default_channel: Optional[Literal["sms", "whatsapp", "telegram", "email", "instagram", "messenger", "voice"]]
| Omit = omit,
+ display_name: Optional[str] | Omit = omit,
metadata: Dict[str, str] | 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.
@@ -502,6 +502,10 @@ async def update(
Set to null to clear.
+ display_name: Human-readable name for this contact. Set to null to clear it and fall back to
+ the contact's identifier. Contacts created automatically from an inbound message
+ have no display name until you set one.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -517,6 +521,7 @@ async def update(
body=await async_maybe_transform(
{
"default_channel": default_channel,
+ "display_name": display_name,
"metadata": metadata,
},
contact_update_params.ContactUpdateParams,
@@ -533,6 +538,8 @@ def list(
cursor: str | Omit = omit,
limit: int | Omit = omit,
phone_number: str | Omit = omit,
+ search: str | Omit = omit,
+ tag: SequenceNotStr[str] | 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,
@@ -544,6 +551,29 @@ def list(
List contacts with their communication channels.
Args:
+ cursor: Opaque cursor from a previous response's `nextCursor`. Do not construct it.
+
+ phone_number: Exact match on the contact's primary phone number, in E.164.
+
+ search: Free-text match over the contact's name (`displayName` and the WhatsApp profile
+ name), phone numbers and email addresses. Case- and accent-insensitive. A phone
+ number matches on a trailing fragment too, so `5551234` finds `+14155551234`.
+
+ Contacts created automatically from an inbound message have no `displayName` —
+ they are matched by their identifier until you set one with
+ `PATCH /v1/contacts/{contactId}`.
+
+ Results come back in relevance order rather than newest-first. `cursor` is
+ opaque in both modes; pass back exactly what the previous response returned, and
+ start a new pagination run when the search term changes.
+
+ tag: Tag name. Repeatable: `?tag=vip&tag=chile` returns contacts carrying **every**
+ tag given, not any of them — the same rule the dashboard filter applies.
+
+ Tags are matched by name, case-insensitively. An unknown tag returns 400 rather
+ than being ignored, because a typo that silently matched every contact would be
+ a worse answer than an error.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -565,6 +595,8 @@ def list(
"cursor": cursor,
"limit": limit,
"phone_number": phone_number,
+ "search": search,
+ "tag": tag,
},
contact_list_params.ContactListParams,
),
@@ -612,40 +644,6 @@ async def delete(
cast_to=NoneType,
)
- async def dismiss_merge_suggestion(
- self,
- contact_id: str,
- *,
- # 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,
- ) -> None:
- """
- Dismiss the merge suggestion for a contact.
-
- Args:
- 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 contact_id:
- raise ValueError(f"Expected a non-empty value for `contact_id` but received {contact_id!r}")
- extra_headers = {"Accept": "*/*", **(extra_headers or {})}
- return await self._delete(
- path_template("/v1/contacts/{contact_id}/merge-suggestion", contact_id=contact_id),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=NoneType,
- )
-
async def merge(
self,
contact_id: str,
@@ -742,9 +740,6 @@ def __init__(self, contacts: ContactsResource) -> None:
self.delete = to_raw_response_wrapper(
contacts.delete,
)
- self.dismiss_merge_suggestion = to_raw_response_wrapper(
- contacts.dismiss_merge_suggestion,
- )
self.merge = to_raw_response_wrapper(
contacts.merge,
)
@@ -776,9 +771,6 @@ def __init__(self, contacts: AsyncContactsResource) -> None:
self.delete = async_to_raw_response_wrapper(
contacts.delete,
)
- self.dismiss_merge_suggestion = async_to_raw_response_wrapper(
- contacts.dismiss_merge_suggestion,
- )
self.merge = async_to_raw_response_wrapper(
contacts.merge,
)
@@ -810,9 +802,6 @@ def __init__(self, contacts: ContactsResource) -> None:
self.delete = to_streamed_response_wrapper(
contacts.delete,
)
- self.dismiss_merge_suggestion = to_streamed_response_wrapper(
- contacts.dismiss_merge_suggestion,
- )
self.merge = to_streamed_response_wrapper(
contacts.merge,
)
@@ -844,9 +833,6 @@ def __init__(self, contacts: AsyncContactsResource) -> None:
self.delete = async_to_streamed_response_wrapper(
contacts.delete,
)
- self.dismiss_merge_suggestion = async_to_streamed_response_wrapper(
- contacts.dismiss_merge_suggestion,
- )
self.merge = async_to_streamed_response_wrapper(
contacts.merge,
)
diff --git a/src/zavudev/resources/conversations.py b/src/zavudev/resources/conversations.py
new file mode 100644
index 0000000..e29a4f8
--- /dev/null
+++ b/src/zavudev/resources/conversations.py
@@ -0,0 +1,531 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from ..types import conversation_list_params, conversation_list_messages_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import path_template, maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..pagination import SyncCursor, AsyncCursor
+from .._base_client import AsyncPaginator, make_request_options
+from ..types.message import Message
+from ..types.conversation_list_response import ConversationListResponse
+from ..types.conversation_retrieve_response import ConversationRetrieveResponse
+from ..types.conversation_mark_as_read_response import ConversationMarkAsReadResponse
+
+__all__ = ["ConversationsResource", "AsyncConversationsResource"]
+
+
+class ConversationsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ConversationsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return ConversationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ConversationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return ConversationsResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ conversation_id: str,
+ *,
+ # 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,
+ ) -> ConversationRetrieveResponse:
+ """
+ Get conversation
+
+ Args:
+ 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 conversation_id:
+ raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}")
+ return self._get(
+ path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ConversationRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ channel: Literal["sms", "sms_oneway", "whatsapp", "email", "telegram", "instagram", "messenger", "voice"]
+ | Omit = omit,
+ cursor: str | Omit = omit,
+ limit: int | Omit = omit,
+ search: str | Omit = omit,
+ sender_id: str | 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,
+ ) -> SyncCursor[ConversationListResponse]:
+ """List inbox threads, most recently active first.
+
+ A conversation groups every
+ message with one contact across channels, which is what you need to build an
+ inbox: `GET /v1/messages` returns a flat log with no thread to hang it on.
+
+ Use `senderId` to scope the list to a single number, and `channel` to keep only
+ threads that have carried that channel.
+
+ Args:
+ channel: Keep only threads that have carried this channel.
+
+ cursor: Opaque cursor from a previous response's `nextCursor`. Do not construct it.
+
+ search: Search threads by identity: phone number (any format — `+1 (555) 123-4567` and
+ `15551234567` both match), email address (full or local part), WhatsApp group
+ subject, WhatsApp username, or BSUID. Matching is by whole word, with prefix
+ matching on the last term, so `mar` finds `maria@example.com` and `+1555` finds
+ `+15551234567`; a fragment from the middle or end of a number (`4567`) does not
+ match.
+
+ It does **not** search message bodies — only who the thread is with.
+
+ Results come back ranked by relevance rather than by recency, so the usual "most
+ recently active first" ordering does not apply while `q` is set. `senderId` and
+ `channel` still narrow the results, and `cursor` paginates them as usual. An
+ empty or whitespace-only `q` returns no items rather than the full list.
+
+ sender_id: Keep only threads last handled by this sender.
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/conversations",
+ page=SyncCursor[ConversationListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "channel": channel,
+ "cursor": cursor,
+ "limit": limit,
+ "search": search,
+ "sender_id": sender_id,
+ },
+ conversation_list_params.ConversationListParams,
+ ),
+ ),
+ model=ConversationListResponse,
+ )
+
+ def list_messages(
+ self,
+ conversation_id: str,
+ *,
+ cursor: str | Omit = omit,
+ limit: int | 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,
+ ) -> SyncCursor[Message]:
+ """
+ Messages in this thread, newest first, across every channel it has carried.
+ Reply with `POST /v1/messages`, passing the conversation's `senderId` as the
+ `Zavu-Sender` header so the answer leaves from the number the contact already
+ knows.
+
+ Args:
+ cursor: Opaque cursor from a previous response's `nextCursor`.
+
+ 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 conversation_id:
+ raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}")
+ return self._get_api_list(
+ path_template("/v1/conversations/{conversation_id}/messages", conversation_id=conversation_id),
+ page=SyncCursor[Message],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "cursor": cursor,
+ "limit": limit,
+ },
+ conversation_list_messages_params.ConversationListMessagesParams,
+ ),
+ ),
+ model=Message,
+ )
+
+ def mark_as_read(
+ self,
+ conversation_id: str,
+ *,
+ # 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,
+ ) -> ConversationMarkAsReadResponse:
+ """Reset the thread's `unreadCount` to zero.
+
+ Marks the thread read in your own
+ inbox only: it does not send a read receipt to the contact.
+
+ Args:
+ 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 conversation_id:
+ raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}")
+ return self._post(
+ path_template("/v1/conversations/{conversation_id}/read", conversation_id=conversation_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ConversationMarkAsReadResponse,
+ )
+
+
+class AsyncConversationsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncConversationsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncConversationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncConversationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncConversationsResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ conversation_id: str,
+ *,
+ # 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,
+ ) -> ConversationRetrieveResponse:
+ """
+ Get conversation
+
+ Args:
+ 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 conversation_id:
+ raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}")
+ return await self._get(
+ path_template("/v1/conversations/{conversation_id}", conversation_id=conversation_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ConversationRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ channel: Literal["sms", "sms_oneway", "whatsapp", "email", "telegram", "instagram", "messenger", "voice"]
+ | Omit = omit,
+ cursor: str | Omit = omit,
+ limit: int | Omit = omit,
+ search: str | Omit = omit,
+ sender_id: str | 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,
+ ) -> AsyncPaginator[ConversationListResponse, AsyncCursor[ConversationListResponse]]:
+ """List inbox threads, most recently active first.
+
+ A conversation groups every
+ message with one contact across channels, which is what you need to build an
+ inbox: `GET /v1/messages` returns a flat log with no thread to hang it on.
+
+ Use `senderId` to scope the list to a single number, and `channel` to keep only
+ threads that have carried that channel.
+
+ Args:
+ channel: Keep only threads that have carried this channel.
+
+ cursor: Opaque cursor from a previous response's `nextCursor`. Do not construct it.
+
+ search: Search threads by identity: phone number (any format — `+1 (555) 123-4567` and
+ `15551234567` both match), email address (full or local part), WhatsApp group
+ subject, WhatsApp username, or BSUID. Matching is by whole word, with prefix
+ matching on the last term, so `mar` finds `maria@example.com` and `+1555` finds
+ `+15551234567`; a fragment from the middle or end of a number (`4567`) does not
+ match.
+
+ It does **not** search message bodies — only who the thread is with.
+
+ Results come back ranked by relevance rather than by recency, so the usual "most
+ recently active first" ordering does not apply while `q` is set. `senderId` and
+ `channel` still narrow the results, and `cursor` paginates them as usual. An
+ empty or whitespace-only `q` returns no items rather than the full list.
+
+ sender_id: Keep only threads last handled by this sender.
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/conversations",
+ page=AsyncCursor[ConversationListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "channel": channel,
+ "cursor": cursor,
+ "limit": limit,
+ "search": search,
+ "sender_id": sender_id,
+ },
+ conversation_list_params.ConversationListParams,
+ ),
+ ),
+ model=ConversationListResponse,
+ )
+
+ def list_messages(
+ self,
+ conversation_id: str,
+ *,
+ cursor: str | Omit = omit,
+ limit: int | 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,
+ ) -> AsyncPaginator[Message, AsyncCursor[Message]]:
+ """
+ Messages in this thread, newest first, across every channel it has carried.
+ Reply with `POST /v1/messages`, passing the conversation's `senderId` as the
+ `Zavu-Sender` header so the answer leaves from the number the contact already
+ knows.
+
+ Args:
+ cursor: Opaque cursor from a previous response's `nextCursor`.
+
+ 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 conversation_id:
+ raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}")
+ return self._get_api_list(
+ path_template("/v1/conversations/{conversation_id}/messages", conversation_id=conversation_id),
+ page=AsyncCursor[Message],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "cursor": cursor,
+ "limit": limit,
+ },
+ conversation_list_messages_params.ConversationListMessagesParams,
+ ),
+ ),
+ model=Message,
+ )
+
+ async def mark_as_read(
+ self,
+ conversation_id: str,
+ *,
+ # 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,
+ ) -> ConversationMarkAsReadResponse:
+ """Reset the thread's `unreadCount` to zero.
+
+ Marks the thread read in your own
+ inbox only: it does not send a read receipt to the contact.
+
+ Args:
+ 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 conversation_id:
+ raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}")
+ return await self._post(
+ path_template("/v1/conversations/{conversation_id}/read", conversation_id=conversation_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ConversationMarkAsReadResponse,
+ )
+
+
+class ConversationsResourceWithRawResponse:
+ def __init__(self, conversations: ConversationsResource) -> None:
+ self._conversations = conversations
+
+ self.retrieve = to_raw_response_wrapper(
+ conversations.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ conversations.list,
+ )
+ self.list_messages = to_raw_response_wrapper(
+ conversations.list_messages,
+ )
+ self.mark_as_read = to_raw_response_wrapper(
+ conversations.mark_as_read,
+ )
+
+
+class AsyncConversationsResourceWithRawResponse:
+ def __init__(self, conversations: AsyncConversationsResource) -> None:
+ self._conversations = conversations
+
+ self.retrieve = async_to_raw_response_wrapper(
+ conversations.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ conversations.list,
+ )
+ self.list_messages = async_to_raw_response_wrapper(
+ conversations.list_messages,
+ )
+ self.mark_as_read = async_to_raw_response_wrapper(
+ conversations.mark_as_read,
+ )
+
+
+class ConversationsResourceWithStreamingResponse:
+ def __init__(self, conversations: ConversationsResource) -> None:
+ self._conversations = conversations
+
+ self.retrieve = to_streamed_response_wrapper(
+ conversations.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ conversations.list,
+ )
+ self.list_messages = to_streamed_response_wrapper(
+ conversations.list_messages,
+ )
+ self.mark_as_read = to_streamed_response_wrapper(
+ conversations.mark_as_read,
+ )
+
+
+class AsyncConversationsResourceWithStreamingResponse:
+ def __init__(self, conversations: AsyncConversationsResource) -> None:
+ self._conversations = conversations
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ conversations.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ conversations.list,
+ )
+ self.list_messages = async_to_streamed_response_wrapper(
+ conversations.list_messages,
+ )
+ self.mark_as_read = async_to_streamed_response_wrapper(
+ conversations.mark_as_read,
+ )
diff --git a/src/zavudev/resources/email_domains.py b/src/zavudev/resources/email_domains.py
new file mode 100644
index 0000000..5234bdc
--- /dev/null
+++ b/src/zavudev/resources/email_domains.py
@@ -0,0 +1,462 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..types import email_domain_create_params
+from .._types import Body, Query, Headers, NoneType, NotGiven, not_given
+from .._utils import path_template, maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.email_domain_list_response import EmailDomainListResponse
+from ..types.email_domain_create_response import EmailDomainCreateResponse
+from ..types.email_domain_verify_response import EmailDomainVerifyResponse
+from ..types.email_domain_retrieve_response import EmailDomainRetrieveResponse
+
+__all__ = ["EmailDomainsResource", "AsyncEmailDomainsResource"]
+
+
+class EmailDomainsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> EmailDomainsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return EmailDomainsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> EmailDomainsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return EmailDomainsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ domain: str,
+ # 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,
+ ) -> EmailDomainCreateResponse:
+ """Add a domain to send email from.
+
+ Returns the DNS records to publish (DKIM CNAMEs
+ are required; SPF, DMARC, and MAIL FROM are recommended). Publish them at your
+ DNS provider, then verify.
+
+ Args:
+ domain: Bare domain, e.g. example.com.
+
+ 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
+ """
+ return self._post(
+ "/v1/email-domains",
+ body=maybe_transform({"domain": domain}, email_domain_create_params.EmailDomainCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ domain_id: str,
+ *,
+ # 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,
+ ) -> EmailDomainRetrieveResponse:
+ """
+ Fetch a domain with its DNS records and current status.
+
+ Args:
+ 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 domain_id:
+ raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
+ return self._get(
+ path_template("/v1/email-domains/{domain_id}", domain_id=domain_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ # 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,
+ ) -> EmailDomainListResponse:
+ """List email domains"""
+ return self._get(
+ "/v1/email-domains",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainListResponse,
+ )
+
+ def delete(
+ self,
+ domain_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Remove an email domain
+
+ Args:
+ 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 domain_id:
+ raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/v1/email-domains/{domain_id}", domain_id=domain_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def verify(
+ self,
+ domain_id: str,
+ *,
+ # 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,
+ ) -> EmailDomainVerifyResponse:
+ """
+ Re-check the domain's published DNS records and refresh its status.
+
+ Args:
+ 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 domain_id:
+ raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
+ return self._post(
+ path_template("/v1/email-domains/{domain_id}/verify", domain_id=domain_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainVerifyResponse,
+ )
+
+
+class AsyncEmailDomainsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncEmailDomainsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncEmailDomainsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncEmailDomainsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncEmailDomainsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ domain: str,
+ # 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,
+ ) -> EmailDomainCreateResponse:
+ """Add a domain to send email from.
+
+ Returns the DNS records to publish (DKIM CNAMEs
+ are required; SPF, DMARC, and MAIL FROM are recommended). Publish them at your
+ DNS provider, then verify.
+
+ Args:
+ domain: Bare domain, e.g. example.com.
+
+ 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
+ """
+ return await self._post(
+ "/v1/email-domains",
+ body=await async_maybe_transform({"domain": domain}, email_domain_create_params.EmailDomainCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ domain_id: str,
+ *,
+ # 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,
+ ) -> EmailDomainRetrieveResponse:
+ """
+ Fetch a domain with its DNS records and current status.
+
+ Args:
+ 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 domain_id:
+ raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
+ return await self._get(
+ path_template("/v1/email-domains/{domain_id}", domain_id=domain_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainRetrieveResponse,
+ )
+
+ async def list(
+ self,
+ *,
+ # 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,
+ ) -> EmailDomainListResponse:
+ """List email domains"""
+ return await self._get(
+ "/v1/email-domains",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainListResponse,
+ )
+
+ async def delete(
+ self,
+ domain_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Remove an email domain
+
+ Args:
+ 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 domain_id:
+ raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/v1/email-domains/{domain_id}", domain_id=domain_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def verify(
+ self,
+ domain_id: str,
+ *,
+ # 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,
+ ) -> EmailDomainVerifyResponse:
+ """
+ Re-check the domain's published DNS records and refresh its status.
+
+ Args:
+ 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 domain_id:
+ raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
+ return await self._post(
+ path_template("/v1/email-domains/{domain_id}/verify", domain_id=domain_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailDomainVerifyResponse,
+ )
+
+
+class EmailDomainsResourceWithRawResponse:
+ def __init__(self, email_domains: EmailDomainsResource) -> None:
+ self._email_domains = email_domains
+
+ self.create = to_raw_response_wrapper(
+ email_domains.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ email_domains.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ email_domains.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ email_domains.delete,
+ )
+ self.verify = to_raw_response_wrapper(
+ email_domains.verify,
+ )
+
+
+class AsyncEmailDomainsResourceWithRawResponse:
+ def __init__(self, email_domains: AsyncEmailDomainsResource) -> None:
+ self._email_domains = email_domains
+
+ self.create = async_to_raw_response_wrapper(
+ email_domains.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ email_domains.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ email_domains.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ email_domains.delete,
+ )
+ self.verify = async_to_raw_response_wrapper(
+ email_domains.verify,
+ )
+
+
+class EmailDomainsResourceWithStreamingResponse:
+ def __init__(self, email_domains: EmailDomainsResource) -> None:
+ self._email_domains = email_domains
+
+ self.create = to_streamed_response_wrapper(
+ email_domains.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ email_domains.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ email_domains.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ email_domains.delete,
+ )
+ self.verify = to_streamed_response_wrapper(
+ email_domains.verify,
+ )
+
+
+class AsyncEmailDomainsResourceWithStreamingResponse:
+ def __init__(self, email_domains: AsyncEmailDomainsResource) -> None:
+ self._email_domains = email_domains
+
+ self.create = async_to_streamed_response_wrapper(
+ email_domains.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ email_domains.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ email_domains.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ email_domains.delete,
+ )
+ self.verify = async_to_streamed_response_wrapper(
+ email_domains.verify,
+ )
diff --git a/src/zavudev/resources/functions/__init__.py b/src/zavudev/resources/functions/__init__.py
index 26b1e54..36edcf8 100644
--- a/src/zavudev/resources/functions/__init__.py
+++ b/src/zavudev/resources/functions/__init__.py
@@ -8,6 +8,22 @@
SecretsResourceWithStreamingResponse,
AsyncSecretsResourceWithStreamingResponse,
)
+from .git_link import (
+ GitLinkResource,
+ AsyncGitLinkResource,
+ GitLinkResourceWithRawResponse,
+ AsyncGitLinkResourceWithRawResponse,
+ GitLinkResourceWithStreamingResponse,
+ AsyncGitLinkResourceWithStreamingResponse,
+)
+from .triggers import (
+ TriggersResource,
+ AsyncTriggersResource,
+ TriggersResourceWithRawResponse,
+ AsyncTriggersResourceWithRawResponse,
+ TriggersResourceWithStreamingResponse,
+ AsyncTriggersResourceWithStreamingResponse,
+)
from .functions import (
FunctionsResource,
AsyncFunctionsResource,
@@ -24,6 +40,18 @@
"AsyncSecretsResourceWithRawResponse",
"SecretsResourceWithStreamingResponse",
"AsyncSecretsResourceWithStreamingResponse",
+ "TriggersResource",
+ "AsyncTriggersResource",
+ "TriggersResourceWithRawResponse",
+ "AsyncTriggersResourceWithRawResponse",
+ "TriggersResourceWithStreamingResponse",
+ "AsyncTriggersResourceWithStreamingResponse",
+ "GitLinkResource",
+ "AsyncGitLinkResource",
+ "GitLinkResourceWithRawResponse",
+ "AsyncGitLinkResourceWithRawResponse",
+ "GitLinkResourceWithStreamingResponse",
+ "AsyncGitLinkResourceWithStreamingResponse",
"FunctionsResource",
"AsyncFunctionsResource",
"FunctionsResourceWithRawResponse",
diff --git a/src/zavudev/resources/functions/functions.py b/src/zavudev/resources/functions/functions.py
index 5b5e86f..1dc58c7 100644
--- a/src/zavudev/resources/functions/functions.py
+++ b/src/zavudev/resources/functions/functions.py
@@ -7,7 +7,14 @@
import httpx
-from ...types import function_create_params, function_deploy_params, function_update_params, function_tail_logs_params
+from ...types import (
+ function_create_params,
+ function_deploy_params,
+ function_update_params,
+ function_tail_logs_params,
+ function_list_deployments_params,
+ function_rollback_deployment_params,
+)
from .secrets import (
SecretsResource,
AsyncSecretsResource,
@@ -18,6 +25,22 @@
)
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from ..._utils import path_template, maybe_transform, async_maybe_transform
+from .git_link import (
+ GitLinkResource,
+ AsyncGitLinkResource,
+ GitLinkResourceWithRawResponse,
+ AsyncGitLinkResourceWithRawResponse,
+ GitLinkResourceWithStreamingResponse,
+ AsyncGitLinkResourceWithStreamingResponse,
+)
+from .triggers import (
+ TriggersResource,
+ AsyncTriggersResource,
+ TriggersResourceWithRawResponse,
+ AsyncTriggersResourceWithRawResponse,
+ TriggersResourceWithStreamingResponse,
+ AsyncTriggersResourceWithStreamingResponse,
+)
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -34,6 +57,9 @@
from ...types.function_retrieve_response import FunctionRetrieveResponse
from ...types.function_tail_logs_response import FunctionTailLogsResponse
from ...types.function_get_deployment_response import FunctionGetDeploymentResponse
+from ...types.function_list_deployments_response import FunctionListDeploymentsResponse
+from ...types.function_list_event_types_response import FunctionListEventTypesResponse
+from ...types.function_rollback_deployment_response import FunctionRollbackDeploymentResponse
__all__ = ["FunctionsResource", "AsyncFunctionsResource"]
@@ -43,6 +69,14 @@ class FunctionsResource(SyncAPIResource):
def secrets(self) -> SecretsResource:
return SecretsResource(self._client)
+ @cached_property
+ def triggers(self) -> TriggersResource:
+ return TriggersResource(self._client)
+
+ @cached_property
+ def git_link(self) -> GitLinkResource:
+ return GitLinkResource(self._client)
+
@cached_property
def with_raw_response(self) -> FunctionsResourceWithRawResponse:
"""
@@ -395,6 +429,113 @@ def get_deployment(
cast_to=FunctionGetDeploymentResponse,
)
+ def list_deployments(
+ self,
+ function_id: str,
+ *,
+ limit: int | 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,
+ ) -> FunctionListDeploymentsResponse:
+ """List a function's deployment history, newest first.
+
+ Source code is omitted;
+ fetch a single deployment via GET /v1/functions/deployments/{deploymentId} for
+ full details.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._get(
+ path_template("/v1/functions/{function_id}/deployments", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"limit": limit}, function_list_deployments_params.FunctionListDeploymentsParams),
+ ),
+ cast_to=FunctionListDeploymentsResponse,
+ )
+
+ def list_event_types(
+ self,
+ *,
+ # 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,
+ ) -> FunctionListEventTypesResponse:
+ """List the event types a function trigger can subscribe to.
+
+ Includes the special
+ type `cron`, which fires on a schedule (see POST
+ /v1/functions/{functionId}/triggers) rather than on a messaging event.
+ """
+ return self._get(
+ "/v1/functions/event-types",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FunctionListEventTypesResponse,
+ )
+
+ def rollback_deployment(
+ self,
+ function_id: str,
+ *,
+ deployment_id: str,
+ # 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,
+ ) -> FunctionRollbackDeploymentResponse:
+ """
+ Re-deploy a previous version by copying its source, dependencies, and runtime
+ pin onto the function's draft, then deploying. Returns immediately with a
+ deployment ID — poll GET /v1/functions/deployments/{deploymentId} until status
+ is active or failed. Secrets are not rolled back.
+
+ Args:
+ deployment_id: ID of the deployment to roll back to.
+
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._post(
+ path_template("/v1/functions/{function_id}/rollback", function_id=function_id),
+ body=maybe_transform(
+ {"deployment_id": deployment_id}, function_rollback_deployment_params.FunctionRollbackDeploymentParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FunctionRollbackDeploymentResponse,
+ )
+
def tail_logs(
self,
function_id: str,
@@ -459,6 +600,14 @@ class AsyncFunctionsResource(AsyncAPIResource):
def secrets(self) -> AsyncSecretsResource:
return AsyncSecretsResource(self._client)
+ @cached_property
+ def triggers(self) -> AsyncTriggersResource:
+ return AsyncTriggersResource(self._client)
+
+ @cached_property
+ def git_link(self) -> AsyncGitLinkResource:
+ return AsyncGitLinkResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncFunctionsResourceWithRawResponse:
"""
@@ -811,6 +960,115 @@ async def get_deployment(
cast_to=FunctionGetDeploymentResponse,
)
+ async def list_deployments(
+ self,
+ function_id: str,
+ *,
+ limit: int | 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,
+ ) -> FunctionListDeploymentsResponse:
+ """List a function's deployment history, newest first.
+
+ Source code is omitted;
+ fetch a single deployment via GET /v1/functions/deployments/{deploymentId} for
+ full details.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._get(
+ path_template("/v1/functions/{function_id}/deployments", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"limit": limit}, function_list_deployments_params.FunctionListDeploymentsParams
+ ),
+ ),
+ cast_to=FunctionListDeploymentsResponse,
+ )
+
+ async def list_event_types(
+ self,
+ *,
+ # 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,
+ ) -> FunctionListEventTypesResponse:
+ """List the event types a function trigger can subscribe to.
+
+ Includes the special
+ type `cron`, which fires on a schedule (see POST
+ /v1/functions/{functionId}/triggers) rather than on a messaging event.
+ """
+ return await self._get(
+ "/v1/functions/event-types",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FunctionListEventTypesResponse,
+ )
+
+ async def rollback_deployment(
+ self,
+ function_id: str,
+ *,
+ deployment_id: str,
+ # 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,
+ ) -> FunctionRollbackDeploymentResponse:
+ """
+ Re-deploy a previous version by copying its source, dependencies, and runtime
+ pin onto the function's draft, then deploying. Returns immediately with a
+ deployment ID — poll GET /v1/functions/deployments/{deploymentId} until status
+ is active or failed. Secrets are not rolled back.
+
+ Args:
+ deployment_id: ID of the deployment to roll back to.
+
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._post(
+ path_template("/v1/functions/{function_id}/rollback", function_id=function_id),
+ body=await async_maybe_transform(
+ {"deployment_id": deployment_id}, function_rollback_deployment_params.FunctionRollbackDeploymentParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FunctionRollbackDeploymentResponse,
+ )
+
async def tail_logs(
self,
function_id: str,
@@ -892,6 +1150,15 @@ def __init__(self, functions: FunctionsResource) -> None:
self.get_deployment = to_raw_response_wrapper(
functions.get_deployment,
)
+ self.list_deployments = to_raw_response_wrapper(
+ functions.list_deployments,
+ )
+ self.list_event_types = to_raw_response_wrapper(
+ functions.list_event_types,
+ )
+ self.rollback_deployment = to_raw_response_wrapper(
+ functions.rollback_deployment,
+ )
self.tail_logs = to_raw_response_wrapper(
functions.tail_logs,
)
@@ -900,6 +1167,14 @@ def __init__(self, functions: FunctionsResource) -> None:
def secrets(self) -> SecretsResourceWithRawResponse:
return SecretsResourceWithRawResponse(self._functions.secrets)
+ @cached_property
+ def triggers(self) -> TriggersResourceWithRawResponse:
+ return TriggersResourceWithRawResponse(self._functions.triggers)
+
+ @cached_property
+ def git_link(self) -> GitLinkResourceWithRawResponse:
+ return GitLinkResourceWithRawResponse(self._functions.git_link)
+
class AsyncFunctionsResourceWithRawResponse:
def __init__(self, functions: AsyncFunctionsResource) -> None:
@@ -923,6 +1198,15 @@ def __init__(self, functions: AsyncFunctionsResource) -> None:
self.get_deployment = async_to_raw_response_wrapper(
functions.get_deployment,
)
+ self.list_deployments = async_to_raw_response_wrapper(
+ functions.list_deployments,
+ )
+ self.list_event_types = async_to_raw_response_wrapper(
+ functions.list_event_types,
+ )
+ self.rollback_deployment = async_to_raw_response_wrapper(
+ functions.rollback_deployment,
+ )
self.tail_logs = async_to_raw_response_wrapper(
functions.tail_logs,
)
@@ -931,6 +1215,14 @@ def __init__(self, functions: AsyncFunctionsResource) -> None:
def secrets(self) -> AsyncSecretsResourceWithRawResponse:
return AsyncSecretsResourceWithRawResponse(self._functions.secrets)
+ @cached_property
+ def triggers(self) -> AsyncTriggersResourceWithRawResponse:
+ return AsyncTriggersResourceWithRawResponse(self._functions.triggers)
+
+ @cached_property
+ def git_link(self) -> AsyncGitLinkResourceWithRawResponse:
+ return AsyncGitLinkResourceWithRawResponse(self._functions.git_link)
+
class FunctionsResourceWithStreamingResponse:
def __init__(self, functions: FunctionsResource) -> None:
@@ -954,6 +1246,15 @@ def __init__(self, functions: FunctionsResource) -> None:
self.get_deployment = to_streamed_response_wrapper(
functions.get_deployment,
)
+ self.list_deployments = to_streamed_response_wrapper(
+ functions.list_deployments,
+ )
+ self.list_event_types = to_streamed_response_wrapper(
+ functions.list_event_types,
+ )
+ self.rollback_deployment = to_streamed_response_wrapper(
+ functions.rollback_deployment,
+ )
self.tail_logs = to_streamed_response_wrapper(
functions.tail_logs,
)
@@ -962,6 +1263,14 @@ def __init__(self, functions: FunctionsResource) -> None:
def secrets(self) -> SecretsResourceWithStreamingResponse:
return SecretsResourceWithStreamingResponse(self._functions.secrets)
+ @cached_property
+ def triggers(self) -> TriggersResourceWithStreamingResponse:
+ return TriggersResourceWithStreamingResponse(self._functions.triggers)
+
+ @cached_property
+ def git_link(self) -> GitLinkResourceWithStreamingResponse:
+ return GitLinkResourceWithStreamingResponse(self._functions.git_link)
+
class AsyncFunctionsResourceWithStreamingResponse:
def __init__(self, functions: AsyncFunctionsResource) -> None:
@@ -985,6 +1294,15 @@ def __init__(self, functions: AsyncFunctionsResource) -> None:
self.get_deployment = async_to_streamed_response_wrapper(
functions.get_deployment,
)
+ self.list_deployments = async_to_streamed_response_wrapper(
+ functions.list_deployments,
+ )
+ self.list_event_types = async_to_streamed_response_wrapper(
+ functions.list_event_types,
+ )
+ self.rollback_deployment = async_to_streamed_response_wrapper(
+ functions.rollback_deployment,
+ )
self.tail_logs = async_to_streamed_response_wrapper(
functions.tail_logs,
)
@@ -992,3 +1310,11 @@ def __init__(self, functions: AsyncFunctionsResource) -> None:
@cached_property
def secrets(self) -> AsyncSecretsResourceWithStreamingResponse:
return AsyncSecretsResourceWithStreamingResponse(self._functions.secrets)
+
+ @cached_property
+ def triggers(self) -> AsyncTriggersResourceWithStreamingResponse:
+ return AsyncTriggersResourceWithStreamingResponse(self._functions.triggers)
+
+ @cached_property
+ def git_link(self) -> AsyncGitLinkResourceWithStreamingResponse:
+ return AsyncGitLinkResourceWithStreamingResponse(self._functions.git_link)
diff --git a/src/zavudev/resources/functions/git_link.py b/src/zavudev/resources/functions/git_link.py
new file mode 100644
index 0000000..e20ac6a
--- /dev/null
+++ b/src/zavudev/resources/functions/git_link.py
@@ -0,0 +1,580 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.functions import git_link_link_params, git_link_update_params
+from ...types.functions.git_link_link_response import GitLinkLinkResponse
+from ...types.functions.git_link_update_response import GitLinkUpdateResponse
+from ...types.functions.git_link_retrieve_response import GitLinkRetrieveResponse
+from ...types.functions.git_link_deploy_now_response import GitLinkDeployNowResponse
+
+__all__ = ["GitLinkResource", "AsyncGitLinkResource"]
+
+
+class GitLinkResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> GitLinkResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return GitLinkResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> GitLinkResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return GitLinkResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> GitLinkRetrieveResponse:
+ """The link and its last deploy.
+
+ Never returns the webhook secret.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._get(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkRetrieveResponse,
+ )
+
+ def update(
+ self,
+ function_id: str,
+ *,
+ auto_deploy: bool | Omit = omit,
+ branch: str | Omit = omit,
+ root_dir: Optional[str] | 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,
+ ) -> GitLinkUpdateResponse:
+ """Change the branch, the root directory, or whether pushes deploy.
+
+ Pass at least
+ one field. `rootDir: null` clears the subdirectory.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._patch(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ body=maybe_transform(
+ {
+ "auto_deploy": auto_deploy,
+ "branch": branch,
+ "root_dir": root_dir,
+ },
+ git_link_update_params.GitLinkUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkUpdateResponse,
+ )
+
+ def deploy_now(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> GitLinkDeployNowResponse:
+ """Fetch the linked branch and deploy it without waiting for a push.
+
+ Returns
+ immediately; follow the outcome with `GET /v1/functions/{functionId}/git-link`,
+ whose `lastStatus` and `lastError` describe the run.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._post(
+ path_template("/v1/functions/{function_id}/git-link/deploy", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkDeployNowResponse,
+ )
+
+ def link(
+ self,
+ function_id: str,
+ *,
+ owner: str,
+ repo: str,
+ auto_deploy: bool | Omit = omit,
+ branch: str | Omit = omit,
+ root_dir: str | 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,
+ ) -> GitLinkLinkResponse:
+ """Bind a repository to this function so every push to `branch` deploys it.
+
+ A
+ function holds at most one link; linking again returns 400.
+
+ **The server decides how the link authenticates.** If the project has the Zavu
+ GitHub App installed, the link uses that installation: private repositories work
+ and there is nothing to configure in the repository. Otherwise it falls back to
+ a manual link and the response carries a `webhookSecret` you add to the
+ repository yourself. `connection` says which one you got.
+
+ The repository is not checked against GitHub here, because it cannot be: an
+ owner/repo that does not exist, or that the installation cannot see, is accepted
+ and fails on the first deploy with a fetch error.
+
+ Args:
+ root_dir: Subdirectory holding the project, for monorepos.
+
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._post(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ body=maybe_transform(
+ {
+ "owner": owner,
+ "repo": repo,
+ "auto_deploy": auto_deploy,
+ "branch": branch,
+ "root_dir": root_dir,
+ },
+ git_link_link_params.GitLinkLinkParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkLinkResponse,
+ )
+
+ def unlink(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """Remove the link.
+
+ The function and its deployments stay. A manual webhook left in
+ the repository stops being accepted, so remove it there too.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncGitLinkResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncGitLinkResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncGitLinkResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncGitLinkResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncGitLinkResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> GitLinkRetrieveResponse:
+ """The link and its last deploy.
+
+ Never returns the webhook secret.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._get(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ function_id: str,
+ *,
+ auto_deploy: bool | Omit = omit,
+ branch: str | Omit = omit,
+ root_dir: Optional[str] | 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,
+ ) -> GitLinkUpdateResponse:
+ """Change the branch, the root directory, or whether pushes deploy.
+
+ Pass at least
+ one field. `rootDir: null` clears the subdirectory.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._patch(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ body=await async_maybe_transform(
+ {
+ "auto_deploy": auto_deploy,
+ "branch": branch,
+ "root_dir": root_dir,
+ },
+ git_link_update_params.GitLinkUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkUpdateResponse,
+ )
+
+ async def deploy_now(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> GitLinkDeployNowResponse:
+ """Fetch the linked branch and deploy it without waiting for a push.
+
+ Returns
+ immediately; follow the outcome with `GET /v1/functions/{functionId}/git-link`,
+ whose `lastStatus` and `lastError` describe the run.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._post(
+ path_template("/v1/functions/{function_id}/git-link/deploy", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkDeployNowResponse,
+ )
+
+ async def link(
+ self,
+ function_id: str,
+ *,
+ owner: str,
+ repo: str,
+ auto_deploy: bool | Omit = omit,
+ branch: str | Omit = omit,
+ root_dir: str | 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,
+ ) -> GitLinkLinkResponse:
+ """Bind a repository to this function so every push to `branch` deploys it.
+
+ A
+ function holds at most one link; linking again returns 400.
+
+ **The server decides how the link authenticates.** If the project has the Zavu
+ GitHub App installed, the link uses that installation: private repositories work
+ and there is nothing to configure in the repository. Otherwise it falls back to
+ a manual link and the response carries a `webhookSecret` you add to the
+ repository yourself. `connection` says which one you got.
+
+ The repository is not checked against GitHub here, because it cannot be: an
+ owner/repo that does not exist, or that the installation cannot see, is accepted
+ and fails on the first deploy with a fetch error.
+
+ Args:
+ root_dir: Subdirectory holding the project, for monorepos.
+
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._post(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ body=await async_maybe_transform(
+ {
+ "owner": owner,
+ "repo": repo,
+ "auto_deploy": auto_deploy,
+ "branch": branch,
+ "root_dir": root_dir,
+ },
+ git_link_link_params.GitLinkLinkParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=GitLinkLinkResponse,
+ )
+
+ async def unlink(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """Remove the link.
+
+ The function and its deployments stay. A manual webhook left in
+ the repository stops being accepted, so remove it there too.
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/v1/functions/{function_id}/git-link", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class GitLinkResourceWithRawResponse:
+ def __init__(self, git_link: GitLinkResource) -> None:
+ self._git_link = git_link
+
+ self.retrieve = to_raw_response_wrapper(
+ git_link.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ git_link.update,
+ )
+ self.deploy_now = to_raw_response_wrapper(
+ git_link.deploy_now,
+ )
+ self.link = to_raw_response_wrapper(
+ git_link.link,
+ )
+ self.unlink = to_raw_response_wrapper(
+ git_link.unlink,
+ )
+
+
+class AsyncGitLinkResourceWithRawResponse:
+ def __init__(self, git_link: AsyncGitLinkResource) -> None:
+ self._git_link = git_link
+
+ self.retrieve = async_to_raw_response_wrapper(
+ git_link.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ git_link.update,
+ )
+ self.deploy_now = async_to_raw_response_wrapper(
+ git_link.deploy_now,
+ )
+ self.link = async_to_raw_response_wrapper(
+ git_link.link,
+ )
+ self.unlink = async_to_raw_response_wrapper(
+ git_link.unlink,
+ )
+
+
+class GitLinkResourceWithStreamingResponse:
+ def __init__(self, git_link: GitLinkResource) -> None:
+ self._git_link = git_link
+
+ self.retrieve = to_streamed_response_wrapper(
+ git_link.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ git_link.update,
+ )
+ self.deploy_now = to_streamed_response_wrapper(
+ git_link.deploy_now,
+ )
+ self.link = to_streamed_response_wrapper(
+ git_link.link,
+ )
+ self.unlink = to_streamed_response_wrapper(
+ git_link.unlink,
+ )
+
+
+class AsyncGitLinkResourceWithStreamingResponse:
+ def __init__(self, git_link: AsyncGitLinkResource) -> None:
+ self._git_link = git_link
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ git_link.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ git_link.update,
+ )
+ self.deploy_now = async_to_streamed_response_wrapper(
+ git_link.deploy_now,
+ )
+ self.link = async_to_streamed_response_wrapper(
+ git_link.link,
+ )
+ self.unlink = async_to_streamed_response_wrapper(
+ git_link.unlink,
+ )
diff --git a/src/zavudev/resources/functions/triggers.py b/src/zavudev/resources/functions/triggers.py
new file mode 100644
index 0000000..28d2032
--- /dev/null
+++ b/src/zavudev/resources/functions/triggers.py
@@ -0,0 +1,461 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.functions import trigger_create_params, trigger_update_params
+from ...types.functions.trigger_list_response import TriggerListResponse
+from ...types.functions.trigger_create_response import TriggerCreateResponse
+from ...types.functions.trigger_update_response import TriggerUpdateResponse
+
+__all__ = ["TriggersResource", "AsyncTriggersResource"]
+
+
+class TriggersResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> TriggersResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return TriggersResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TriggersResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return TriggersResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ function_id: str,
+ *,
+ event_types: SequenceNotStr[str],
+ sender_ids: SequenceNotStr[Optional[str]],
+ cron: str | 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,
+ ) -> TriggerCreateResponse:
+ """
+ Subscribe a function to one or more event types, optionally scoped to specific
+ senders. Provide eventTypes and senderIds (use null in senderIds for all
+ senders); a trigger is created for each event type and sender combination.
+
+ The special event type `cron` runs the function on a schedule instead of a
+ messaging event: include a `cron` field with a 5-field UTC cron expression
+ (minimum granularity one minute). A cron trigger ignores the sender axis, and a
+ function may hold several cron triggers with different expressions. The function
+ receives an event with `type: "cron"` and `data.cron`.
+
+ Args:
+ event_types: Event types to subscribe to.
+
+ sender_ids: Senders to scope the triggers to. Use null for all senders.
+
+ cron: Required when eventTypes includes `cron`: a 5-field cron expression (minute hour
+ day-of-month month day-of-week), evaluated in UTC.
+
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._post(
+ path_template("/v1/functions/{function_id}/triggers", function_id=function_id),
+ body=maybe_transform(
+ {
+ "event_types": event_types,
+ "sender_ids": sender_ids,
+ "cron": cron,
+ },
+ trigger_create_params.TriggerCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TriggerCreateResponse,
+ )
+
+ def update(
+ self,
+ trigger_id: str,
+ *,
+ active: bool,
+ # 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,
+ ) -> TriggerUpdateResponse:
+ """
+ Enable or disable a trigger
+
+ Args:
+ 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 trigger_id:
+ raise ValueError(f"Expected a non-empty value for `trigger_id` but received {trigger_id!r}")
+ return self._patch(
+ path_template("/v1/functions/triggers/{trigger_id}", trigger_id=trigger_id),
+ body=maybe_transform({"active": active}, trigger_update_params.TriggerUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TriggerUpdateResponse,
+ )
+
+ def list(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> TriggerListResponse:
+ """
+ List function triggers
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return self._get(
+ path_template("/v1/functions/{function_id}/triggers", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TriggerListResponse,
+ )
+
+ def delete(
+ self,
+ trigger_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Delete a trigger
+
+ Args:
+ 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 trigger_id:
+ raise ValueError(f"Expected a non-empty value for `trigger_id` but received {trigger_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/v1/functions/triggers/{trigger_id}", trigger_id=trigger_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncTriggersResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncTriggersResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTriggersResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTriggersResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncTriggersResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ function_id: str,
+ *,
+ event_types: SequenceNotStr[str],
+ sender_ids: SequenceNotStr[Optional[str]],
+ cron: str | 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,
+ ) -> TriggerCreateResponse:
+ """
+ Subscribe a function to one or more event types, optionally scoped to specific
+ senders. Provide eventTypes and senderIds (use null in senderIds for all
+ senders); a trigger is created for each event type and sender combination.
+
+ The special event type `cron` runs the function on a schedule instead of a
+ messaging event: include a `cron` field with a 5-field UTC cron expression
+ (minimum granularity one minute). A cron trigger ignores the sender axis, and a
+ function may hold several cron triggers with different expressions. The function
+ receives an event with `type: "cron"` and `data.cron`.
+
+ Args:
+ event_types: Event types to subscribe to.
+
+ sender_ids: Senders to scope the triggers to. Use null for all senders.
+
+ cron: Required when eventTypes includes `cron`: a 5-field cron expression (minute hour
+ day-of-month month day-of-week), evaluated in UTC.
+
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._post(
+ path_template("/v1/functions/{function_id}/triggers", function_id=function_id),
+ body=await async_maybe_transform(
+ {
+ "event_types": event_types,
+ "sender_ids": sender_ids,
+ "cron": cron,
+ },
+ trigger_create_params.TriggerCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TriggerCreateResponse,
+ )
+
+ async def update(
+ self,
+ trigger_id: str,
+ *,
+ active: bool,
+ # 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,
+ ) -> TriggerUpdateResponse:
+ """
+ Enable or disable a trigger
+
+ Args:
+ 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 trigger_id:
+ raise ValueError(f"Expected a non-empty value for `trigger_id` but received {trigger_id!r}")
+ return await self._patch(
+ path_template("/v1/functions/triggers/{trigger_id}", trigger_id=trigger_id),
+ body=await async_maybe_transform({"active": active}, trigger_update_params.TriggerUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TriggerUpdateResponse,
+ )
+
+ async def list(
+ self,
+ function_id: str,
+ *,
+ # 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,
+ ) -> TriggerListResponse:
+ """
+ List function triggers
+
+ Args:
+ 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 function_id:
+ raise ValueError(f"Expected a non-empty value for `function_id` but received {function_id!r}")
+ return await self._get(
+ path_template("/v1/functions/{function_id}/triggers", function_id=function_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TriggerListResponse,
+ )
+
+ async def delete(
+ self,
+ trigger_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Delete a trigger
+
+ Args:
+ 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 trigger_id:
+ raise ValueError(f"Expected a non-empty value for `trigger_id` but received {trigger_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/v1/functions/triggers/{trigger_id}", trigger_id=trigger_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class TriggersResourceWithRawResponse:
+ def __init__(self, triggers: TriggersResource) -> None:
+ self._triggers = triggers
+
+ self.create = to_raw_response_wrapper(
+ triggers.create,
+ )
+ self.update = to_raw_response_wrapper(
+ triggers.update,
+ )
+ self.list = to_raw_response_wrapper(
+ triggers.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ triggers.delete,
+ )
+
+
+class AsyncTriggersResourceWithRawResponse:
+ def __init__(self, triggers: AsyncTriggersResource) -> None:
+ self._triggers = triggers
+
+ self.create = async_to_raw_response_wrapper(
+ triggers.create,
+ )
+ self.update = async_to_raw_response_wrapper(
+ triggers.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ triggers.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ triggers.delete,
+ )
+
+
+class TriggersResourceWithStreamingResponse:
+ def __init__(self, triggers: TriggersResource) -> None:
+ self._triggers = triggers
+
+ self.create = to_streamed_response_wrapper(
+ triggers.create,
+ )
+ self.update = to_streamed_response_wrapper(
+ triggers.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ triggers.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ triggers.delete,
+ )
+
+
+class AsyncTriggersResourceWithStreamingResponse:
+ def __init__(self, triggers: AsyncTriggersResource) -> None:
+ self._triggers = triggers
+
+ self.create = async_to_streamed_response_wrapper(
+ triggers.create,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ triggers.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ triggers.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ triggers.delete,
+ )
diff --git a/src/zavudev/resources/introspect.py b/src/zavudev/resources/introspect.py
index 533b4c7..2a34d74 100644
--- a/src/zavudev/resources/introspect.py
+++ b/src/zavudev/resources/introspect.py
@@ -4,8 +4,8 @@
import httpx
-from ..types import introspect_validate_phone_params
-from .._types import Body, Query, Headers, NotGiven, not_given
+from ..types import introspect_validate_email_params, introspect_validate_phone_params
+from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
@@ -16,6 +16,7 @@
async_to_streamed_response_wrapper,
)
from .._base_client import make_request_options
+from ..types.introspect_validate_email_response import IntrospectValidateEmailResponse
from ..types.introspect_validate_phone_response import IntrospectValidatePhoneResponse
__all__ = ["IntrospectResource", "AsyncIntrospectResource"]
@@ -41,6 +42,58 @@ def with_streaming_response(self) -> IntrospectResourceWithStreamingResponse:
"""
return IntrospectResourceWithStreamingResponse(self)
+ def validate_email(
+ self,
+ *,
+ email: str | Omit = omit,
+ emails: SequenceNotStr[str] | 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,
+ ) -> IntrospectValidateEmailResponse:
+ """
+ Heuristic email validation to run before sending: catches invalid syntax, dead
+ domains (no MX/A records), disposable inboxes, role-based addresses (info@,
+ contacto@, sales@), and addresses already on your project's suppression list.
+ Use it to clean a list before a broadcast and keep your bounce rate low.
+
+ No mailbox-level (SMTP) probe is performed, so a `deliverable` verdict is not a
+ delivery guarantee — it means no negative signal was found. Treat `risky`
+ addresses with care and drop `undeliverable` ones.
+
+ Accepts a single `email` or an `emails` batch (max 100 per request).
+
+ Args:
+ email: Single email address to validate.
+
+ emails: Batch of email addresses to validate (max 100).
+
+ 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
+ """
+ return self._post(
+ "/v1/introspect/email",
+ body=maybe_transform(
+ {
+ "email": email,
+ "emails": emails,
+ },
+ introspect_validate_email_params.IntrospectValidateEmailParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=IntrospectValidateEmailResponse,
+ )
+
def validate_phone(
self,
*,
@@ -96,6 +149,58 @@ def with_streaming_response(self) -> AsyncIntrospectResourceWithStreamingRespons
"""
return AsyncIntrospectResourceWithStreamingResponse(self)
+ async def validate_email(
+ self,
+ *,
+ email: str | Omit = omit,
+ emails: SequenceNotStr[str] | 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,
+ ) -> IntrospectValidateEmailResponse:
+ """
+ Heuristic email validation to run before sending: catches invalid syntax, dead
+ domains (no MX/A records), disposable inboxes, role-based addresses (info@,
+ contacto@, sales@), and addresses already on your project's suppression list.
+ Use it to clean a list before a broadcast and keep your bounce rate low.
+
+ No mailbox-level (SMTP) probe is performed, so a `deliverable` verdict is not a
+ delivery guarantee — it means no negative signal was found. Treat `risky`
+ addresses with care and drop `undeliverable` ones.
+
+ Accepts a single `email` or an `emails` batch (max 100 per request).
+
+ Args:
+ email: Single email address to validate.
+
+ emails: Batch of email addresses to validate (max 100).
+
+ 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
+ """
+ return await self._post(
+ "/v1/introspect/email",
+ body=await async_maybe_transform(
+ {
+ "email": email,
+ "emails": emails,
+ },
+ introspect_validate_email_params.IntrospectValidateEmailParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=IntrospectValidateEmailResponse,
+ )
+
async def validate_phone(
self,
*,
@@ -135,6 +240,9 @@ class IntrospectResourceWithRawResponse:
def __init__(self, introspect: IntrospectResource) -> None:
self._introspect = introspect
+ self.validate_email = to_raw_response_wrapper(
+ introspect.validate_email,
+ )
self.validate_phone = to_raw_response_wrapper(
introspect.validate_phone,
)
@@ -144,6 +252,9 @@ class AsyncIntrospectResourceWithRawResponse:
def __init__(self, introspect: AsyncIntrospectResource) -> None:
self._introspect = introspect
+ self.validate_email = async_to_raw_response_wrapper(
+ introspect.validate_email,
+ )
self.validate_phone = async_to_raw_response_wrapper(
introspect.validate_phone,
)
@@ -153,6 +264,9 @@ class IntrospectResourceWithStreamingResponse:
def __init__(self, introspect: IntrospectResource) -> None:
self._introspect = introspect
+ self.validate_email = to_streamed_response_wrapper(
+ introspect.validate_email,
+ )
self.validate_phone = to_streamed_response_wrapper(
introspect.validate_phone,
)
@@ -162,6 +276,9 @@ class AsyncIntrospectResourceWithStreamingResponse:
def __init__(self, introspect: AsyncIntrospectResource) -> None:
self._introspect = introspect
+ self.validate_email = async_to_streamed_response_wrapper(
+ introspect.validate_email,
+ )
self.validate_phone = async_to_streamed_response_wrapper(
introspect.validate_phone,
)
diff --git a/src/zavudev/resources/messages.py b/src/zavudev/resources/messages.py
index c4ea702..1e3c984 100644
--- a/src/zavudev/resources/messages.py
+++ b/src/zavudev/resources/messages.py
@@ -26,6 +26,7 @@
from ..types.message_response import MessageResponse
from ..types.message_content_param import MessageContentParam
from ..types.message_show_typing_response import MessageShowTypingResponse
+from ..types.message_list_attachments_response import MessageListAttachmentsResponse
__all__ = ["MessagesResource", "AsyncMessagesResource"]
@@ -137,6 +138,44 @@ def list(
model=Message,
)
+ def list_attachments(
+ self,
+ message_id: str,
+ *,
+ # 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,
+ ) -> MessageListAttachmentsResponse:
+ """
+ List the stored file attachments for an email message and get a short-lived
+ signed `downloadUrl` for each. Works for both inbound emails (received via
+ `message.inbound`) and outbound emails you sent with attachments. Messages
+ without stored attachments (including SMS, WhatsApp, and other channels) return
+ an empty list. Each `downloadUrl` is generated fresh per request and expires —
+ fetch the file promptly and do not cache the URL.
+
+ Args:
+ 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 message_id:
+ raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}")
+ return self._get(
+ path_template("/v1/messages/{message_id}/attachments", message_id=message_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=MessageListAttachmentsResponse,
+ )
+
def react(
self,
message_id: str,
@@ -230,6 +269,36 @@ def send(
100/day. Teams on earlier plans keep their original email quotas instead
- SMS and voice are billed per message from your balance on every plan
+ **Account verification and daily limits:**
+
+ - A brand-new account can send on every channel immediately, but `sms`,
+ `sms_oneway` and `voice` reach only the phone numbers the project has
+ verified. Sending elsewhere returns `403` with code
+ `destination_not_verified`; `details.verifiedNumbers` lists the numbers that
+ are reachable. A number is verified from the dashboard's Sandbox screen:
+ generate a code and send the pre-filled WhatsApp message from that phone to
+ Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to
+ 5 numbers per project. To send to any destination, do any one of these: verify
+ your identity, add a payment method, settle a deposit, or subscribe to a paid
+ plan. Business verification (KYB) is never required to send
+ - Daily ceilings apply per channel group and rise with verification. An account
+ that has verified nothing: 25/day across `sms` + `sms_oneway`, 5/day for
+ `voice`, 100/day across WhatsApp, Telegram, Instagram and Messenger combined.
+ Past that floor: 200/day for SMS, or 10,000/day once identity or business
+ verification is approved (or a higher limit agreed for your account); 50/day
+ voice and 250/day conversational on Free. **Paid plans have no voice or
+ conversational daily ceiling.** Over a ceiling, sends return `429` with code
+ `daily_limit_exceeded` and `details.limit`; the count resets at 00:00 UTC
+ - The daily ceiling never reduces the monthly allowance: 100/day on the
+ conversational group still reaches the 2,000 monthly A2P messages Free
+ includes
+ - Email needs no account verification here: a sender with a verified domain
+ sends from day one, within the plan quota (100/day and 3,000/month on Free).
+ Over the daily quota it returns `429` with code `daily_limit_exceeded`. Email
+ broadcasts are the exception: they need the account past the unverified level,
+ see `POST /v1/broadcasts/{broadcastId}/send`
+ - Full reference: https://docs.zavu.dev/concepts/sending-limits
+
**Email recipient pre-flight:** Email messages are validated automatically
before dispatch. Sends that would be a guaranteed hard bounce are failed instead
of sent, protecting your bounce rate: the message transitions to `failed`
@@ -460,6 +529,44 @@ def list(
model=Message,
)
+ async def list_attachments(
+ self,
+ message_id: str,
+ *,
+ # 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,
+ ) -> MessageListAttachmentsResponse:
+ """
+ List the stored file attachments for an email message and get a short-lived
+ signed `downloadUrl` for each. Works for both inbound emails (received via
+ `message.inbound`) and outbound emails you sent with attachments. Messages
+ without stored attachments (including SMS, WhatsApp, and other channels) return
+ an empty list. Each `downloadUrl` is generated fresh per request and expires —
+ fetch the file promptly and do not cache the URL.
+
+ Args:
+ 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 message_id:
+ raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}")
+ return await self._get(
+ path_template("/v1/messages/{message_id}/attachments", message_id=message_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=MessageListAttachmentsResponse,
+ )
+
async def react(
self,
message_id: str,
@@ -553,6 +660,36 @@ async def send(
100/day. Teams on earlier plans keep their original email quotas instead
- SMS and voice are billed per message from your balance on every plan
+ **Account verification and daily limits:**
+
+ - A brand-new account can send on every channel immediately, but `sms`,
+ `sms_oneway` and `voice` reach only the phone numbers the project has
+ verified. Sending elsewhere returns `403` with code
+ `destination_not_verified`; `details.verifiedNumbers` lists the numbers that
+ are reachable. A number is verified from the dashboard's Sandbox screen:
+ generate a code and send the pre-filled WhatsApp message from that phone to
+ Zavu's sandbox number. One verification covers WhatsApp, SMS and calls, up to
+ 5 numbers per project. To send to any destination, do any one of these: verify
+ your identity, add a payment method, settle a deposit, or subscribe to a paid
+ plan. Business verification (KYB) is never required to send
+ - Daily ceilings apply per channel group and rise with verification. An account
+ that has verified nothing: 25/day across `sms` + `sms_oneway`, 5/day for
+ `voice`, 100/day across WhatsApp, Telegram, Instagram and Messenger combined.
+ Past that floor: 200/day for SMS, or 10,000/day once identity or business
+ verification is approved (or a higher limit agreed for your account); 50/day
+ voice and 250/day conversational on Free. **Paid plans have no voice or
+ conversational daily ceiling.** Over a ceiling, sends return `429` with code
+ `daily_limit_exceeded` and `details.limit`; the count resets at 00:00 UTC
+ - The daily ceiling never reduces the monthly allowance: 100/day on the
+ conversational group still reaches the 2,000 monthly A2P messages Free
+ includes
+ - Email needs no account verification here: a sender with a verified domain
+ sends from day one, within the plan quota (100/day and 3,000/month on Free).
+ Over the daily quota it returns `429` with code `daily_limit_exceeded`. Email
+ broadcasts are the exception: they need the account past the unverified level,
+ see `POST /v1/broadcasts/{broadcastId}/send`
+ - Full reference: https://docs.zavu.dev/concepts/sending-limits
+
**Email recipient pre-flight:** Email messages are validated automatically
before dispatch. Sends that would be a guaranteed hard bounce are failed instead
of sent, protecting your bounce rate: the message transitions to `failed`
@@ -686,6 +823,9 @@ def __init__(self, messages: MessagesResource) -> None:
self.list = to_raw_response_wrapper(
messages.list,
)
+ self.list_attachments = to_raw_response_wrapper(
+ messages.list_attachments,
+ )
self.react = to_raw_response_wrapper(
messages.react,
)
@@ -707,6 +847,9 @@ def __init__(self, messages: AsyncMessagesResource) -> None:
self.list = async_to_raw_response_wrapper(
messages.list,
)
+ self.list_attachments = async_to_raw_response_wrapper(
+ messages.list_attachments,
+ )
self.react = async_to_raw_response_wrapper(
messages.react,
)
@@ -728,6 +871,9 @@ def __init__(self, messages: MessagesResource) -> None:
self.list = to_streamed_response_wrapper(
messages.list,
)
+ self.list_attachments = to_streamed_response_wrapper(
+ messages.list_attachments,
+ )
self.react = to_streamed_response_wrapper(
messages.react,
)
@@ -749,6 +895,9 @@ def __init__(self, messages: AsyncMessagesResource) -> None:
self.list = async_to_streamed_response_wrapper(
messages.list,
)
+ self.list_attachments = async_to_streamed_response_wrapper(
+ messages.list_attachments,
+ )
self.react = async_to_streamed_response_wrapper(
messages.react,
)
diff --git a/src/zavudev/resources/number_10dlc/brands.py b/src/zavudev/resources/number_10dlc/brands.py
index 128aa5c..1d5b973 100644
--- a/src/zavudev/resources/number_10dlc/brands.py
+++ b/src/zavudev/resources/number_10dlc/brands.py
@@ -354,11 +354,16 @@ def submit(
"""Submit a draft brand to The Campaign Registry (TCR) for vetting.
The brand must
- be in draft status. TCR's one-time $4 brand registration fee is charged from
- your balance at submission (passed through at cost) and refunded if the carrier
- rejects the registration. A team that already paid this fee through the
- compliance flow is not charged again. Campaign registration is billed separately
- when a campaign is submitted.
+ be in draft status, and the team must have an approved Business Verification
+ (KYB) — carriers register a brand against a vetted legal entity, so submitting
+ without one returns `403` with code `kyb_required`.
+
+ TCR's one-time $4 brand registration fee is charged from your balance at
+ submission (passed through at cost) and refunded if the carrier rejects the
+ registration. The fee is per BRAND: a team registering a second legal entity
+ pays it again. A brand already paid for through the compliance flow is not
+ charged twice. Campaign registration is billed separately when a campaign is
+ submitted.
Args:
extra_headers: Send extra headers
@@ -739,11 +744,16 @@ async def submit(
"""Submit a draft brand to The Campaign Registry (TCR) for vetting.
The brand must
- be in draft status. TCR's one-time $4 brand registration fee is charged from
- your balance at submission (passed through at cost) and refunded if the carrier
- rejects the registration. A team that already paid this fee through the
- compliance flow is not charged again. Campaign registration is billed separately
- when a campaign is submitted.
+ be in draft status, and the team must have an approved Business Verification
+ (KYB) — carriers register a brand against a vetted legal entity, so submitting
+ without one returns `403` with code `kyb_required`.
+
+ TCR's one-time $4 brand registration fee is charged from your balance at
+ submission (passed through at cost) and refunded if the carrier rejects the
+ registration. The fee is per BRAND: a team registering a second legal entity
+ pays it again. A brand already paid for through the compliance flow is not
+ charged twice. Campaign registration is billed separately when a campaign is
+ submitted.
Args:
extra_headers: Send extra headers
diff --git a/src/zavudev/resources/number_10dlc/campaigns/campaigns.py b/src/zavudev/resources/number_10dlc/campaigns/campaigns.py
index fa73cd5..95287af 100644
--- a/src/zavudev/resources/number_10dlc/campaigns/campaigns.py
+++ b/src/zavudev/resources/number_10dlc/campaigns/campaigns.py
@@ -316,15 +316,15 @@ def submit(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> CampaignSubmitResponse:
- """Submit a draft campaign for carrier review.
-
- The campaign must be in draft status
- and its brand must be verified. TCR's one-time registration fee is charged from
- your balance at submission ($15 for standard use cases, $2 for LOW_VOLUME),
- passed through at cost and refunded if the carrier rejects it. Once approved,
- the campaign's monthly TCR fee ($10 standard, $2 LOW_VOLUME) is charged from
- your balance while the campaign is active — see registrationCostCents and
- monthlyFeeCents on the campaign object.
+ """
+ The team must have an approved Business Verification (KYB): submitting without
+ one returns `403` with code `kyb_required`. Submit a draft campaign for carrier
+ review. The campaign must be in draft status and its brand must be verified.
+ TCR's one-time registration fee is charged from your balance at submission ($15
+ for standard use cases, $2 for LOW_VOLUME), passed through at cost and refunded
+ if the carrier rejects it. Once approved, the campaign's monthly TCR fee ($10
+ standard, $2 LOW_VOLUME) is charged from your balance while the campaign is
+ active — see registrationCostCents and monthlyFeeCents on the campaign object.
Args:
extra_headers: Send extra headers
@@ -662,15 +662,15 @@ async def submit(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> CampaignSubmitResponse:
- """Submit a draft campaign for carrier review.
-
- The campaign must be in draft status
- and its brand must be verified. TCR's one-time registration fee is charged from
- your balance at submission ($15 for standard use cases, $2 for LOW_VOLUME),
- passed through at cost and refunded if the carrier rejects it. Once approved,
- the campaign's monthly TCR fee ($10 standard, $2 LOW_VOLUME) is charged from
- your balance while the campaign is active — see registrationCostCents and
- monthlyFeeCents on the campaign object.
+ """
+ The team must have an approved Business Verification (KYB): submitting without
+ one returns `403` with code `kyb_required`. Submit a draft campaign for carrier
+ review. The campaign must be in draft status and its brand must be verified.
+ TCR's one-time registration fee is charged from your balance at submission ($15
+ for standard use cases, $2 for LOW_VOLUME), passed through at cost and refunded
+ if the carrier rejects it. Once approved, the campaign's monthly TCR fee ($10
+ standard, $2 LOW_VOLUME) is charged from your balance while the campaign is
+ active — see registrationCostCents and monthlyFeeCents on the campaign object.
Args:
extra_headers: Send extra headers
diff --git a/src/zavudev/resources/senders/__init__.py b/src/zavudev/resources/senders/__init__.py
index d73613e..2389ab1 100644
--- a/src/zavudev/resources/senders/__init__.py
+++ b/src/zavudev/resources/senders/__init__.py
@@ -16,6 +16,14 @@
SendersResourceWithStreamingResponse,
AsyncSendersResourceWithStreamingResponse,
)
+from .telegram import (
+ TelegramResource,
+ AsyncTelegramResource,
+ TelegramResourceWithRawResponse,
+ AsyncTelegramResourceWithRawResponse,
+ TelegramResourceWithStreamingResponse,
+ AsyncTelegramResourceWithStreamingResponse,
+)
from .whatsapp_sync import (
WhatsappSyncResource,
AsyncWhatsappSyncResource,
@@ -38,6 +46,12 @@
"AsyncWhatsappSyncResourceWithRawResponse",
"WhatsappSyncResourceWithStreamingResponse",
"AsyncWhatsappSyncResourceWithStreamingResponse",
+ "TelegramResource",
+ "AsyncTelegramResource",
+ "TelegramResourceWithRawResponse",
+ "AsyncTelegramResourceWithRawResponse",
+ "TelegramResourceWithStreamingResponse",
+ "AsyncTelegramResourceWithStreamingResponse",
"SendersResource",
"AsyncSendersResource",
"SendersResourceWithRawResponse",
diff --git a/src/zavudev/resources/senders/agent/agent.py b/src/zavudev/resources/senders/agent/agent.py
index 39ea5bc..5b66d87 100644
--- a/src/zavudev/resources/senders/agent/agent.py
+++ b/src/zavudev/resources/senders/agent/agent.py
@@ -14,14 +14,6 @@
FlowsResourceWithStreamingResponse,
AsyncFlowsResourceWithStreamingResponse,
)
-from .tools import (
- ToolsResource,
- AsyncToolsResource,
- ToolsResourceWithRawResponse,
- AsyncToolsResourceWithRawResponse,
- ToolsResourceWithStreamingResponse,
- AsyncToolsResourceWithStreamingResponse,
-)
from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
from ...._utils import path_template, maybe_transform, async_maybe_transform
from ...._compat import cached_property
@@ -33,6 +25,14 @@
ExecutionsResourceWithStreamingResponse,
AsyncExecutionsResourceWithStreamingResponse,
)
+from .tools.tools import (
+ ToolsResource,
+ AsyncToolsResource,
+ ToolsResourceWithRawResponse,
+ AsyncToolsResourceWithRawResponse,
+ ToolsResourceWithStreamingResponse,
+ AsyncToolsResourceWithStreamingResponse,
+)
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
to_raw_response_wrapper,
diff --git a/src/zavudev/resources/senders/agent/knowledge_bases/documents.py b/src/zavudev/resources/senders/agent/knowledge_bases/documents.py
index 3e9762f..92becbe 100644
--- a/src/zavudev/resources/senders/agent/knowledge_bases/documents.py
+++ b/src/zavudev/resources/senders/agent/knowledge_bases/documents.py
@@ -17,8 +17,16 @@
from .....pagination import SyncCursor, AsyncCursor
from ....._base_client import AsyncPaginator, make_request_options
from .....types.senders.agent.agent_document import AgentDocument
-from .....types.senders.agent.knowledge_bases import document_list_params, document_create_params
+from .....types.senders.agent.knowledge_bases import (
+ document_list_params,
+ document_create_params,
+ document_update_document_params,
+)
from .....types.senders.agent.knowledge_bases.document_create_response import DocumentCreateResponse
+from .....types.senders.agent.knowledge_bases.document_update_document_response import DocumentUpdateDocumentResponse
+from .....types.senders.agent.knowledge_bases.document_retrieve_document_response import (
+ DocumentRetrieveDocumentResponse,
+)
__all__ = ["DocumentsResource", "AsyncDocumentsResource"]
@@ -188,6 +196,105 @@ def delete(
cast_to=NoneType,
)
+ def retrieve_document(
+ self,
+ doc_id: str,
+ *,
+ sender_id: str,
+ kb_id: str,
+ # 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,
+ ) -> DocumentRetrieveDocumentResponse:
+ """
+ Get a single document from a knowledge base.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not kb_id:
+ raise ValueError(f"Expected a non-empty value for `kb_id` but received {kb_id!r}")
+ if not doc_id:
+ raise ValueError(f"Expected a non-empty value for `doc_id` but received {doc_id!r}")
+ return self._get(
+ path_template(
+ "/v1/senders/{sender_id}/agent/knowledge-bases/{kb_id}/documents/{doc_id}",
+ sender_id=sender_id,
+ kb_id=kb_id,
+ doc_id=doc_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DocumentRetrieveDocumentResponse,
+ )
+
+ def update_document(
+ self,
+ doc_id: str,
+ *,
+ sender_id: str,
+ kb_id: str,
+ content: str | Omit = omit,
+ title: str | 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,
+ ) -> DocumentUpdateDocumentResponse:
+ """Update a document's title or content.
+
+ Updating content reprocesses the document
+ for RAG.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not kb_id:
+ raise ValueError(f"Expected a non-empty value for `kb_id` but received {kb_id!r}")
+ if not doc_id:
+ raise ValueError(f"Expected a non-empty value for `doc_id` but received {doc_id!r}")
+ return self._patch(
+ path_template(
+ "/v1/senders/{sender_id}/agent/knowledge-bases/{kb_id}/documents/{doc_id}",
+ sender_id=sender_id,
+ kb_id=kb_id,
+ doc_id=doc_id,
+ ),
+ body=maybe_transform(
+ {
+ "content": content,
+ "title": title,
+ },
+ document_update_document_params.DocumentUpdateDocumentParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DocumentUpdateDocumentResponse,
+ )
+
class AsyncDocumentsResource(AsyncAPIResource):
@cached_property
@@ -354,6 +461,105 @@ async def delete(
cast_to=NoneType,
)
+ async def retrieve_document(
+ self,
+ doc_id: str,
+ *,
+ sender_id: str,
+ kb_id: str,
+ # 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,
+ ) -> DocumentRetrieveDocumentResponse:
+ """
+ Get a single document from a knowledge base.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not kb_id:
+ raise ValueError(f"Expected a non-empty value for `kb_id` but received {kb_id!r}")
+ if not doc_id:
+ raise ValueError(f"Expected a non-empty value for `doc_id` but received {doc_id!r}")
+ return await self._get(
+ path_template(
+ "/v1/senders/{sender_id}/agent/knowledge-bases/{kb_id}/documents/{doc_id}",
+ sender_id=sender_id,
+ kb_id=kb_id,
+ doc_id=doc_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DocumentRetrieveDocumentResponse,
+ )
+
+ async def update_document(
+ self,
+ doc_id: str,
+ *,
+ sender_id: str,
+ kb_id: str,
+ content: str | Omit = omit,
+ title: str | 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,
+ ) -> DocumentUpdateDocumentResponse:
+ """Update a document's title or content.
+
+ Updating content reprocesses the document
+ for RAG.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not kb_id:
+ raise ValueError(f"Expected a non-empty value for `kb_id` but received {kb_id!r}")
+ if not doc_id:
+ raise ValueError(f"Expected a non-empty value for `doc_id` but received {doc_id!r}")
+ return await self._patch(
+ path_template(
+ "/v1/senders/{sender_id}/agent/knowledge-bases/{kb_id}/documents/{doc_id}",
+ sender_id=sender_id,
+ kb_id=kb_id,
+ doc_id=doc_id,
+ ),
+ body=await async_maybe_transform(
+ {
+ "content": content,
+ "title": title,
+ },
+ document_update_document_params.DocumentUpdateDocumentParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DocumentUpdateDocumentResponse,
+ )
+
class DocumentsResourceWithRawResponse:
def __init__(self, documents: DocumentsResource) -> None:
@@ -368,6 +574,12 @@ def __init__(self, documents: DocumentsResource) -> None:
self.delete = to_raw_response_wrapper(
documents.delete,
)
+ self.retrieve_document = to_raw_response_wrapper(
+ documents.retrieve_document,
+ )
+ self.update_document = to_raw_response_wrapper(
+ documents.update_document,
+ )
class AsyncDocumentsResourceWithRawResponse:
@@ -383,6 +595,12 @@ def __init__(self, documents: AsyncDocumentsResource) -> None:
self.delete = async_to_raw_response_wrapper(
documents.delete,
)
+ self.retrieve_document = async_to_raw_response_wrapper(
+ documents.retrieve_document,
+ )
+ self.update_document = async_to_raw_response_wrapper(
+ documents.update_document,
+ )
class DocumentsResourceWithStreamingResponse:
@@ -398,6 +616,12 @@ def __init__(self, documents: DocumentsResource) -> None:
self.delete = to_streamed_response_wrapper(
documents.delete,
)
+ self.retrieve_document = to_streamed_response_wrapper(
+ documents.retrieve_document,
+ )
+ self.update_document = to_streamed_response_wrapper(
+ documents.update_document,
+ )
class AsyncDocumentsResourceWithStreamingResponse:
@@ -413,3 +637,9 @@ def __init__(self, documents: AsyncDocumentsResource) -> None:
self.delete = async_to_streamed_response_wrapper(
documents.delete,
)
+ self.retrieve_document = async_to_streamed_response_wrapper(
+ documents.retrieve_document,
+ )
+ self.update_document = async_to_streamed_response_wrapper(
+ documents.update_document,
+ )
diff --git a/src/zavudev/resources/senders/agent/tools/__init__.py b/src/zavudev/resources/senders/agent/tools/__init__.py
new file mode 100644
index 0000000..1b25ec5
--- /dev/null
+++ b/src/zavudev/resources/senders/agent/tools/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .tools import (
+ ToolsResource,
+ AsyncToolsResource,
+ ToolsResourceWithRawResponse,
+ AsyncToolsResourceWithRawResponse,
+ ToolsResourceWithStreamingResponse,
+ AsyncToolsResourceWithStreamingResponse,
+)
+from .webhook import (
+ WebhookResource,
+ AsyncWebhookResource,
+ WebhookResourceWithRawResponse,
+ AsyncWebhookResourceWithRawResponse,
+ WebhookResourceWithStreamingResponse,
+ AsyncWebhookResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "WebhookResource",
+ "AsyncWebhookResource",
+ "WebhookResourceWithRawResponse",
+ "AsyncWebhookResourceWithRawResponse",
+ "WebhookResourceWithStreamingResponse",
+ "AsyncWebhookResourceWithStreamingResponse",
+ "ToolsResource",
+ "AsyncToolsResource",
+ "ToolsResourceWithRawResponse",
+ "AsyncToolsResourceWithRawResponse",
+ "ToolsResourceWithStreamingResponse",
+ "AsyncToolsResourceWithStreamingResponse",
+]
diff --git a/src/zavudev/resources/senders/agent/tools.py b/src/zavudev/resources/senders/agent/tools/tools.py
similarity index 81%
rename from src/zavudev/resources/senders/agent/tools.py
rename to src/zavudev/resources/senders/agent/tools/tools.py
index 11448ba..15b6863 100644
--- a/src/zavudev/resources/senders/agent/tools.py
+++ b/src/zavudev/resources/senders/agent/tools/tools.py
@@ -6,35 +6,49 @@
import httpx
-from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ...._utils import path_template, maybe_transform, async_maybe_transform
-from ...._compat import cached_property
-from ...._resource import SyncAPIResource, AsyncAPIResource
-from ...._response import (
+from .webhook import (
+ WebhookResource,
+ AsyncWebhookResource,
+ WebhookResourceWithRawResponse,
+ AsyncWebhookResourceWithRawResponse,
+ WebhookResourceWithStreamingResponse,
+ AsyncWebhookResourceWithStreamingResponse,
+)
+from ....._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from ....._utils import path_template, maybe_transform, async_maybe_transform
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import (
to_raw_response_wrapper,
to_streamed_response_wrapper,
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ....pagination import SyncCursor, AsyncCursor
-from ...._base_client import AsyncPaginator, make_request_options
-from ....types.senders.agent import (
+from .....pagination import SyncCursor, AsyncCursor
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.senders.agent import (
tool_list_params,
tool_test_params,
tool_create_params,
tool_update_params,
+ tool_list_test_runs_params,
)
-from ....types.senders.agent.agent_tool import AgentTool
-from ....types.senders.agent.tool_test_response import ToolTestResponse
-from ....types.senders.agent.tool_create_response import ToolCreateResponse
-from ....types.senders.agent.tool_update_response import ToolUpdateResponse
-from ....types.senders.agent.tool_parameters_param import ToolParametersParam
-from ....types.senders.agent.tool_retrieve_response import ToolRetrieveResponse
+from .....types.senders.agent.agent_tool import AgentTool
+from .....types.senders.agent.tool_test_response import ToolTestResponse
+from .....types.senders.agent.tool_create_response import ToolCreateResponse
+from .....types.senders.agent.tool_update_response import ToolUpdateResponse
+from .....types.senders.agent.tool_parameters_param import ToolParametersParam
+from .....types.senders.agent.tool_retrieve_response import ToolRetrieveResponse
+from .....types.senders.agent.tool_list_test_runs_response import ToolListTestRunsResponse
__all__ = ["ToolsResource", "AsyncToolsResource"]
class ToolsResource(SyncAPIResource):
+ @cached_property
+ def webhook(self) -> WebhookResource:
+ return WebhookResource(self._client)
+
@cached_property
def with_raw_response(self) -> ToolsResourceWithRawResponse:
"""
@@ -285,6 +299,52 @@ def delete(
cast_to=NoneType,
)
+ def list_test_runs(
+ self,
+ tool_id: str,
+ *,
+ sender_id: str,
+ limit: int | 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,
+ ) -> ToolListTestRunsResponse:
+ """Recent runs of this tool triggered from the test endpoint, newest first.
+
+ Covers
+ manual tests only: a tool called by an agent during a real conversation is not
+ recorded here.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not tool_id:
+ raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}")
+ return self._get(
+ path_template(
+ "/v1/senders/{sender_id}/agent/tools/{tool_id}/test-runs", sender_id=sender_id, tool_id=tool_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"limit": limit}, tool_list_test_runs_params.ToolListTestRunsParams),
+ ),
+ cast_to=ToolListTestRunsResponse,
+ )
+
def test(
self,
tool_id: str,
@@ -336,6 +396,10 @@ def test(
class AsyncToolsResource(AsyncAPIResource):
+ @cached_property
+ def webhook(self) -> AsyncWebhookResource:
+ return AsyncWebhookResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncToolsResourceWithRawResponse:
"""
@@ -586,6 +650,52 @@ async def delete(
cast_to=NoneType,
)
+ async def list_test_runs(
+ self,
+ tool_id: str,
+ *,
+ sender_id: str,
+ limit: int | 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,
+ ) -> ToolListTestRunsResponse:
+ """Recent runs of this tool triggered from the test endpoint, newest first.
+
+ Covers
+ manual tests only: a tool called by an agent during a real conversation is not
+ recorded here.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not tool_id:
+ raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}")
+ return await self._get(
+ path_template(
+ "/v1/senders/{sender_id}/agent/tools/{tool_id}/test-runs", sender_id=sender_id, tool_id=tool_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"limit": limit}, tool_list_test_runs_params.ToolListTestRunsParams),
+ ),
+ cast_to=ToolListTestRunsResponse,
+ )
+
async def test(
self,
tool_id: str,
@@ -655,10 +765,17 @@ def __init__(self, tools: ToolsResource) -> None:
self.delete = to_raw_response_wrapper(
tools.delete,
)
+ self.list_test_runs = to_raw_response_wrapper(
+ tools.list_test_runs,
+ )
self.test = to_raw_response_wrapper(
tools.test,
)
+ @cached_property
+ def webhook(self) -> WebhookResourceWithRawResponse:
+ return WebhookResourceWithRawResponse(self._tools.webhook)
+
class AsyncToolsResourceWithRawResponse:
def __init__(self, tools: AsyncToolsResource) -> None:
@@ -679,10 +796,17 @@ def __init__(self, tools: AsyncToolsResource) -> None:
self.delete = async_to_raw_response_wrapper(
tools.delete,
)
+ self.list_test_runs = async_to_raw_response_wrapper(
+ tools.list_test_runs,
+ )
self.test = async_to_raw_response_wrapper(
tools.test,
)
+ @cached_property
+ def webhook(self) -> AsyncWebhookResourceWithRawResponse:
+ return AsyncWebhookResourceWithRawResponse(self._tools.webhook)
+
class ToolsResourceWithStreamingResponse:
def __init__(self, tools: ToolsResource) -> None:
@@ -703,10 +827,17 @@ def __init__(self, tools: ToolsResource) -> None:
self.delete = to_streamed_response_wrapper(
tools.delete,
)
+ self.list_test_runs = to_streamed_response_wrapper(
+ tools.list_test_runs,
+ )
self.test = to_streamed_response_wrapper(
tools.test,
)
+ @cached_property
+ def webhook(self) -> WebhookResourceWithStreamingResponse:
+ return WebhookResourceWithStreamingResponse(self._tools.webhook)
+
class AsyncToolsResourceWithStreamingResponse:
def __init__(self, tools: AsyncToolsResource) -> None:
@@ -727,6 +858,13 @@ def __init__(self, tools: AsyncToolsResource) -> None:
self.delete = async_to_streamed_response_wrapper(
tools.delete,
)
+ self.list_test_runs = async_to_streamed_response_wrapper(
+ tools.list_test_runs,
+ )
self.test = async_to_streamed_response_wrapper(
tools.test,
)
+
+ @cached_property
+ def webhook(self) -> AsyncWebhookResourceWithStreamingResponse:
+ return AsyncWebhookResourceWithStreamingResponse(self._tools.webhook)
diff --git a/src/zavudev/resources/senders/agent/tools/webhook.py b/src/zavudev/resources/senders/agent/tools/webhook.py
new file mode 100644
index 0000000..e9a1d9f
--- /dev/null
+++ b/src/zavudev/resources/senders/agent/tools/webhook.py
@@ -0,0 +1,180 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ....._types import Body, Query, Headers, NotGiven, not_given
+from ....._utils import path_template
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ....._base_client import make_request_options
+from .....types.webhook_secret_response import WebhookSecretResponse
+
+__all__ = ["WebhookResource", "AsyncWebhookResource"]
+
+
+class WebhookResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> WebhookResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return WebhookResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> WebhookResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return WebhookResourceWithStreamingResponse(self)
+
+ def rotate_secret(
+ self,
+ tool_id: str,
+ *,
+ sender_id: str,
+ # 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,
+ ) -> WebhookSecretResponse:
+ """Generate a new signing secret for this tool.
+
+ The previous one stops working on
+ the next call, with no overlap, so update your endpoint first. The tool keeps
+ its id, so flows that reference it by name are unaffected.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not tool_id:
+ raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}")
+ return self._post(
+ path_template(
+ "/v1/senders/{sender_id}/agent/tools/{tool_id}/webhook/secret", sender_id=sender_id, tool_id=tool_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookSecretResponse,
+ )
+
+
+class AsyncWebhookResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncWebhookResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncWebhookResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncWebhookResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncWebhookResourceWithStreamingResponse(self)
+
+ async def rotate_secret(
+ self,
+ tool_id: str,
+ *,
+ sender_id: str,
+ # 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,
+ ) -> WebhookSecretResponse:
+ """Generate a new signing secret for this tool.
+
+ The previous one stops working on
+ the next call, with no overlap, so update your endpoint first. The tool keeps
+ its id, so flows that reference it by name are unaffected.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ if not tool_id:
+ raise ValueError(f"Expected a non-empty value for `tool_id` but received {tool_id!r}")
+ return await self._post(
+ path_template(
+ "/v1/senders/{sender_id}/agent/tools/{tool_id}/webhook/secret", sender_id=sender_id, tool_id=tool_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookSecretResponse,
+ )
+
+
+class WebhookResourceWithRawResponse:
+ def __init__(self, webhook: WebhookResource) -> None:
+ self._webhook = webhook
+
+ self.rotate_secret = to_raw_response_wrapper(
+ webhook.rotate_secret,
+ )
+
+
+class AsyncWebhookResourceWithRawResponse:
+ def __init__(self, webhook: AsyncWebhookResource) -> None:
+ self._webhook = webhook
+
+ self.rotate_secret = async_to_raw_response_wrapper(
+ webhook.rotate_secret,
+ )
+
+
+class WebhookResourceWithStreamingResponse:
+ def __init__(self, webhook: WebhookResource) -> None:
+ self._webhook = webhook
+
+ self.rotate_secret = to_streamed_response_wrapper(
+ webhook.rotate_secret,
+ )
+
+
+class AsyncWebhookResourceWithStreamingResponse:
+ def __init__(self, webhook: AsyncWebhookResource) -> None:
+ self._webhook = webhook
+
+ self.rotate_secret = async_to_streamed_response_wrapper(
+ webhook.rotate_secret,
+ )
diff --git a/src/zavudev/resources/senders/senders.py b/src/zavudev/resources/senders/senders.py
index 5abd856..ab19db8 100644
--- a/src/zavudev/resources/senders/senders.py
+++ b/src/zavudev/resources/senders/senders.py
@@ -17,6 +17,14 @@
)
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
from ..._utils import path_template, maybe_transform, async_maybe_transform
+from .telegram import (
+ TelegramResource,
+ AsyncTelegramResource,
+ TelegramResourceWithRawResponse,
+ AsyncTelegramResourceWithRawResponse,
+ TelegramResourceWithStreamingResponse,
+ AsyncTelegramResourceWithStreamingResponse,
+)
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -63,6 +71,10 @@ def agent(self) -> AgentResource:
def whatsapp_sync(self) -> WhatsappSyncResource:
return WhatsappSyncResource(self._client)
+ @cached_property
+ def telegram(self) -> TelegramResource:
+ return TelegramResource(self._client)
+
@cached_property
def with_raw_response(self) -> SendersResourceWithRawResponse:
"""
@@ -593,6 +605,10 @@ def agent(self) -> AsyncAgentResource:
def whatsapp_sync(self) -> AsyncWhatsappSyncResource:
return AsyncWhatsappSyncResource(self._client)
+ @cached_property
+ def telegram(self) -> AsyncTelegramResource:
+ return AsyncTelegramResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncSendersResourceWithRawResponse:
"""
@@ -1154,6 +1170,10 @@ def agent(self) -> AgentResourceWithRawResponse:
def whatsapp_sync(self) -> WhatsappSyncResourceWithRawResponse:
return WhatsappSyncResourceWithRawResponse(self._senders.whatsapp_sync)
+ @cached_property
+ def telegram(self) -> TelegramResourceWithRawResponse:
+ return TelegramResourceWithRawResponse(self._senders.telegram)
+
class AsyncSendersResourceWithRawResponse:
def __init__(self, senders: AsyncSendersResource) -> None:
@@ -1195,6 +1215,10 @@ def agent(self) -> AsyncAgentResourceWithRawResponse:
def whatsapp_sync(self) -> AsyncWhatsappSyncResourceWithRawResponse:
return AsyncWhatsappSyncResourceWithRawResponse(self._senders.whatsapp_sync)
+ @cached_property
+ def telegram(self) -> AsyncTelegramResourceWithRawResponse:
+ return AsyncTelegramResourceWithRawResponse(self._senders.telegram)
+
class SendersResourceWithStreamingResponse:
def __init__(self, senders: SendersResource) -> None:
@@ -1236,6 +1260,10 @@ def agent(self) -> AgentResourceWithStreamingResponse:
def whatsapp_sync(self) -> WhatsappSyncResourceWithStreamingResponse:
return WhatsappSyncResourceWithStreamingResponse(self._senders.whatsapp_sync)
+ @cached_property
+ def telegram(self) -> TelegramResourceWithStreamingResponse:
+ return TelegramResourceWithStreamingResponse(self._senders.telegram)
+
class AsyncSendersResourceWithStreamingResponse:
def __init__(self, senders: AsyncSendersResource) -> None:
@@ -1276,3 +1304,7 @@ def agent(self) -> AsyncAgentResourceWithStreamingResponse:
@cached_property
def whatsapp_sync(self) -> AsyncWhatsappSyncResourceWithStreamingResponse:
return AsyncWhatsappSyncResourceWithStreamingResponse(self._senders.whatsapp_sync)
+
+ @cached_property
+ def telegram(self) -> AsyncTelegramResourceWithStreamingResponse:
+ return AsyncTelegramResourceWithStreamingResponse(self._senders.telegram)
diff --git a/src/zavudev/resources/senders/telegram.py b/src/zavudev/resources/senders/telegram.py
new file mode 100644
index 0000000..a420625
--- /dev/null
+++ b/src/zavudev/resources/senders/telegram.py
@@ -0,0 +1,259 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Query, Headers, NoneType, NotGiven, not_given
+from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.senders import telegram_connect_params
+from ...types.senders.telegram_connect_response import TelegramConnectResponse
+
+__all__ = ["TelegramResource", "AsyncTelegramResource"]
+
+
+class TelegramResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> TelegramResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return TelegramResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TelegramResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return TelegramResourceWithStreamingResponse(self)
+
+ def connect(
+ self,
+ sender_id: str,
+ *,
+ bot_token: str,
+ # 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,
+ ) -> TelegramConnectResponse:
+ """Connect a Telegram bot to a sender.
+
+ Provide the bot token from @BotFather; Zavu
+ validates it, registers the webhook, and routes the sender's Telegram messages
+ through it.
+
+ Args:
+ bot_token: Bot token from @BotFather.
+
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ return self._post(
+ path_template("/v1/senders/{sender_id}/telegram", sender_id=sender_id),
+ body=maybe_transform({"bot_token": bot_token}, telegram_connect_params.TelegramConnectParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TelegramConnectResponse,
+ )
+
+ def disconnect(
+ self,
+ sender_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Disconnect Telegram from a sender and remove the webhook.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/v1/senders/{sender_id}/telegram", sender_id=sender_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncTelegramResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncTelegramResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncTelegramResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTelegramResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/zavudev/sdk-python#with_streaming_response
+ """
+ return AsyncTelegramResourceWithStreamingResponse(self)
+
+ async def connect(
+ self,
+ sender_id: str,
+ *,
+ bot_token: str,
+ # 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,
+ ) -> TelegramConnectResponse:
+ """Connect a Telegram bot to a sender.
+
+ Provide the bot token from @BotFather; Zavu
+ validates it, registers the webhook, and routes the sender's Telegram messages
+ through it.
+
+ Args:
+ bot_token: Bot token from @BotFather.
+
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ return await self._post(
+ path_template("/v1/senders/{sender_id}/telegram", sender_id=sender_id),
+ body=await async_maybe_transform({"bot_token": bot_token}, telegram_connect_params.TelegramConnectParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TelegramConnectResponse,
+ )
+
+ async def disconnect(
+ self,
+ sender_id: str,
+ *,
+ # 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,
+ ) -> None:
+ """
+ Disconnect Telegram from a sender and remove the webhook.
+
+ Args:
+ 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 sender_id:
+ raise ValueError(f"Expected a non-empty value for `sender_id` but received {sender_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/v1/senders/{sender_id}/telegram", sender_id=sender_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class TelegramResourceWithRawResponse:
+ def __init__(self, telegram: TelegramResource) -> None:
+ self._telegram = telegram
+
+ self.connect = to_raw_response_wrapper(
+ telegram.connect,
+ )
+ self.disconnect = to_raw_response_wrapper(
+ telegram.disconnect,
+ )
+
+
+class AsyncTelegramResourceWithRawResponse:
+ def __init__(self, telegram: AsyncTelegramResource) -> None:
+ self._telegram = telegram
+
+ self.connect = async_to_raw_response_wrapper(
+ telegram.connect,
+ )
+ self.disconnect = async_to_raw_response_wrapper(
+ telegram.disconnect,
+ )
+
+
+class TelegramResourceWithStreamingResponse:
+ def __init__(self, telegram: TelegramResource) -> None:
+ self._telegram = telegram
+
+ self.connect = to_streamed_response_wrapper(
+ telegram.connect,
+ )
+ self.disconnect = to_streamed_response_wrapper(
+ telegram.disconnect,
+ )
+
+
+class AsyncTelegramResourceWithStreamingResponse:
+ def __init__(self, telegram: AsyncTelegramResource) -> None:
+ self._telegram = telegram
+
+ self.connect = async_to_streamed_response_wrapper(
+ telegram.connect,
+ )
+ self.disconnect = async_to_streamed_response_wrapper(
+ telegram.disconnect,
+ )
diff --git a/src/zavudev/resources/templates.py b/src/zavudev/resources/templates.py
index 6f8aa56..bb802f7 100644
--- a/src/zavudev/resources/templates.py
+++ b/src/zavudev/resources/templates.py
@@ -7,7 +7,13 @@
import httpx
-from ..types import WhatsappCategory, template_list_params, template_create_params, template_submit_params
+from ..types import (
+ WhatsappCategory,
+ template_list_params,
+ template_sync_params,
+ template_create_params,
+ template_submit_params,
+)
from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
from .._utils import path_template, maybe_transform, async_maybe_transform
from .._compat import cached_property
@@ -22,6 +28,7 @@
from .._base_client import AsyncPaginator, make_request_options
from ..types.template import Template
from ..types.whatsapp_category import WhatsappCategory
+from ..types.template_sync_response import TemplateSyncResponse
__all__ = ["TemplatesResource", "AsyncTemplatesResource"]
@@ -292,6 +299,62 @@ def submit(
cast_to=Template,
)
+ def sync(
+ self,
+ *,
+ sender_id: str | 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,
+ ) -> TemplateSyncResponse:
+ """Reconcile this project's templates against WhatsApp.
+
+ Two things happen per
+ connected WhatsApp Business Account: templates that exist on Meta but not in
+ Zavu are imported (or linked to an existing template with the same name), and
+ the approval status of the templates Zavu already knows about is refreshed from
+ Meta.
+
+ This is what to call when a template was created outside Zavu — in Meta Business
+ Manager, or by another tool — or when a `template.status_changed` webhook was
+ missed and a template is stuck in `pending`. Status changes normally arrive by
+ webhook; this endpoint is the recovery path and the only path for a template
+ Zavu never created.
+
+ Templates that Meta reports as rejected or disabled are not imported; they are
+ counted in `skipped`. Existing local templates are matched first by Meta
+ template ID, then by name.
+
+ By default every sender in the project with a WhatsApp Business Account is
+ synced. Pass `senderId` to sync only that sender's account. The call is
+ synchronous — it waits for Meta and returns what changed — so it can take a few
+ seconds per account. A failure on one account does not fail the request: it is
+ reported in `errors` and the remaining accounts are still synced.
+
+ Args:
+ sender_id: Sync only the WhatsApp Business Account attached to this sender. If omitted,
+ every WhatsApp sender in the project is synced.
+
+ 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
+ """
+ return self._post(
+ "/v1/templates/sync",
+ body=maybe_transform({"sender_id": sender_id}, template_sync_params.TemplateSyncParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TemplateSyncResponse,
+ )
+
class AsyncTemplatesResource(AsyncAPIResource):
@cached_property
@@ -559,6 +622,62 @@ async def submit(
cast_to=Template,
)
+ async def sync(
+ self,
+ *,
+ sender_id: str | 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,
+ ) -> TemplateSyncResponse:
+ """Reconcile this project's templates against WhatsApp.
+
+ Two things happen per
+ connected WhatsApp Business Account: templates that exist on Meta but not in
+ Zavu are imported (or linked to an existing template with the same name), and
+ the approval status of the templates Zavu already knows about is refreshed from
+ Meta.
+
+ This is what to call when a template was created outside Zavu — in Meta Business
+ Manager, or by another tool — or when a `template.status_changed` webhook was
+ missed and a template is stuck in `pending`. Status changes normally arrive by
+ webhook; this endpoint is the recovery path and the only path for a template
+ Zavu never created.
+
+ Templates that Meta reports as rejected or disabled are not imported; they are
+ counted in `skipped`. Existing local templates are matched first by Meta
+ template ID, then by name.
+
+ By default every sender in the project with a WhatsApp Business Account is
+ synced. Pass `senderId` to sync only that sender's account. The call is
+ synchronous — it waits for Meta and returns what changed — so it can take a few
+ seconds per account. A failure on one account does not fail the request: it is
+ reported in `errors` and the remaining accounts are still synced.
+
+ Args:
+ sender_id: Sync only the WhatsApp Business Account attached to this sender. If omitted,
+ every WhatsApp sender in the project is synced.
+
+ 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
+ """
+ return await self._post(
+ "/v1/templates/sync",
+ body=await async_maybe_transform({"sender_id": sender_id}, template_sync_params.TemplateSyncParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=TemplateSyncResponse,
+ )
+
class TemplatesResourceWithRawResponse:
def __init__(self, templates: TemplatesResource) -> None:
@@ -579,6 +698,9 @@ def __init__(self, templates: TemplatesResource) -> None:
self.submit = to_raw_response_wrapper(
templates.submit,
)
+ self.sync = to_raw_response_wrapper(
+ templates.sync,
+ )
class AsyncTemplatesResourceWithRawResponse:
@@ -600,6 +722,9 @@ def __init__(self, templates: AsyncTemplatesResource) -> None:
self.submit = async_to_raw_response_wrapper(
templates.submit,
)
+ self.sync = async_to_raw_response_wrapper(
+ templates.sync,
+ )
class TemplatesResourceWithStreamingResponse:
@@ -621,6 +746,9 @@ def __init__(self, templates: TemplatesResource) -> None:
self.submit = to_streamed_response_wrapper(
templates.submit,
)
+ self.sync = to_streamed_response_wrapper(
+ templates.sync,
+ )
class AsyncTemplatesResourceWithStreamingResponse:
@@ -642,3 +770,6 @@ def __init__(self, templates: AsyncTemplatesResource) -> None:
self.submit = async_to_streamed_response_wrapper(
templates.submit,
)
+ self.sync = async_to_streamed_response_wrapper(
+ templates.sync,
+ )
diff --git a/src/zavudev/resources/urls.py b/src/zavudev/resources/urls.py
index e3630a1..151e96b 100644
--- a/src/zavudev/resources/urls.py
+++ b/src/zavudev/resources/urls.py
@@ -6,7 +6,7 @@
import httpx
-from ..types import url_list_verified_params, url_submit_for_verification_params
+from ..types import url_escalate_params, url_list_verified_params, url_submit_for_verification_params
from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._utils import path_template, maybe_transform, async_maybe_transform
from .._compat import cached_property
@@ -20,6 +20,7 @@
from ..pagination import SyncCursor, AsyncCursor
from .._base_client import AsyncPaginator, make_request_options
from ..types.verified_url import VerifiedURL
+from ..types.url_escalate_response import URLEscalateResponse
from ..types.url_retrieve_details_response import URLRetrieveDetailsResponse
from ..types.url_submit_for_verification_response import URLSubmitForVerificationResponse
@@ -46,6 +47,45 @@ def with_streaming_response(self) -> URLsResourceWithStreamingResponse:
"""
return URLsResourceWithStreamingResponse(self)
+ def escalate(
+ self,
+ url_id: str,
+ *,
+ reason: str,
+ # 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,
+ ) -> URLEscalateResponse:
+ """Request manual review of a rejected URL.
+
+ Only URLs in 'rejected' status can be
+ escalated; the status then moves to 'escalated'.
+
+ Args:
+ reason: Why the URL should be reviewed manually.
+
+ 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 url_id:
+ raise ValueError(f"Expected a non-empty value for `url_id` but received {url_id!r}")
+ return self._post(
+ path_template("/v1/urls/{url_id}/escalate", url_id=url_id),
+ body=maybe_transform({"reason": reason}, url_escalate_params.URLEscalateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=URLEscalateResponse,
+ )
+
def list_verified(
self,
*,
@@ -188,6 +228,45 @@ def with_streaming_response(self) -> AsyncURLsResourceWithStreamingResponse:
"""
return AsyncURLsResourceWithStreamingResponse(self)
+ async def escalate(
+ self,
+ url_id: str,
+ *,
+ reason: str,
+ # 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,
+ ) -> URLEscalateResponse:
+ """Request manual review of a rejected URL.
+
+ Only URLs in 'rejected' status can be
+ escalated; the status then moves to 'escalated'.
+
+ Args:
+ reason: Why the URL should be reviewed manually.
+
+ 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 url_id:
+ raise ValueError(f"Expected a non-empty value for `url_id` but received {url_id!r}")
+ return await self._post(
+ path_template("/v1/urls/{url_id}/escalate", url_id=url_id),
+ body=await async_maybe_transform({"reason": reason}, url_escalate_params.URLEscalateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=URLEscalateResponse,
+ )
+
def list_verified(
self,
*,
@@ -316,6 +395,9 @@ class URLsResourceWithRawResponse:
def __init__(self, urls: URLsResource) -> None:
self._urls = urls
+ self.escalate = to_raw_response_wrapper(
+ urls.escalate,
+ )
self.list_verified = to_raw_response_wrapper(
urls.list_verified,
)
@@ -331,6 +413,9 @@ class AsyncURLsResourceWithRawResponse:
def __init__(self, urls: AsyncURLsResource) -> None:
self._urls = urls
+ self.escalate = async_to_raw_response_wrapper(
+ urls.escalate,
+ )
self.list_verified = async_to_raw_response_wrapper(
urls.list_verified,
)
@@ -346,6 +431,9 @@ class URLsResourceWithStreamingResponse:
def __init__(self, urls: URLsResource) -> None:
self._urls = urls
+ self.escalate = to_streamed_response_wrapper(
+ urls.escalate,
+ )
self.list_verified = to_streamed_response_wrapper(
urls.list_verified,
)
@@ -361,6 +449,9 @@ class AsyncURLsResourceWithStreamingResponse:
def __init__(self, urls: AsyncURLsResource) -> None:
self._urls = urls
+ self.escalate = async_to_streamed_response_wrapper(
+ urls.escalate,
+ )
self.list_verified = async_to_streamed_response_wrapper(
urls.list_verified,
)
diff --git a/src/zavudev/types/__init__.py b/src/zavudev/types/__init__.py
index 70032b3..1a16448 100644
--- a/src/zavudev/types/__init__.py
+++ b/src/zavudev/types/__init__.py
@@ -22,22 +22,33 @@
from .contact_channel import ContactChannel as ContactChannel
from .message_content import MessageContent as MessageContent
from .broadcast_status import BroadcastStatus as BroadcastStatus
+from .call_list_params import CallListParams as CallListParams
from .message_response import MessageResponse as MessageResponse
from .requirement_type import RequirementType as RequirementType
+from .agent_list_params import AgentListParams as AgentListParams
+from .agent_test_params import AgentTestParams as AgentTestParams
from .broadcast_channel import BroadcastChannel as BroadcastChannel
from .broadcast_contact import BroadcastContact as BroadcastContact
from .broadcast_content import BroadcastContent as BroadcastContent
from .phone_number_type import PhoneNumberType as PhoneNumberType
from .whatsapp_category import WhatsappCategory as WhatsappCategory
from .broadcast_progress import BroadcastProgress as BroadcastProgress
+from .call_create_params import CallCreateParams as CallCreateParams
+from .call_list_response import CallListResponse as CallListResponse
from .owned_phone_number import OwnedPhoneNumber as OwnedPhoneNumber
from .sender_list_params import SenderListParams as SenderListParams
from .address_list_params import AddressListParams as AddressListParams
+from .agent_create_params import AgentCreateParams as AgentCreateParams
+from .agent_test_response import AgentTestResponse as AgentTestResponse
+from .agent_update_params import AgentUpdateParams as AgentUpdateParams
from .contact_list_params import ContactListParams as ContactListParams
from .message_list_params import MessageListParams as MessageListParams
from .message_send_params import MessageSendParams as MessageSendParams
from .phone_number_status import PhoneNumberStatus as PhoneNumberStatus
from .regulatory_document import RegulatoryDocument as RegulatoryDocument
+from .url_escalate_params import URLEscalateParams as URLEscalateParams
+from .call_create_response import CallCreateResponse as CallCreateResponse
+from .call_hangup_response import CallHangupResponse as CallHangupResponse
from .contact_merge_params import ContactMergeParams as ContactMergeParams
from .me_retrieve_response import MeRetrieveResponse as MeRetrieveResponse
from .message_react_params import MessageReactParams as MessageReactParams
@@ -45,14 +56,19 @@
from .sender_create_params import SenderCreateParams as SenderCreateParams
from .sender_update_params import SenderUpdateParams as SenderUpdateParams
from .template_list_params import TemplateListParams as TemplateListParams
+from .template_sync_params import TemplateSyncParams as TemplateSyncParams
from .address_create_params import AddressCreateParams as AddressCreateParams
+from .agent_create_response import AgentCreateResponse as AgentCreateResponse
+from .agent_update_response import AgentUpdateResponse as AgentUpdateResponse
from .broadcast_list_params import BroadcastListParams as BroadcastListParams
from .broadcast_send_params import BroadcastSendParams as BroadcastSendParams
from .contact_create_params import ContactCreateParams as ContactCreateParams
from .contact_update_params import ContactUpdateParams as ContactUpdateParams
from .message_content_param import MessageContentParam as MessageContentParam
+from .url_escalate_response import URLEscalateResponse as URLEscalateResponse
from .available_phone_number import AvailablePhoneNumber as AvailablePhoneNumber
from .broadcast_message_type import BroadcastMessageType as BroadcastMessageType
+from .call_retrieve_response import CallRetrieveResponse as CallRetrieveResponse
from .function_create_params import FunctionCreateParams as FunctionCreateParams
from .function_deploy_params import FunctionDeployParams as FunctionDeployParams
from .function_update_params import FunctionUpdateParams as FunctionUpdateParams
@@ -60,14 +76,18 @@
from .requirement_field_type import RequirementFieldType as RequirementFieldType
from .template_create_params import TemplateCreateParams as TemplateCreateParams
from .template_submit_params import TemplateSubmitParams as TemplateSubmitParams
+from .template_sync_response import TemplateSyncResponse as TemplateSyncResponse
from .address_create_response import AddressCreateResponse as AddressCreateResponse
+from .agent_retrieve_response import AgentRetrieveResponse as AgentRetrieveResponse
from .broadcast_content_param import BroadcastContentParam as BroadcastContentParam
from .broadcast_create_params import BroadcastCreateParams as BroadcastCreateParams
from .broadcast_send_response import BroadcastSendResponse as BroadcastSendResponse
from .broadcast_update_params import BroadcastUpdateParams as BroadcastUpdateParams
from .sub_account_list_params import SubAccountListParams as SubAccountListParams
from .webhook_secret_response import WebhookSecretResponse as WebhookSecretResponse
+from .agent_list_voices_params import AgentListVoicesParams as AgentListVoicesParams
from .broadcast_contact_status import BroadcastContactStatus as BroadcastContactStatus
+from .conversation_list_params import ConversationListParams as ConversationListParams
from .function_create_response import FunctionCreateResponse as FunctionCreateResponse
from .function_delete_response import FunctionDeleteResponse as FunctionDeleteResponse
from .function_deploy_response import FunctionDeployResponse as FunctionDeployResponse
@@ -85,6 +105,10 @@
from .sub_account_create_params import SubAccountCreateParams as SubAccountCreateParams
from .sub_account_update_params import SubAccountUpdateParams as SubAccountUpdateParams
from .whatsapp_business_profile import WhatsappBusinessProfile as WhatsappBusinessProfile
+from .agent_list_voices_response import AgentListVoicesResponse as AgentListVoicesResponse
+from .conversation_list_response import ConversationListResponse as ConversationListResponse
+from .email_domain_create_params import EmailDomainCreateParams as EmailDomainCreateParams
+from .email_domain_list_response import EmailDomainListResponse as EmailDomainListResponse
from .function_retrieve_response import FunctionRetrieveResponse as FunctionRetrieveResponse
from .invitation_cancel_response import InvitationCancelResponse as InvitationCancelResponse
from .invitation_create_response import InvitationCreateResponse as InvitationCreateResponse
@@ -95,6 +119,9 @@
from .function_tail_logs_response import FunctionTailLogsResponse as FunctionTailLogsResponse
from .sub_account_create_response import SubAccountCreateResponse as SubAccountCreateResponse
from .sub_account_update_response import SubAccountUpdateResponse as SubAccountUpdateResponse
+from .agent_template_list_response import AgentTemplateListResponse as AgentTemplateListResponse
+from .email_domain_create_response import EmailDomainCreateResponse as EmailDomainCreateResponse
+from .email_domain_verify_response import EmailDomainVerifyResponse as EmailDomainVerifyResponse
from .invitation_retrieve_response import InvitationRetrieveResponse as InvitationRetrieveResponse
from .message_show_typing_response import MessageShowTypingResponse as MessageShowTypingResponse
from .phone_number_purchase_params import PhoneNumberPurchaseParams as PhoneNumberPurchaseParams
@@ -103,6 +130,8 @@
from .broadcast_reschedule_response import BroadcastRescheduleResponse as BroadcastRescheduleResponse
from .sub_account_retrieve_response import SubAccountRetrieveResponse as SubAccountRetrieveResponse
from .url_retrieve_details_response import URLRetrieveDetailsResponse as URLRetrieveDetailsResponse
+from .conversation_retrieve_response import ConversationRetrieveResponse as ConversationRetrieveResponse
+from .email_domain_retrieve_response import EmailDomainRetrieveResponse as EmailDomainRetrieveResponse
from .phone_number_purchase_response import PhoneNumberPurchaseResponse as PhoneNumberPurchaseResponse
from .phone_number_retrieve_response import PhoneNumberRetrieveResponse as PhoneNumberRetrieveResponse
from .sender_update_profile_response import SenderUpdateProfileResponse as SenderUpdateProfileResponse
@@ -110,21 +139,34 @@
from .regulatory_document_list_params import RegulatoryDocumentListParams as RegulatoryDocumentListParams
from .requirement_acceptance_criteria import RequirementAcceptanceCriteria as RequirementAcceptanceCriteria
from .sub_account_deactivate_response import SubAccountDeactivateResponse as SubAccountDeactivateResponse
+from .agent_template_retrieve_response import AgentTemplateRetrieveResponse as AgentTemplateRetrieveResponse
from .function_get_deployment_response import FunctionGetDeploymentResponse as FunctionGetDeploymentResponse
+from .function_list_deployments_params import FunctionListDeploymentsParams as FunctionListDeploymentsParams
+from .introspect_validate_email_params import IntrospectValidateEmailParams as IntrospectValidateEmailParams
from .introspect_validate_phone_params import IntrospectValidatePhoneParams as IntrospectValidatePhoneParams
from .phone_number_requirements_params import PhoneNumberRequirementsParams as PhoneNumberRequirementsParams
from .sub_account_get_balance_response import SubAccountGetBalanceResponse as SubAccountGetBalanceResponse
+from .conversation_list_messages_params import ConversationListMessagesParams as ConversationListMessagesParams
+from .message_list_attachments_response import MessageListAttachmentsResponse as MessageListAttachmentsResponse
from .regulatory_document_create_params import RegulatoryDocumentCreateParams as RegulatoryDocumentCreateParams
from .broadcast_escalate_review_response import BroadcastEscalateReviewResponse as BroadcastEscalateReviewResponse
+from .conversation_mark_as_read_response import ConversationMarkAsReadResponse as ConversationMarkAsReadResponse
+from .function_list_deployments_response import FunctionListDeploymentsResponse as FunctionListDeploymentsResponse
+from .function_list_event_types_response import FunctionListEventTypesResponse as FunctionListEventTypesResponse
+from .introspect_validate_email_response import IntrospectValidateEmailResponse as IntrospectValidateEmailResponse
from .introspect_validate_phone_response import IntrospectValidatePhoneResponse as IntrospectValidatePhoneResponse
from .phone_number_requirements_response import PhoneNumberRequirementsResponse as PhoneNumberRequirementsResponse
from .url_submit_for_verification_params import URLSubmitForVerificationParams as URLSubmitForVerificationParams
from .whatsapp_business_profile_response import WhatsappBusinessProfileResponse as WhatsappBusinessProfileResponse
from .whatsapp_business_profile_vertical import WhatsappBusinessProfileVertical as WhatsappBusinessProfileVertical
+from .function_rollback_deployment_params import FunctionRollbackDeploymentParams as FunctionRollbackDeploymentParams
from .regulatory_document_create_response import RegulatoryDocumentCreateResponse as RegulatoryDocumentCreateResponse
from .phone_number_search_available_params import PhoneNumberSearchAvailableParams as PhoneNumberSearchAvailableParams
from .sender_upload_profile_picture_params import SenderUploadProfilePictureParams as SenderUploadProfilePictureParams
from .url_submit_for_verification_response import URLSubmitForVerificationResponse as URLSubmitForVerificationResponse
+from .function_rollback_deployment_response import (
+ FunctionRollbackDeploymentResponse as FunctionRollbackDeploymentResponse,
+)
from .regulatory_document_retrieve_response import (
RegulatoryDocumentRetrieveResponse as RegulatoryDocumentRetrieveResponse,
)
diff --git a/src/zavudev/types/agent_create_params.py b/src/zavudev/types/agent_create_params.py
new file mode 100644
index 0000000..cfa38c8
--- /dev/null
+++ b/src/zavudev/types/agent_create_params.py
@@ -0,0 +1,140 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._types import SequenceNotStr
+from .._utils import PropertyInfo
+from .senders.agent_provider import AgentProvider
+
+__all__ = ["AgentCreateParams", "Voice"]
+
+
+class AgentCreateParams(TypedDict, total=False):
+ model: Required[str]
+
+ name: Required[str]
+
+ provider: Required[AgentProvider]
+ """LLM provider for the AI agent."""
+
+ system_prompt: Required[Annotated[str, PropertyInfo(alias="systemPrompt")]]
+
+ context_window_messages: Annotated[int, PropertyInfo(alias="contextWindowMessages")]
+
+ include_contact_metadata: Annotated[bool, PropertyInfo(alias="includeContactMetadata")]
+
+ max_tokens: Annotated[int, PropertyInfo(alias="maxTokens")]
+
+ temperature: float
+
+ trigger_on_channels: Annotated[SequenceNotStr[str], PropertyInfo(alias="triggerOnChannels")]
+
+ trigger_on_message_types: Annotated[SequenceNotStr[str], PropertyInfo(alias="triggerOnMessageTypes")]
+
+ voice: Voice
+ """Voice Agent configuration on a sender's AI agent.
+
+ Controls how the agent behaves on inbound and outbound phone calls through
+ Zavu's managed voice pipeline (speech recognition, the agent's LLM, and speech
+ synthesis, with real-time interruption handling). Requires the Voice Agents
+ feature to be enabled for your team.
+ """
+
+
+class Voice(TypedDict, total=False):
+ """Voice Agent configuration on a sender's AI agent.
+
+ Controls how the agent behaves on inbound and outbound phone calls through Zavu's managed voice pipeline (speech recognition, the agent's LLM, and speech synthesis, with real-time interruption handling). Requires the Voice Agents feature to be enabled for your team.
+ """
+
+ enabled: Required[bool]
+ """Whether the agent handles voice calls.
+
+ When false, the sender's number is not answered by the voice agent and outbound
+ calls are rejected.
+ """
+
+ greeting: str
+ """Opening line the agent speaks when the call connects.
+
+ If omitted, the agent waits for the caller to speak first.
+ """
+
+ greetings: Dict[str, str]
+ """Greeting per language, keyed by language code.
+
+ Used when the caller's language differs from the one `greeting` is written in.
+ """
+
+ interruptible: bool
+ """Whether the caller can interrupt the agent while it is speaking (barge-in).
+
+ When true, the agent stops talking as soon as the caller starts.
+ """
+
+ language: str
+ """BCP-47 language code used for both speech recognition and speech synthesis (e.g.
+
+ `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.
+ """
+
+ max_call_duration_minutes: Annotated[int, PropertyInfo(alias="maxCallDurationMinutes")]
+ """Hard limit on call length in minutes. The call ends automatically when reached."""
+
+ max_idle_seconds: Annotated[int, PropertyInfo(alias="maxIdleSeconds")]
+ """How long the agent waits during silence before ending the call."""
+
+ model: str
+ """
+ Model that runs the conversation, co-located in the voice network for lowest
+ latency. Independent of the model used for text messaging. Derived from the
+ agent's text model when omitted.
+ """
+
+ record_calls: Annotated[bool, PropertyInfo(alias="recordCalls")]
+ """Whether the call audio is recorded."""
+
+ stt_model: Annotated[str, PropertyInfo(alias="sttModel")]
+ """Speech-recognition model. Uses the default when omitted."""
+
+ stt_provider: Annotated[str, PropertyInfo(alias="sttProvider")]
+ """Speech-recognition provider. Uses the default when omitted."""
+
+ transfer_phone_number: Annotated[str, PropertyInfo(alias="transferPhoneNumber")]
+ """E.164 phone number the agent can transfer the call to.
+
+ When set, the agent is given a transfer tool it can use to hand the call to a
+ human.
+ """
+
+ tts_provider: Annotated[str, PropertyInfo(alias="ttsProvider")]
+ """Speech-synthesis provider. Uses the default when omitted."""
+
+ tts_voice_id: Annotated[str, PropertyInfo(alias="ttsVoiceId")]
+ """Identifier of the synthesized voice that speaks.
+
+ Choose from the voices available in the dashboard. Uses a neutral default when
+ omitted.
+ """
+
+ voicemail_action: Annotated[Literal["hangup", "leave_message"], PropertyInfo(alias="voicemailAction")]
+ """
+ What the agent does when an answering machine or voicemail is detected on an
+ outbound call.
+ """
+
+ voicemail_message: Annotated[str, PropertyInfo(alias="voicemailMessage")]
+ """Message spoken when `voicemailAction` is `leave_message`.
+
+ Falls back to `greeting` when omitted.
+ """
+
+ voice_speed: Annotated[float, PropertyInfo(alias="voiceSpeed")]
+ """Speech rate.
+
+ 1.0 is natural. Only honoured by voices that support rate control; ignored by
+ the others.
+ """
diff --git a/src/zavudev/types/agent_create_response.py b/src/zavudev/types/agent_create_response.py
new file mode 100644
index 0000000..ec39d67
--- /dev/null
+++ b/src/zavudev/types/agent_create_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+from .senders.agent.agent import Agent
+
+__all__ = ["AgentCreateResponse"]
+
+
+class AgentCreateResponse(BaseModel):
+ agent: Agent
+ """AI Agent configuration for a sender."""
diff --git a/src/zavudev/types/agent_list_params.py b/src/zavudev/types/agent_list_params.py
new file mode 100644
index 0000000..3e5638f
--- /dev/null
+++ b/src/zavudev/types/agent_list_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["AgentListParams"]
+
+
+class AgentListParams(TypedDict, total=False):
+ cursor: str
+
+ limit: int
diff --git a/src/zavudev/types/agent_list_voices_params.py b/src/zavudev/types/agent_list_voices_params.py
new file mode 100644
index 0000000..4b4de91
--- /dev/null
+++ b/src/zavudev/types/agent_list_voices_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["AgentListVoicesParams"]
+
+
+class AgentListVoicesParams(TypedDict, total=False):
+ language: str
+ """BCP-47 tag (`en`, `es`, `pt-BR`). Omit, or pass `auto`, for every voice."""
diff --git a/src/zavudev/types/agent_list_voices_response.py b/src/zavudev/types/agent_list_voices_response.py
new file mode 100644
index 0000000..f6c6038
--- /dev/null
+++ b/src/zavudev/types/agent_list_voices_response.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from .._models import BaseModel
+
+__all__ = ["AgentListVoicesResponse", "Item"]
+
+
+class Item(BaseModel):
+ id: str
+ """Value for `voice.ttsVoiceId`."""
+
+ language: str
+
+ name: str
+
+
+class AgentListVoicesResponse(BaseModel):
+ items: List[Item]
+
+ languages: List[str]
+ """Languages an agent can be pinned to. `auto` follows the caller."""
+
+ total: Optional[int] = None
+ """Voices in the catalog, before filtering."""
diff --git a/src/zavudev/types/agent_retrieve_response.py b/src/zavudev/types/agent_retrieve_response.py
new file mode 100644
index 0000000..58155d0
--- /dev/null
+++ b/src/zavudev/types/agent_retrieve_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+from .senders.agent.agent import Agent
+
+__all__ = ["AgentRetrieveResponse"]
+
+
+class AgentRetrieveResponse(BaseModel):
+ agent: Agent
+ """AI Agent configuration for a sender."""
diff --git a/src/zavudev/types/agent_template_list_response.py b/src/zavudev/types/agent_template_list_response.py
new file mode 100644
index 0000000..dd1bde8
--- /dev/null
+++ b/src/zavudev/types/agent_template_list_response.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["AgentTemplateListResponse", "Item"]
+
+
+class Item(BaseModel):
+ """Compact catalog entry for a factory agent."""
+
+ id: str
+
+ category: Literal["sales", "support", "frontDesk", "ops"]
+
+ name: str
+
+ summary: str
+
+ tool_count: int = FieldInfo(alias="toolCount")
+
+ voice: bool
+ """Whether this agent answers phone calls."""
+
+
+class AgentTemplateListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/zavudev/types/agent_template_retrieve_response.py b/src/zavudev/types/agent_template_retrieve_response.py
new file mode 100644
index 0000000..2533d10
--- /dev/null
+++ b/src/zavudev/types/agent_template_retrieve_response.py
@@ -0,0 +1,57 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["AgentTemplateRetrieveResponse", "Template", "TemplateFile", "TemplateRequiredSecret"]
+
+
+class TemplateFile(BaseModel):
+ content: str
+ """File contents to write verbatim."""
+
+ path: str
+
+
+class TemplateRequiredSecret(BaseModel):
+ hint: str
+
+ key: str
+
+
+class Template(BaseModel):
+ """
+ A fully rendered factory agent: the function files to scaffold plus the secrets it needs. Returned by GET /v1/agent-templates/{templateId} and consumed by `npx zavudev agents pull`.
+ """
+
+ id: str
+
+ category: Literal["sales", "support", "frontDesk", "ops"]
+
+ default_slug: str = FieldInfo(alias="defaultSlug")
+
+ dependencies: Dict[str, str]
+ """npm dependencies for the scaffolded function."""
+
+ files: List[TemplateFile]
+
+ name: str
+
+ required_secrets: List[TemplateRequiredSecret] = FieldInfo(alias="requiredSecrets")
+
+ summary: str
+
+ voice: bool
+
+
+class AgentTemplateRetrieveResponse(BaseModel):
+ template: Template
+ """
+ A fully rendered factory agent: the function files to scaffold plus the secrets
+ it needs. Returned by GET /v1/agent-templates/{templateId} and consumed by
+ `npx zavudev agents pull`.
+ """
diff --git a/src/zavudev/types/agent_test_params.py b/src/zavudev/types/agent_test_params.py
new file mode 100644
index 0000000..438fecc
--- /dev/null
+++ b/src/zavudev/types/agent_test_params.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["AgentTestParams", "History"]
+
+
+class AgentTestParams(TypedDict, total=False):
+ message: Required[str]
+ """What to say to the agent."""
+
+ execute_tools: Annotated[bool, PropertyInfo(alias="executeTools")]
+ """Run the tools the agent calls instead of reporting the choice and stopping.
+
+ Off by default because a tool handler talks to the outside world: a rehearsal
+ that charges a card is not a rehearsal. Turn it on to exercise the loop that
+ actually matters — the model picks a tool, the handler answers, the model
+ replies with the result — without sending a message to anyone. What ran comes
+ back in `executedToolCalls`.
+ """
+
+ history: Iterable[History]
+ """
+ Prior turns, oldest first, to exercise multi-turn behaviour without persisting a
+ thread. Trimmed to the agent's context window.
+ """
+
+ use_knowledge_base: Annotated[bool, PropertyInfo(alias="useKnowledgeBase")]
+ """
+ Set false to skip retrieval and isolate prompt behaviour from the knowledge
+ base.
+ """
+
+
+class History(TypedDict, total=False):
+ content: Required[str]
+
+ role: Required[Literal["user", "assistant"]]
diff --git a/src/zavudev/types/agent_test_response.py b/src/zavudev/types/agent_test_response.py
new file mode 100644
index 0000000..7c44b49
--- /dev/null
+++ b/src/zavudev/types/agent_test_response.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["AgentTestResponse", "ExecutedToolCall"]
+
+
+class ExecutedToolCall(BaseModel):
+ name: str
+
+ ok: bool
+
+ error: Optional[str] = None
+
+
+class AgentTestResponse(BaseModel):
+ error: Optional[str] = None
+
+ input_tokens: int = FieldInfo(alias="inputTokens")
+
+ knowledge_chunks_used: int = FieldInfo(alias="knowledgeChunksUsed")
+ """Knowledge-base chunks retrieved for this message.
+
+ Zero means the answer was not grounded in your documents.
+ """
+
+ latency_ms: int = FieldInfo(alias="latencyMs")
+
+ output_tokens: int = FieldInfo(alias="outputTokens")
+
+ success: bool
+
+ text: Optional[str] = None
+ """What the agent would reply."""
+
+ warnings: List[str]
+ """Things that are true of this agent but that a dry run cannot prove.
+
+ Surfaced so a passing dry run is never mistaken for proof that the agent works
+ live.
+
+ - The agent being disabled.
+ - Enabled tools that were **not offered to the model** here — the model never
+ saw them, so a reply that looks like a lookup was invented. Live conversations
+ on every channel do offer them; running them here would cause real side
+ effects.
+ - An agent whose sender has none of the channels it triggers on, which answers
+ every dry run and no real message.
+ - Contact metadata that exists on a real conversation but not here.
+ """
+
+ executed_tool_calls: Optional[List[ExecutedToolCall]] = FieldInfo(alias="executedToolCalls", default=None)
+ """Tools that actually ran, in order, when the request set `executeTools`.
+
+ Empty on a normal dry run, where nothing is executed. An entry with `ok: false`
+ means the agent saw an error and answered around it, which is what a customer
+ would have received.
+ """
diff --git a/src/zavudev/types/agent_update_params.py b/src/zavudev/types/agent_update_params.py
new file mode 100644
index 0000000..7531e56
--- /dev/null
+++ b/src/zavudev/types/agent_update_params.py
@@ -0,0 +1,142 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._types import SequenceNotStr
+from .._utils import PropertyInfo
+from .senders.agent_provider import AgentProvider
+
+__all__ = ["AgentUpdateParams", "Voice"]
+
+
+class AgentUpdateParams(TypedDict, total=False):
+ api_key: Annotated[str, PropertyInfo(alias="apiKey")]
+
+ context_window_messages: Annotated[int, PropertyInfo(alias="contextWindowMessages")]
+
+ enabled: bool
+
+ include_contact_metadata: Annotated[bool, PropertyInfo(alias="includeContactMetadata")]
+
+ max_tokens: Annotated[Optional[int], PropertyInfo(alias="maxTokens")]
+
+ model: str
+
+ name: str
+
+ provider: AgentProvider
+ """LLM provider for the AI agent."""
+
+ system_prompt: Annotated[str, PropertyInfo(alias="systemPrompt")]
+
+ temperature: Optional[float]
+
+ trigger_on_channels: Annotated[SequenceNotStr[str], PropertyInfo(alias="triggerOnChannels")]
+
+ trigger_on_message_types: Annotated[SequenceNotStr[str], PropertyInfo(alias="triggerOnMessageTypes")]
+
+ voice: Voice
+ """Voice Agent configuration.
+
+ Patch this object to enable voice, change the greeting, or adjust call limits.
+ Requires the Voice Agents feature to be enabled for your team.
+ """
+
+
+class Voice(TypedDict, total=False):
+ """Voice Agent configuration.
+
+ Patch this object to enable voice, change the greeting, or adjust call limits. Requires the Voice Agents feature to be enabled for your team.
+ """
+
+ enabled: Required[bool]
+ """Whether the agent handles voice calls.
+
+ When false, the sender's number is not answered by the voice agent and outbound
+ calls are rejected.
+ """
+
+ greeting: str
+ """Opening line the agent speaks when the call connects.
+
+ If omitted, the agent waits for the caller to speak first.
+ """
+
+ greetings: Dict[str, str]
+ """Greeting per language, keyed by language code.
+
+ Used when the caller's language differs from the one `greeting` is written in.
+ """
+
+ interruptible: bool
+ """Whether the caller can interrupt the agent while it is speaking (barge-in).
+
+ When true, the agent stops talking as soon as the caller starts.
+ """
+
+ language: str
+ """BCP-47 language code used for both speech recognition and speech synthesis (e.g.
+
+ `en`, `es`, `pt-BR`). Auto-detected from the recipient when omitted.
+ """
+
+ max_call_duration_minutes: Annotated[int, PropertyInfo(alias="maxCallDurationMinutes")]
+ """Hard limit on call length in minutes. The call ends automatically when reached."""
+
+ max_idle_seconds: Annotated[int, PropertyInfo(alias="maxIdleSeconds")]
+ """How long the agent waits during silence before ending the call."""
+
+ model: str
+ """
+ Model that runs the conversation, co-located in the voice network for lowest
+ latency. Independent of the model used for text messaging. Derived from the
+ agent's text model when omitted.
+ """
+
+ record_calls: Annotated[bool, PropertyInfo(alias="recordCalls")]
+ """Whether the call audio is recorded."""
+
+ stt_model: Annotated[str, PropertyInfo(alias="sttModel")]
+ """Speech-recognition model. Uses the default when omitted."""
+
+ stt_provider: Annotated[str, PropertyInfo(alias="sttProvider")]
+ """Speech-recognition provider. Uses the default when omitted."""
+
+ transfer_phone_number: Annotated[str, PropertyInfo(alias="transferPhoneNumber")]
+ """E.164 phone number the agent can transfer the call to.
+
+ When set, the agent is given a transfer tool it can use to hand the call to a
+ human.
+ """
+
+ tts_provider: Annotated[str, PropertyInfo(alias="ttsProvider")]
+ """Speech-synthesis provider. Uses the default when omitted."""
+
+ tts_voice_id: Annotated[str, PropertyInfo(alias="ttsVoiceId")]
+ """Identifier of the synthesized voice that speaks.
+
+ Choose from the voices available in the dashboard. Uses a neutral default when
+ omitted.
+ """
+
+ voicemail_action: Annotated[Literal["hangup", "leave_message"], PropertyInfo(alias="voicemailAction")]
+ """
+ What the agent does when an answering machine or voicemail is detected on an
+ outbound call.
+ """
+
+ voicemail_message: Annotated[str, PropertyInfo(alias="voicemailMessage")]
+ """Message spoken when `voicemailAction` is `leave_message`.
+
+ Falls back to `greeting` when omitted.
+ """
+
+ voice_speed: Annotated[float, PropertyInfo(alias="voiceSpeed")]
+ """Speech rate.
+
+ 1.0 is natural. Only honoured by voices that support rate control; ignored by
+ the others.
+ """
diff --git a/src/zavudev/types/agent_update_response.py b/src/zavudev/types/agent_update_response.py
new file mode 100644
index 0000000..c4dcce8
--- /dev/null
+++ b/src/zavudev/types/agent_update_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+from .senders.agent.agent import Agent
+
+__all__ = ["AgentUpdateResponse"]
+
+
+class AgentUpdateResponse(BaseModel):
+ agent: Agent
+ """AI Agent configuration for a sender."""
diff --git a/src/zavudev/types/agents/__init__.py b/src/zavudev/types/agents/__init__.py
new file mode 100644
index 0000000..16c63cb
--- /dev/null
+++ b/src/zavudev/types/agents/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .sender_connect_params import SenderConnectParams as SenderConnectParams
+from .sender_connect_response import SenderConnectResponse as SenderConnectResponse
diff --git a/src/zavudev/types/agents/sender_connect_params.py b/src/zavudev/types/agents/sender_connect_params.py
new file mode 100644
index 0000000..9919687
--- /dev/null
+++ b/src/zavudev/types/agents/sender_connect_params.py
@@ -0,0 +1,14 @@
+# 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
+
+__all__ = ["SenderConnectParams"]
+
+
+class SenderConnectParams(TypedDict, total=False):
+ sender_id: Required[Annotated[str, PropertyInfo(alias="senderId")]]
+ """Sender to connect."""
diff --git a/src/zavudev/types/agents/sender_connect_response.py b/src/zavudev/types/agents/sender_connect_response.py
new file mode 100644
index 0000000..cead704
--- /dev/null
+++ b/src/zavudev/types/agents/sender_connect_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+from ..senders.agent.agent import Agent
+
+__all__ = ["SenderConnectResponse"]
+
+
+class SenderConnectResponse(BaseModel):
+ agent: Agent
+ """AI Agent configuration for a sender."""
diff --git a/src/zavudev/types/call_create_params.py b/src/zavudev/types/call_create_params.py
new file mode 100644
index 0000000..d86daa4
--- /dev/null
+++ b/src/zavudev/types/call_create_params.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["CallCreateParams"]
+
+
+class CallCreateParams(TypedDict, total=False):
+ to: Required[str]
+ """Recipient phone number in E.164 format."""
+
+ greeting: str
+ """Overrides the agent's configured greeting for this call only."""
+
+ language: str
+ """
+ Language the agent speaks on this call only, as a BCP-47 tag (`en`, `es`,
+ `es-ES`, `pt-BR`), or `auto` to detect the caller's language and follow it.
+ Overrides the agent's configured language for speech recognition, the agent's
+ replies, and the synthesized voice. If the agent uses a custom voice you
+ supplied, that voice is kept and only the language changes. When omitted, the
+ agent's configured language is used.
+ """
+
+ max_duration_minutes: Annotated[int, PropertyInfo(alias="maxDurationMinutes")]
+ """Overrides the agent's maximum call duration for this call only."""
+
+ metadata: Dict[str, str]
+ """Arbitrary metadata to associate with the call.
+
+ Returned on the call object and included in voice webhooks.
+ """
+
+ sender_id: Annotated[str, PropertyInfo(alias="senderId")]
+ """Sender profile that places the call.
+
+ Uses the project's default sender if omitted. The sender's agent must have voice
+ enabled.
+ """
diff --git a/src/zavudev/types/call_create_response.py b/src/zavudev/types/call_create_response.py
new file mode 100644
index 0000000..5b547cf
--- /dev/null
+++ b/src/zavudev/types/call_create_response.py
@@ -0,0 +1,110 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CallCreateResponse", "Call", "CallTranscript"]
+
+
+class CallTranscript(BaseModel):
+ """A single turn in a voice call transcript."""
+
+ role: Literal["user", "assistant", "tool"]
+ """Who produced the turn.
+
+ `tool` records a tool call the agent made during the conversation.
+ """
+
+ seq: int
+ """Ordinal position of the turn within the call, starting at 0."""
+
+ text: str
+ """
+ Transcribed speech for `user` and `assistant` turns, or a JSON summary of the
+ tool call for `tool` turns.
+ """
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the turn ended."""
+
+ started_at: Optional[datetime] = FieldInfo(alias="startedAt", default=None)
+ """When the turn started."""
+
+
+class Call(BaseModel):
+ id: str
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ direction: Literal["inbound", "outbound"]
+ """
+ Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+ """
+
+ from_: str = FieldInfo(alias="from")
+ """Caller phone number in E.164 format.
+
+ Your sender's number for outbound calls; the caller's number for inbound calls.
+ """
+
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ """Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+ """
+
+ to: str
+ """Callee phone number in E.164 format."""
+
+ answered_at: Optional[datetime] = FieldInfo(alias="answeredAt", default=None)
+ """When the call was answered."""
+
+ cost: Optional[float] = None
+ """
+ Total cost of the call in USD, combining the managed voice pipeline per-minute
+ charge and telephony. Available once the call has ended.
+ """
+
+ duration_seconds: Optional[int] = FieldInfo(alias="durationSeconds", default=None)
+ """Billable talk time in seconds, measured from answer to hangup."""
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the call ended."""
+
+ end_reason: Optional[str] = FieldInfo(alias="endReason", default=None)
+ """Why the call ended (e.g.
+
+ `agent_ended`, `max_duration`, `transfer`, `hangup`). Present once the call is
+ no longer active.
+ """
+
+ metadata: Optional[Dict[str, str]] = None
+ """Arbitrary metadata you attached when creating the call."""
+
+ transcript: Optional[List[CallTranscript]] = None
+ """Ordered transcript of the call.
+
+ Included when retrieving a single call; omitted from list responses.
+ """
+
+ turn_count: Optional[int] = FieldInfo(alias="turnCount", default=None)
+ """Number of conversation turns exchanged during the call."""
+
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
+
+
+class CallCreateResponse(BaseModel):
+ call: Call
diff --git a/src/zavudev/types/call_hangup_response.py b/src/zavudev/types/call_hangup_response.py
new file mode 100644
index 0000000..d606862
--- /dev/null
+++ b/src/zavudev/types/call_hangup_response.py
@@ -0,0 +1,110 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CallHangupResponse", "Call", "CallTranscript"]
+
+
+class CallTranscript(BaseModel):
+ """A single turn in a voice call transcript."""
+
+ role: Literal["user", "assistant", "tool"]
+ """Who produced the turn.
+
+ `tool` records a tool call the agent made during the conversation.
+ """
+
+ seq: int
+ """Ordinal position of the turn within the call, starting at 0."""
+
+ text: str
+ """
+ Transcribed speech for `user` and `assistant` turns, or a JSON summary of the
+ tool call for `tool` turns.
+ """
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the turn ended."""
+
+ started_at: Optional[datetime] = FieldInfo(alias="startedAt", default=None)
+ """When the turn started."""
+
+
+class Call(BaseModel):
+ id: str
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ direction: Literal["inbound", "outbound"]
+ """
+ Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+ """
+
+ from_: str = FieldInfo(alias="from")
+ """Caller phone number in E.164 format.
+
+ Your sender's number for outbound calls; the caller's number for inbound calls.
+ """
+
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ """Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+ """
+
+ to: str
+ """Callee phone number in E.164 format."""
+
+ answered_at: Optional[datetime] = FieldInfo(alias="answeredAt", default=None)
+ """When the call was answered."""
+
+ cost: Optional[float] = None
+ """
+ Total cost of the call in USD, combining the managed voice pipeline per-minute
+ charge and telephony. Available once the call has ended.
+ """
+
+ duration_seconds: Optional[int] = FieldInfo(alias="durationSeconds", default=None)
+ """Billable talk time in seconds, measured from answer to hangup."""
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the call ended."""
+
+ end_reason: Optional[str] = FieldInfo(alias="endReason", default=None)
+ """Why the call ended (e.g.
+
+ `agent_ended`, `max_duration`, `transfer`, `hangup`). Present once the call is
+ no longer active.
+ """
+
+ metadata: Optional[Dict[str, str]] = None
+ """Arbitrary metadata you attached when creating the call."""
+
+ transcript: Optional[List[CallTranscript]] = None
+ """Ordered transcript of the call.
+
+ Included when retrieving a single call; omitted from list responses.
+ """
+
+ turn_count: Optional[int] = FieldInfo(alias="turnCount", default=None)
+ """Number of conversation turns exchanged during the call."""
+
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
+
+
+class CallHangupResponse(BaseModel):
+ call: Call
diff --git a/src/zavudev/types/call_list_params.py b/src/zavudev/types/call_list_params.py
new file mode 100644
index 0000000..f28880d
--- /dev/null
+++ b/src/zavudev/types/call_list_params.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["CallListParams"]
+
+
+class CallListParams(TypedDict, total=False):
+ cursor: str
+
+ direction: Literal["inbound", "outbound"]
+ """
+ Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+ """
+
+ limit: int
+
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ """Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+ """
diff --git a/src/zavudev/types/call_list_response.py b/src/zavudev/types/call_list_response.py
new file mode 100644
index 0000000..668f202
--- /dev/null
+++ b/src/zavudev/types/call_list_response.py
@@ -0,0 +1,106 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CallListResponse", "Transcript"]
+
+
+class Transcript(BaseModel):
+ """A single turn in a voice call transcript."""
+
+ role: Literal["user", "assistant", "tool"]
+ """Who produced the turn.
+
+ `tool` records a tool call the agent made during the conversation.
+ """
+
+ seq: int
+ """Ordinal position of the turn within the call, starting at 0."""
+
+ text: str
+ """
+ Transcribed speech for `user` and `assistant` turns, or a JSON summary of the
+ tool call for `tool` turns.
+ """
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the turn ended."""
+
+ started_at: Optional[datetime] = FieldInfo(alias="startedAt", default=None)
+ """When the turn started."""
+
+
+class CallListResponse(BaseModel):
+ id: str
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ direction: Literal["inbound", "outbound"]
+ """
+ Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+ """
+
+ from_: str = FieldInfo(alias="from")
+ """Caller phone number in E.164 format.
+
+ Your sender's number for outbound calls; the caller's number for inbound calls.
+ """
+
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ """Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+ """
+
+ to: str
+ """Callee phone number in E.164 format."""
+
+ answered_at: Optional[datetime] = FieldInfo(alias="answeredAt", default=None)
+ """When the call was answered."""
+
+ cost: Optional[float] = None
+ """
+ Total cost of the call in USD, combining the managed voice pipeline per-minute
+ charge and telephony. Available once the call has ended.
+ """
+
+ duration_seconds: Optional[int] = FieldInfo(alias="durationSeconds", default=None)
+ """Billable talk time in seconds, measured from answer to hangup."""
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the call ended."""
+
+ end_reason: Optional[str] = FieldInfo(alias="endReason", default=None)
+ """Why the call ended (e.g.
+
+ `agent_ended`, `max_duration`, `transfer`, `hangup`). Present once the call is
+ no longer active.
+ """
+
+ metadata: Optional[Dict[str, str]] = None
+ """Arbitrary metadata you attached when creating the call."""
+
+ transcript: Optional[List[Transcript]] = None
+ """Ordered transcript of the call.
+
+ Included when retrieving a single call; omitted from list responses.
+ """
+
+ turn_count: Optional[int] = FieldInfo(alias="turnCount", default=None)
+ """Number of conversation turns exchanged during the call."""
+
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
diff --git a/src/zavudev/types/call_retrieve_response.py b/src/zavudev/types/call_retrieve_response.py
new file mode 100644
index 0000000..ced058c
--- /dev/null
+++ b/src/zavudev/types/call_retrieve_response.py
@@ -0,0 +1,110 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["CallRetrieveResponse", "Call", "CallTranscript"]
+
+
+class CallTranscript(BaseModel):
+ """A single turn in a voice call transcript."""
+
+ role: Literal["user", "assistant", "tool"]
+ """Who produced the turn.
+
+ `tool` records a tool call the agent made during the conversation.
+ """
+
+ seq: int
+ """Ordinal position of the turn within the call, starting at 0."""
+
+ text: str
+ """
+ Transcribed speech for `user` and `assistant` turns, or a JSON summary of the
+ tool call for `tool` turns.
+ """
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the turn ended."""
+
+ started_at: Optional[datetime] = FieldInfo(alias="startedAt", default=None)
+ """When the turn started."""
+
+
+class Call(BaseModel):
+ id: str
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ direction: Literal["inbound", "outbound"]
+ """
+ Whether the call was placed by Zavu (outbound) or received from a caller
+ (inbound).
+ """
+
+ from_: str = FieldInfo(alias="from")
+ """Caller phone number in E.164 format.
+
+ Your sender's number for outbound calls; the caller's number for inbound calls.
+ """
+
+ status: Literal["queued", "ringing", "in_progress", "completed", "failed", "busy", "no_answer", "canceled"]
+ """Lifecycle status of a voice call.
+
+ - `queued`: outbound call created, not yet dialing.
+ - `ringing`: dialing (outbound) or received and ringing (inbound).
+ - `in_progress`: answered, the agent is connected.
+ - `completed`: ended after a conversation.
+ - `failed`: could not be completed.
+ - `busy`: the line was busy.
+ - `no_answer`: rang but was not answered.
+ - `canceled`: canceled before it was answered.
+ """
+
+ to: str
+ """Callee phone number in E.164 format."""
+
+ answered_at: Optional[datetime] = FieldInfo(alias="answeredAt", default=None)
+ """When the call was answered."""
+
+ cost: Optional[float] = None
+ """
+ Total cost of the call in USD, combining the managed voice pipeline per-minute
+ charge and telephony. Available once the call has ended.
+ """
+
+ duration_seconds: Optional[int] = FieldInfo(alias="durationSeconds", default=None)
+ """Billable talk time in seconds, measured from answer to hangup."""
+
+ ended_at: Optional[datetime] = FieldInfo(alias="endedAt", default=None)
+ """When the call ended."""
+
+ end_reason: Optional[str] = FieldInfo(alias="endReason", default=None)
+ """Why the call ended (e.g.
+
+ `agent_ended`, `max_duration`, `transfer`, `hangup`). Present once the call is
+ no longer active.
+ """
+
+ metadata: Optional[Dict[str, str]] = None
+ """Arbitrary metadata you attached when creating the call."""
+
+ transcript: Optional[List[CallTranscript]] = None
+ """Ordered transcript of the call.
+
+ Included when retrieving a single call; omitted from list responses.
+ """
+
+ turn_count: Optional[int] = FieldInfo(alias="turnCount", default=None)
+ """Number of conversation turns exchanged during the call."""
+
+ updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
+
+
+class CallRetrieveResponse(BaseModel):
+ call: Call
diff --git a/src/zavudev/types/contact.py b/src/zavudev/types/contact.py
index c2c6775..e56807d 100644
--- a/src/zavudev/types/contact.py
+++ b/src/zavudev/types/contact.py
@@ -50,7 +50,4 @@ class Contact(BaseModel):
profile_name: Optional[str] = FieldInfo(alias="profileName", default=None)
"""Contact's WhatsApp profile name. Only available for WhatsApp contacts."""
- suggested_merge_with: Optional[str] = FieldInfo(alias="suggestedMergeWith", default=None)
- """ID of a contact suggested for merging."""
-
updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None)
diff --git a/src/zavudev/types/contact_list_params.py b/src/zavudev/types/contact_list_params.py
index b1b11d1..8fa96d4 100644
--- a/src/zavudev/types/contact_list_params.py
+++ b/src/zavudev/types/contact_list_params.py
@@ -4,6 +4,7 @@
from typing_extensions import Annotated, TypedDict
+from .._types import SequenceNotStr
from .._utils import PropertyInfo
__all__ = ["ContactListParams"]
@@ -11,7 +12,35 @@
class ContactListParams(TypedDict, total=False):
cursor: str
+ """Opaque cursor from a previous response's `nextCursor`. Do not construct it."""
limit: int
phone_number: Annotated[str, PropertyInfo(alias="phoneNumber")]
+ """Exact match on the contact's primary phone number, in E.164."""
+
+ search: str
+ """
+ Free-text match over the contact's name (`displayName` and the WhatsApp profile
+ name), phone numbers and email addresses. Case- and accent-insensitive. A phone
+ number matches on a trailing fragment too, so `5551234` finds `+14155551234`.
+
+ Contacts created automatically from an inbound message have no `displayName` —
+ they are matched by their identifier until you set one with
+ `PATCH /v1/contacts/{contactId}`.
+
+ Results come back in relevance order rather than newest-first. `cursor` is
+ opaque in both modes; pass back exactly what the previous response returned, and
+ start a new pagination run when the search term changes.
+ """
+
+ tag: SequenceNotStr[str]
+ """Tag name.
+
+ Repeatable: `?tag=vip&tag=chile` returns contacts carrying **every** tag given,
+ not any of them — the same rule the dashboard filter applies.
+
+ Tags are matched by name, case-insensitively. An unknown tag returns 400 rather
+ than being ignored, because a typo that silently matched every contact would be
+ a worse answer than an error.
+ """
diff --git a/src/zavudev/types/contact_update_params.py b/src/zavudev/types/contact_update_params.py
index 190f967..97d0d0b 100644
--- a/src/zavudev/types/contact_update_params.py
+++ b/src/zavudev/types/contact_update_params.py
@@ -17,4 +17,12 @@ class ContactUpdateParams(TypedDict, total=False):
]
"""Preferred channel for this contact. Set to null to clear."""
+ display_name: Annotated[Optional[str], PropertyInfo(alias="displayName")]
+ """Human-readable name for this contact.
+
+ Set to null to clear it and fall back to the contact's identifier. Contacts
+ created automatically from an inbound message have no display name until you set
+ one.
+ """
+
metadata: Dict[str, str]
diff --git a/src/zavudev/types/conversation_list_messages_params.py b/src/zavudev/types/conversation_list_messages_params.py
new file mode 100644
index 0000000..5c1efdb
--- /dev/null
+++ b/src/zavudev/types/conversation_list_messages_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["ConversationListMessagesParams"]
+
+
+class ConversationListMessagesParams(TypedDict, total=False):
+ cursor: str
+ """Opaque cursor from a previous response's `nextCursor`."""
+
+ limit: int
diff --git a/src/zavudev/types/conversation_list_params.py b/src/zavudev/types/conversation_list_params.py
new file mode 100644
index 0000000..f063113
--- /dev/null
+++ b/src/zavudev/types/conversation_list_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["ConversationListParams"]
+
+
+class ConversationListParams(TypedDict, total=False):
+ channel: Literal["sms", "sms_oneway", "whatsapp", "email", "telegram", "instagram", "messenger", "voice"]
+ """Keep only threads that have carried this channel."""
+
+ cursor: str
+ """Opaque cursor from a previous response's `nextCursor`. Do not construct it."""
+
+ limit: int
+
+ search: str
+ """
+ Search threads by identity: phone number (any format — `+1 (555) 123-4567` and
+ `15551234567` both match), email address (full or local part), WhatsApp group
+ subject, WhatsApp username, or BSUID. Matching is by whole word, with prefix
+ matching on the last term, so `mar` finds `maria@example.com` and `+1555` finds
+ `+15551234567`; a fragment from the middle or end of a number (`4567`) does not
+ match.
+
+ It does **not** search message bodies — only who the thread is with.
+
+ Results come back ranked by relevance rather than by recency, so the usual "most
+ recently active first" ordering does not apply while `q` is set. `senderId` and
+ `channel` still narrow the results, and `cursor` paginates them as usual. An
+ empty or whitespace-only `q` returns no items rather than the full list.
+ """
+
+ sender_id: Annotated[str, PropertyInfo(alias="senderId")]
+ """Keep only threads last handled by this sender."""
diff --git a/src/zavudev/types/conversation_list_response.py b/src/zavudev/types/conversation_list_response.py
new file mode 100644
index 0000000..60426ec
--- /dev/null
+++ b/src/zavudev/types/conversation_list_response.py
@@ -0,0 +1,109 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .channel import Channel
+from .._models import BaseModel
+
+__all__ = ["ConversationListResponse", "LastMessage", "Group", "Whatsapp"]
+
+
+class LastMessage(BaseModel):
+ """
+ Denormalized preview of the most recent message, so a thread list needs no extra fetch.
+ """
+
+ id: str
+
+ at: datetime
+
+ channel: Channel
+ """Delivery channel. Use 'auto' for intelligent routing."""
+
+ direction: Literal["inbound", "outbound"]
+
+ text: str
+ """Text or caption. Empty when the last message carried no text (e.g. media)."""
+
+
+class Group(BaseModel):
+ """Present when the thread is a group chat rather than a one-to-one conversation."""
+
+ id: str
+
+ participant_count: Optional[int] = FieldInfo(alias="participantCount", default=None)
+
+ subject: Optional[str] = None
+
+
+class Whatsapp(BaseModel):
+ """WhatsApp identity, present when the contact adopted a username."""
+
+ bsuid: Optional[str] = None
+ """Business-scoped user ID. Can be used as `to` when sending."""
+
+ username: Optional[str] = None
+
+
+class ConversationListResponse(BaseModel):
+ """An inbox thread with one contact.
+
+ A conversation groups every message exchanged with that contact across channels, so a contact who writes on WhatsApp and later by email stays in one thread.
+ """
+
+ id: str
+
+ channels: List[str]
+ """Every channel this thread has carried messages on."""
+
+ contact_identifier: str = FieldInfo(alias="contactIdentifier")
+ """
+ The key this thread is filed under: a phone number in E.164, a WhatsApp
+ business-scoped user ID (BSUID), a numeric chat ID
+ (Telegram/Instagram/Messenger), or a group JID. It is not always a phone number,
+ so do not parse it as one.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ last_message: LastMessage = FieldInfo(alias="lastMessage")
+ """
+ Denormalized preview of the most recent message, so a thread list needs no extra
+ fetch.
+ """
+
+ message_count: int = FieldInfo(alias="messageCount")
+
+ unread_count: int = FieldInfo(alias="unreadCount")
+ """Inbound messages not yet marked read.
+
+ Reset with POST /v1/conversations/{conversationId}/read.
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ contact_id: Optional[str] = FieldInfo(alias="contactId", default=None)
+ """ID of the contact this thread belongs to.
+
+ Absent on group threads and on threads whose contact has not been resolved yet.
+ """
+
+ email: Optional[str] = None
+ """Email address of the thread, when the contact was reached by email."""
+
+ group: Optional[Group] = None
+ """Present when the thread is a group chat rather than a one-to-one conversation."""
+
+ sender_id: Optional[str] = FieldInfo(alias="senderId", default=None)
+ """Sender that last handled this thread.
+
+ Use it as the `Zavu-Sender` header when replying so the answer leaves from the
+ same number the contact knows.
+ """
+
+ whatsapp: Optional[Whatsapp] = None
+ """WhatsApp identity, present when the contact adopted a username."""
diff --git a/src/zavudev/types/conversation_mark_as_read_response.py b/src/zavudev/types/conversation_mark_as_read_response.py
new file mode 100644
index 0000000..6df3aa7
--- /dev/null
+++ b/src/zavudev/types/conversation_mark_as_read_response.py
@@ -0,0 +1,124 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .channel import Channel
+from .._models import BaseModel
+
+__all__ = [
+ "ConversationMarkAsReadResponse",
+ "Conversation",
+ "ConversationLastMessage",
+ "ConversationGroup",
+ "ConversationWhatsapp",
+]
+
+
+class ConversationLastMessage(BaseModel):
+ """
+ Denormalized preview of the most recent message, so a thread list needs no extra fetch.
+ """
+
+ id: str
+
+ at: datetime
+
+ channel: Channel
+ """Delivery channel. Use 'auto' for intelligent routing."""
+
+ direction: Literal["inbound", "outbound"]
+
+ text: str
+ """Text or caption. Empty when the last message carried no text (e.g. media)."""
+
+
+class ConversationGroup(BaseModel):
+ """Present when the thread is a group chat rather than a one-to-one conversation."""
+
+ id: str
+
+ participant_count: Optional[int] = FieldInfo(alias="participantCount", default=None)
+
+ subject: Optional[str] = None
+
+
+class ConversationWhatsapp(BaseModel):
+ """WhatsApp identity, present when the contact adopted a username."""
+
+ bsuid: Optional[str] = None
+ """Business-scoped user ID. Can be used as `to` when sending."""
+
+ username: Optional[str] = None
+
+
+class Conversation(BaseModel):
+ """An inbox thread with one contact.
+
+ A conversation groups every message exchanged with that contact across channels, so a contact who writes on WhatsApp and later by email stays in one thread.
+ """
+
+ id: str
+
+ channels: List[str]
+ """Every channel this thread has carried messages on."""
+
+ contact_identifier: str = FieldInfo(alias="contactIdentifier")
+ """
+ The key this thread is filed under: a phone number in E.164, a WhatsApp
+ business-scoped user ID (BSUID), a numeric chat ID
+ (Telegram/Instagram/Messenger), or a group JID. It is not always a phone number,
+ so do not parse it as one.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ last_message: ConversationLastMessage = FieldInfo(alias="lastMessage")
+ """
+ Denormalized preview of the most recent message, so a thread list needs no extra
+ fetch.
+ """
+
+ message_count: int = FieldInfo(alias="messageCount")
+
+ unread_count: int = FieldInfo(alias="unreadCount")
+ """Inbound messages not yet marked read.
+
+ Reset with POST /v1/conversations/{conversationId}/read.
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ contact_id: Optional[str] = FieldInfo(alias="contactId", default=None)
+ """ID of the contact this thread belongs to.
+
+ Absent on group threads and on threads whose contact has not been resolved yet.
+ """
+
+ email: Optional[str] = None
+ """Email address of the thread, when the contact was reached by email."""
+
+ group: Optional[ConversationGroup] = None
+ """Present when the thread is a group chat rather than a one-to-one conversation."""
+
+ sender_id: Optional[str] = FieldInfo(alias="senderId", default=None)
+ """Sender that last handled this thread.
+
+ Use it as the `Zavu-Sender` header when replying so the answer leaves from the
+ same number the contact knows.
+ """
+
+ whatsapp: Optional[ConversationWhatsapp] = None
+ """WhatsApp identity, present when the contact adopted a username."""
+
+
+class ConversationMarkAsReadResponse(BaseModel):
+ conversation: Conversation
+ """An inbox thread with one contact.
+
+ A conversation groups every message exchanged with that contact across channels,
+ so a contact who writes on WhatsApp and later by email stays in one thread.
+ """
diff --git a/src/zavudev/types/conversation_retrieve_response.py b/src/zavudev/types/conversation_retrieve_response.py
new file mode 100644
index 0000000..271dc1b
--- /dev/null
+++ b/src/zavudev/types/conversation_retrieve_response.py
@@ -0,0 +1,124 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .channel import Channel
+from .._models import BaseModel
+
+__all__ = [
+ "ConversationRetrieveResponse",
+ "Conversation",
+ "ConversationLastMessage",
+ "ConversationGroup",
+ "ConversationWhatsapp",
+]
+
+
+class ConversationLastMessage(BaseModel):
+ """
+ Denormalized preview of the most recent message, so a thread list needs no extra fetch.
+ """
+
+ id: str
+
+ at: datetime
+
+ channel: Channel
+ """Delivery channel. Use 'auto' for intelligent routing."""
+
+ direction: Literal["inbound", "outbound"]
+
+ text: str
+ """Text or caption. Empty when the last message carried no text (e.g. media)."""
+
+
+class ConversationGroup(BaseModel):
+ """Present when the thread is a group chat rather than a one-to-one conversation."""
+
+ id: str
+
+ participant_count: Optional[int] = FieldInfo(alias="participantCount", default=None)
+
+ subject: Optional[str] = None
+
+
+class ConversationWhatsapp(BaseModel):
+ """WhatsApp identity, present when the contact adopted a username."""
+
+ bsuid: Optional[str] = None
+ """Business-scoped user ID. Can be used as `to` when sending."""
+
+ username: Optional[str] = None
+
+
+class Conversation(BaseModel):
+ """An inbox thread with one contact.
+
+ A conversation groups every message exchanged with that contact across channels, so a contact who writes on WhatsApp and later by email stays in one thread.
+ """
+
+ id: str
+
+ channels: List[str]
+ """Every channel this thread has carried messages on."""
+
+ contact_identifier: str = FieldInfo(alias="contactIdentifier")
+ """
+ The key this thread is filed under: a phone number in E.164, a WhatsApp
+ business-scoped user ID (BSUID), a numeric chat ID
+ (Telegram/Instagram/Messenger), or a group JID. It is not always a phone number,
+ so do not parse it as one.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ last_message: ConversationLastMessage = FieldInfo(alias="lastMessage")
+ """
+ Denormalized preview of the most recent message, so a thread list needs no extra
+ fetch.
+ """
+
+ message_count: int = FieldInfo(alias="messageCount")
+
+ unread_count: int = FieldInfo(alias="unreadCount")
+ """Inbound messages not yet marked read.
+
+ Reset with POST /v1/conversations/{conversationId}/read.
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ contact_id: Optional[str] = FieldInfo(alias="contactId", default=None)
+ """ID of the contact this thread belongs to.
+
+ Absent on group threads and on threads whose contact has not been resolved yet.
+ """
+
+ email: Optional[str] = None
+ """Email address of the thread, when the contact was reached by email."""
+
+ group: Optional[ConversationGroup] = None
+ """Present when the thread is a group chat rather than a one-to-one conversation."""
+
+ sender_id: Optional[str] = FieldInfo(alias="senderId", default=None)
+ """Sender that last handled this thread.
+
+ Use it as the `Zavu-Sender` header when replying so the answer leaves from the
+ same number the contact knows.
+ """
+
+ whatsapp: Optional[ConversationWhatsapp] = None
+ """WhatsApp identity, present when the contact adopted a username."""
+
+
+class ConversationRetrieveResponse(BaseModel):
+ conversation: Conversation
+ """An inbox thread with one contact.
+
+ A conversation groups every message exchanged with that contact across channels,
+ so a contact who writes on WhatsApp and later by email stays in one thread.
+ """
diff --git a/src/zavudev/types/email_domain_create_params.py b/src/zavudev/types/email_domain_create_params.py
new file mode 100644
index 0000000..552a029
--- /dev/null
+++ b/src/zavudev/types/email_domain_create_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["EmailDomainCreateParams"]
+
+
+class EmailDomainCreateParams(TypedDict, total=False):
+ domain: Required[str]
+ """Bare domain, e.g. example.com."""
diff --git a/src/zavudev/types/email_domain_create_response.py b/src/zavudev/types/email_domain_create_response.py
new file mode 100644
index 0000000..cecfee6
--- /dev/null
+++ b/src/zavudev/types/email_domain_create_response.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["EmailDomainCreateResponse", "Domain", "DomainDNSRecord"]
+
+
+class DomainDNSRecord(BaseModel):
+ name: str
+ """Record host/name to create."""
+
+ purpose: Literal["dkim", "spf", "dmarc", "mail_from"]
+ """What the record is for."""
+
+ required: bool
+ """
+ Whether the record is required to verify + send (DKIM) or recommended for
+ deliverability.
+ """
+
+ type: str
+ """DNS record type."""
+
+ value: str
+ """Record value."""
+
+ priority: Optional[int] = None
+ """Priority (MX records only)."""
+
+
+class Domain(BaseModel):
+ id: str
+
+ dkim_status: str = FieldInfo(alias="dkimStatus")
+
+ domain: str
+
+ status: str
+ """Overall verification status."""
+
+ dns_records: Optional[List[DomainDNSRecord]] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records to publish.
+
+ Present when fetching a single domain or after adding one.
+ """
+
+
+class EmailDomainCreateResponse(BaseModel):
+ domain: Domain
diff --git a/src/zavudev/types/email_domain_list_response.py b/src/zavudev/types/email_domain_list_response.py
new file mode 100644
index 0000000..5d1437e
--- /dev/null
+++ b/src/zavudev/types/email_domain_list_response.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["EmailDomainListResponse", "Item", "ItemDNSRecord"]
+
+
+class ItemDNSRecord(BaseModel):
+ name: str
+ """Record host/name to create."""
+
+ purpose: Literal["dkim", "spf", "dmarc", "mail_from"]
+ """What the record is for."""
+
+ required: bool
+ """
+ Whether the record is required to verify + send (DKIM) or recommended for
+ deliverability.
+ """
+
+ type: str
+ """DNS record type."""
+
+ value: str
+ """Record value."""
+
+ priority: Optional[int] = None
+ """Priority (MX records only)."""
+
+
+class Item(BaseModel):
+ id: str
+
+ dkim_status: str = FieldInfo(alias="dkimStatus")
+
+ domain: str
+
+ status: str
+ """Overall verification status."""
+
+ dns_records: Optional[List[ItemDNSRecord]] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records to publish.
+
+ Present when fetching a single domain or after adding one.
+ """
+
+
+class EmailDomainListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/zavudev/types/email_domain_retrieve_response.py b/src/zavudev/types/email_domain_retrieve_response.py
new file mode 100644
index 0000000..72beba0
--- /dev/null
+++ b/src/zavudev/types/email_domain_retrieve_response.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["EmailDomainRetrieveResponse", "Domain", "DomainDNSRecord"]
+
+
+class DomainDNSRecord(BaseModel):
+ name: str
+ """Record host/name to create."""
+
+ purpose: Literal["dkim", "spf", "dmarc", "mail_from"]
+ """What the record is for."""
+
+ required: bool
+ """
+ Whether the record is required to verify + send (DKIM) or recommended for
+ deliverability.
+ """
+
+ type: str
+ """DNS record type."""
+
+ value: str
+ """Record value."""
+
+ priority: Optional[int] = None
+ """Priority (MX records only)."""
+
+
+class Domain(BaseModel):
+ id: str
+
+ dkim_status: str = FieldInfo(alias="dkimStatus")
+
+ domain: str
+
+ status: str
+ """Overall verification status."""
+
+ dns_records: Optional[List[DomainDNSRecord]] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records to publish.
+
+ Present when fetching a single domain or after adding one.
+ """
+
+
+class EmailDomainRetrieveResponse(BaseModel):
+ domain: Domain
diff --git a/src/zavudev/types/email_domain_verify_response.py b/src/zavudev/types/email_domain_verify_response.py
new file mode 100644
index 0000000..64a1e1c
--- /dev/null
+++ b/src/zavudev/types/email_domain_verify_response.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["EmailDomainVerifyResponse", "Domain", "DomainDNSRecord"]
+
+
+class DomainDNSRecord(BaseModel):
+ name: str
+ """Record host/name to create."""
+
+ purpose: Literal["dkim", "spf", "dmarc", "mail_from"]
+ """What the record is for."""
+
+ required: bool
+ """
+ Whether the record is required to verify + send (DKIM) or recommended for
+ deliverability.
+ """
+
+ type: str
+ """DNS record type."""
+
+ value: str
+ """Record value."""
+
+ priority: Optional[int] = None
+ """Priority (MX records only)."""
+
+
+class Domain(BaseModel):
+ id: str
+
+ dkim_status: str = FieldInfo(alias="dkimStatus")
+
+ domain: str
+
+ status: str
+ """Overall verification status."""
+
+ dns_records: Optional[List[DomainDNSRecord]] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records to publish.
+
+ Present when fetching a single domain or after adding one.
+ """
+
+
+class EmailDomainVerifyResponse(BaseModel):
+ domain: Domain
diff --git a/src/zavudev/types/function_list_deployments_params.py b/src/zavudev/types/function_list_deployments_params.py
new file mode 100644
index 0000000..a820d06
--- /dev/null
+++ b/src/zavudev/types/function_list_deployments_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["FunctionListDeploymentsParams"]
+
+
+class FunctionListDeploymentsParams(TypedDict, total=False):
+ limit: int
diff --git a/src/zavudev/types/function_list_deployments_response.py b/src/zavudev/types/function_list_deployments_response.py
new file mode 100644
index 0000000..41f9874
--- /dev/null
+++ b/src/zavudev/types/function_list_deployments_response.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["FunctionListDeploymentsResponse", "Deployment"]
+
+
+class Deployment(BaseModel):
+ id: Optional[str] = None
+
+ bundle_size_bytes: Optional[int] = FieldInfo(alias="bundleSizeBytes", default=None)
+
+ created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None)
+
+ deployed_at: Optional[datetime] = FieldInfo(alias="deployedAt", default=None)
+
+ error_message: Optional[str] = FieldInfo(alias="errorMessage", default=None)
+
+ is_active: Optional[bool] = FieldInfo(alias="isActive", default=None)
+
+ status: Optional[Literal["pending", "bundling", "uploading", "publishing", "active", "failed", "superseded"]] = None
+ """Stage of a function deployment."""
+
+ version: Optional[int] = None
+
+
+class FunctionListDeploymentsResponse(BaseModel):
+ deployments: List[Deployment]
diff --git a/src/zavudev/types/function_list_event_types_response.py b/src/zavudev/types/function_list_event_types_response.py
new file mode 100644
index 0000000..f5d6ba2
--- /dev/null
+++ b/src/zavudev/types/function_list_event_types_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .._models import BaseModel
+
+__all__ = ["FunctionListEventTypesResponse"]
+
+
+class FunctionListEventTypesResponse(BaseModel):
+ events: List[str]
diff --git a/src/zavudev/types/function_rollback_deployment_params.py b/src/zavudev/types/function_rollback_deployment_params.py
new file mode 100644
index 0000000..2d02ba0
--- /dev/null
+++ b/src/zavudev/types/function_rollback_deployment_params.py
@@ -0,0 +1,14 @@
+# 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
+
+__all__ = ["FunctionRollbackDeploymentParams"]
+
+
+class FunctionRollbackDeploymentParams(TypedDict, total=False):
+ deployment_id: Required[Annotated[str, PropertyInfo(alias="deploymentId")]]
+ """ID of the deployment to roll back to."""
diff --git a/src/zavudev/types/function_rollback_deployment_response.py b/src/zavudev/types/function_rollback_deployment_response.py
new file mode 100644
index 0000000..37b65eb
--- /dev/null
+++ b/src/zavudev/types/function_rollback_deployment_response.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["FunctionRollbackDeploymentResponse", "Deployment"]
+
+
+class Deployment(BaseModel):
+ id: str
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ function_id: str = FieldInfo(alias="functionId")
+
+ status: Literal["pending", "bundling", "uploading", "publishing", "active", "failed", "superseded"]
+ """Stage of a function deployment."""
+
+ version: int
+ """Monotonically increasing deployment version, starting at 1."""
+
+ build_logs: Optional[str] = FieldInfo(alias="buildLogs", default=None)
+ """
+ What the build printed: dependency installation, the bundler's output, and the
+ compiler's message when it failed. Returned when fetching a single deployment,
+ omitted from the list. Read this first when a deploy fails — `errorMessage` is
+ often the outer wrapper's summary, and the line that names the broken import or
+ the syntax error is here.
+ """
+
+ bundle_bytes: Optional[int] = FieldInfo(alias="bundleBytes", default=None)
+ """Size of the built bundle in bytes. Null until the build finishes."""
+
+ deployed_at: Optional[datetime] = FieldInfo(alias="deployedAt", default=None)
+
+ error_message: Optional[str] = FieldInfo(alias="errorMessage", default=None)
+ """Failure reason when status is 'failed'."""
+
+ source_code_bytes: Optional[int] = FieldInfo(alias="sourceCodeBytes", default=None)
+ """Total size of the deployed source tree in bytes."""
+
+
+class FunctionRollbackDeploymentResponse(BaseModel):
+ deployment: Deployment
+
+ previous_draft: Optional[object] = FieldInfo(alias="previousDraft", default=None)
+ """The draft that was replaced, so a UI can offer to restore it."""
+
+ rolled_back_to_version: Optional[int] = FieldInfo(alias="rolledBackToVersion", default=None)
diff --git a/src/zavudev/types/functions/__init__.py b/src/zavudev/types/functions/__init__.py
index e3ecbef..3c81028 100644
--- a/src/zavudev/types/functions/__init__.py
+++ b/src/zavudev/types/functions/__init__.py
@@ -3,4 +3,15 @@
from __future__ import annotations
from .secret_set_params import SecretSetParams as SecretSetParams
+from .git_link_link_params import GitLinkLinkParams as GitLinkLinkParams
from .secret_list_response import SecretListResponse as SecretListResponse
+from .trigger_create_params import TriggerCreateParams as TriggerCreateParams
+from .trigger_list_response import TriggerListResponse as TriggerListResponse
+from .trigger_update_params import TriggerUpdateParams as TriggerUpdateParams
+from .git_link_link_response import GitLinkLinkResponse as GitLinkLinkResponse
+from .git_link_update_params import GitLinkUpdateParams as GitLinkUpdateParams
+from .trigger_create_response import TriggerCreateResponse as TriggerCreateResponse
+from .trigger_update_response import TriggerUpdateResponse as TriggerUpdateResponse
+from .git_link_update_response import GitLinkUpdateResponse as GitLinkUpdateResponse
+from .git_link_retrieve_response import GitLinkRetrieveResponse as GitLinkRetrieveResponse
+from .git_link_deploy_now_response import GitLinkDeployNowResponse as GitLinkDeployNowResponse
diff --git a/src/zavudev/types/functions/git_link_deploy_now_response.py b/src/zavudev/types/functions/git_link_deploy_now_response.py
new file mode 100644
index 0000000..51fde23
--- /dev/null
+++ b/src/zavudev/types/functions/git_link_deploy_now_response.py
@@ -0,0 +1,9 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["GitLinkDeployNowResponse"]
+
+
+class GitLinkDeployNowResponse(BaseModel):
+ scheduled: bool
diff --git a/src/zavudev/types/functions/git_link_link_params.py b/src/zavudev/types/functions/git_link_link_params.py
new file mode 100644
index 0000000..dc4eee1
--- /dev/null
+++ b/src/zavudev/types/functions/git_link_link_params.py
@@ -0,0 +1,22 @@
+# 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
+
+__all__ = ["GitLinkLinkParams"]
+
+
+class GitLinkLinkParams(TypedDict, total=False):
+ owner: Required[str]
+
+ repo: Required[str]
+
+ auto_deploy: Annotated[bool, PropertyInfo(alias="autoDeploy")]
+
+ branch: str
+
+ root_dir: Annotated[str, PropertyInfo(alias="rootDir")]
+ """Subdirectory holding the project, for monorepos."""
diff --git a/src/zavudev/types/functions/git_link_link_response.py b/src/zavudev/types/functions/git_link_link_response.py
new file mode 100644
index 0000000..352cb2e
--- /dev/null
+++ b/src/zavudev/types/functions/git_link_link_response.py
@@ -0,0 +1,87 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["GitLinkLinkResponse", "Link"]
+
+
+class Link(BaseModel):
+ """A GitHub repository bound to a function.
+
+ A push to `branch` deploys the function. A function holds at most one link.
+ """
+
+ id: str
+
+ auto_deploy: bool = FieldInfo(alias="autoDeploy")
+ """When false the link is kept and pushes are ignored."""
+
+ branch: str
+ """Only pushes to this branch deploy."""
+
+ connection: Literal["app", "manual"]
+ """How this link authenticates, decided by the server rather than by the caller.
+
+ - `app`: the Zavu GitHub App is installed on the account. Pushes arrive on the
+ app's webhook and private repositories work. Nothing to configure in the
+ repository.
+ - `manual`: no installation. The link carries its own secret and you add the
+ webhook to the repository yourself.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ function_id: str = FieldInfo(alias="functionId")
+
+ owner: str
+
+ provider: Literal["github"]
+
+ repo: str
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ last_commit_message: Optional[str] = FieldInfo(alias="lastCommitMessage", default=None)
+
+ last_commit_sha: Optional[str] = FieldInfo(alias="lastCommitSha", default=None)
+
+ last_deploy_at: Optional[datetime] = FieldInfo(alias="lastDeployAt", default=None)
+
+ last_error: Optional[str] = FieldInfo(alias="lastError", default=None)
+ """Why the last deploy failed. Null otherwise."""
+
+ last_status: Optional[Literal["deploying", "deployed", "failed"]] = FieldInfo(alias="lastStatus", default=None)
+
+ root_dir: Optional[str] = FieldInfo(alias="rootDir", default=None)
+ """Subdirectory holding the project, for monorepos.
+
+ Null when the project is at the repository root.
+ """
+
+
+class GitLinkLinkResponse(BaseModel):
+ link: Link
+ """A GitHub repository bound to a function.
+
+ A push to `branch` deploys the function. A function holds at most one link.
+ """
+
+ webhook_url: str = FieldInfo(alias="webhookUrl")
+ """Endpoint that receives GitHub's push deliveries.
+
+ Only needed on a `manual` link, where you add it to the repository yourself.
+ """
+
+ webhook_secret: Optional[str] = FieldInfo(alias="webhookSecret", default=None)
+ """Shared secret for the repository's webhook.
+
+ **Returned only when creating a `manual` link, and only there** — every later
+ read strips it, and re-linking mints a new one. Absent entirely on an `app`
+ link, which needs no secret of its own.
+ """
diff --git a/src/zavudev/types/functions/git_link_retrieve_response.py b/src/zavudev/types/functions/git_link_retrieve_response.py
new file mode 100644
index 0000000..75e3349
--- /dev/null
+++ b/src/zavudev/types/functions/git_link_retrieve_response.py
@@ -0,0 +1,87 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["GitLinkRetrieveResponse", "Link"]
+
+
+class Link(BaseModel):
+ """A GitHub repository bound to a function.
+
+ A push to `branch` deploys the function. A function holds at most one link.
+ """
+
+ id: str
+
+ auto_deploy: bool = FieldInfo(alias="autoDeploy")
+ """When false the link is kept and pushes are ignored."""
+
+ branch: str
+ """Only pushes to this branch deploy."""
+
+ connection: Literal["app", "manual"]
+ """How this link authenticates, decided by the server rather than by the caller.
+
+ - `app`: the Zavu GitHub App is installed on the account. Pushes arrive on the
+ app's webhook and private repositories work. Nothing to configure in the
+ repository.
+ - `manual`: no installation. The link carries its own secret and you add the
+ webhook to the repository yourself.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ function_id: str = FieldInfo(alias="functionId")
+
+ owner: str
+
+ provider: Literal["github"]
+
+ repo: str
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ last_commit_message: Optional[str] = FieldInfo(alias="lastCommitMessage", default=None)
+
+ last_commit_sha: Optional[str] = FieldInfo(alias="lastCommitSha", default=None)
+
+ last_deploy_at: Optional[datetime] = FieldInfo(alias="lastDeployAt", default=None)
+
+ last_error: Optional[str] = FieldInfo(alias="lastError", default=None)
+ """Why the last deploy failed. Null otherwise."""
+
+ last_status: Optional[Literal["deploying", "deployed", "failed"]] = FieldInfo(alias="lastStatus", default=None)
+
+ root_dir: Optional[str] = FieldInfo(alias="rootDir", default=None)
+ """Subdirectory holding the project, for monorepos.
+
+ Null when the project is at the repository root.
+ """
+
+
+class GitLinkRetrieveResponse(BaseModel):
+ link: Link
+ """A GitHub repository bound to a function.
+
+ A push to `branch` deploys the function. A function holds at most one link.
+ """
+
+ webhook_url: str = FieldInfo(alias="webhookUrl")
+ """Endpoint that receives GitHub's push deliveries.
+
+ Only needed on a `manual` link, where you add it to the repository yourself.
+ """
+
+ webhook_secret: Optional[str] = FieldInfo(alias="webhookSecret", default=None)
+ """Shared secret for the repository's webhook.
+
+ **Returned only when creating a `manual` link, and only there** — every later
+ read strips it, and re-linking mints a new one. Absent entirely on an `app`
+ link, which needs no secret of its own.
+ """
diff --git a/src/zavudev/types/functions/git_link_update_params.py b/src/zavudev/types/functions/git_link_update_params.py
new file mode 100644
index 0000000..15a2de8
--- /dev/null
+++ b/src/zavudev/types/functions/git_link_update_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 import Optional
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["GitLinkUpdateParams"]
+
+
+class GitLinkUpdateParams(TypedDict, total=False):
+ auto_deploy: Annotated[bool, PropertyInfo(alias="autoDeploy")]
+
+ branch: str
+
+ root_dir: Annotated[Optional[str], PropertyInfo(alias="rootDir")]
diff --git a/src/zavudev/types/functions/git_link_update_response.py b/src/zavudev/types/functions/git_link_update_response.py
new file mode 100644
index 0000000..aab6b69
--- /dev/null
+++ b/src/zavudev/types/functions/git_link_update_response.py
@@ -0,0 +1,87 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["GitLinkUpdateResponse", "Link"]
+
+
+class Link(BaseModel):
+ """A GitHub repository bound to a function.
+
+ A push to `branch` deploys the function. A function holds at most one link.
+ """
+
+ id: str
+
+ auto_deploy: bool = FieldInfo(alias="autoDeploy")
+ """When false the link is kept and pushes are ignored."""
+
+ branch: str
+ """Only pushes to this branch deploy."""
+
+ connection: Literal["app", "manual"]
+ """How this link authenticates, decided by the server rather than by the caller.
+
+ - `app`: the Zavu GitHub App is installed on the account. Pushes arrive on the
+ app's webhook and private repositories work. Nothing to configure in the
+ repository.
+ - `manual`: no installation. The link carries its own secret and you add the
+ webhook to the repository yourself.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ function_id: str = FieldInfo(alias="functionId")
+
+ owner: str
+
+ provider: Literal["github"]
+
+ repo: str
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ last_commit_message: Optional[str] = FieldInfo(alias="lastCommitMessage", default=None)
+
+ last_commit_sha: Optional[str] = FieldInfo(alias="lastCommitSha", default=None)
+
+ last_deploy_at: Optional[datetime] = FieldInfo(alias="lastDeployAt", default=None)
+
+ last_error: Optional[str] = FieldInfo(alias="lastError", default=None)
+ """Why the last deploy failed. Null otherwise."""
+
+ last_status: Optional[Literal["deploying", "deployed", "failed"]] = FieldInfo(alias="lastStatus", default=None)
+
+ root_dir: Optional[str] = FieldInfo(alias="rootDir", default=None)
+ """Subdirectory holding the project, for monorepos.
+
+ Null when the project is at the repository root.
+ """
+
+
+class GitLinkUpdateResponse(BaseModel):
+ link: Link
+ """A GitHub repository bound to a function.
+
+ A push to `branch` deploys the function. A function holds at most one link.
+ """
+
+ webhook_url: str = FieldInfo(alias="webhookUrl")
+ """Endpoint that receives GitHub's push deliveries.
+
+ Only needed on a `manual` link, where you add it to the repository yourself.
+ """
+
+ webhook_secret: Optional[str] = FieldInfo(alias="webhookSecret", default=None)
+ """Shared secret for the repository's webhook.
+
+ **Returned only when creating a `manual` link, and only there** — every later
+ read strips it, and re-linking mints a new one. Absent entirely on an `app`
+ link, which needs no secret of its own.
+ """
diff --git a/src/zavudev/types/functions/trigger_create_params.py b/src/zavudev/types/functions/trigger_create_params.py
new file mode 100644
index 0000000..50a5197
--- /dev/null
+++ b/src/zavudev/types/functions/trigger_create_params.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
+
+__all__ = ["TriggerCreateParams"]
+
+
+class TriggerCreateParams(TypedDict, total=False):
+ event_types: Required[Annotated[SequenceNotStr[str], PropertyInfo(alias="eventTypes")]]
+ """Event types to subscribe to."""
+
+ sender_ids: Required[Annotated[SequenceNotStr[Optional[str]], PropertyInfo(alias="senderIds")]]
+ """Senders to scope the triggers to. Use null for all senders."""
+
+ cron: str
+ """
+ Required when eventTypes includes `cron`: a 5-field cron expression (minute hour
+ day-of-month month day-of-week), evaluated in UTC.
+ """
diff --git a/src/zavudev/types/functions/trigger_create_response.py b/src/zavudev/types/functions/trigger_create_response.py
new file mode 100644
index 0000000..ed48cc7
--- /dev/null
+++ b/src/zavudev/types/functions/trigger_create_response.py
@@ -0,0 +1,56 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["TriggerCreateResponse", "Trigger"]
+
+
+class Trigger(BaseModel):
+ """A subscription that runs a Zavu Function when a messaging event fires."""
+
+ id: str
+
+ active: bool
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ event_type: str = FieldInfo(alias="eventType")
+ """Event type that fires the function.
+
+ See GET /v1/functions/event-types for the supported list. The special type
+ `cron` fires on a schedule instead of a messaging event and carries a `cron`
+ expression.
+ """
+
+ function_id: str = FieldInfo(alias="functionId")
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ cron: Optional[str] = None
+ """
+ 5-field cron expression (minute hour day-of-month month day-of-week), evaluated
+ in UTC. Present only on `cron` triggers.
+ """
+
+ last_run_at: Optional[datetime] = FieldInfo(alias="lastRunAt", default=None)
+ """Last time the schedule fired. Null until the first fire."""
+
+ next_run_at: Optional[datetime] = FieldInfo(alias="nextRunAt", default=None)
+ """Next scheduled fire time. Present only on `cron` triggers."""
+
+ sender_id: Optional[str] = FieldInfo(alias="senderId", default=None)
+ """Restrict the trigger to a single sender. Null means all senders in the project."""
+
+
+class TriggerCreateResponse(BaseModel):
+ added: int
+
+ skipped: int
+ """Number of triggers that already existed."""
+
+ triggers: List[Trigger]
diff --git a/src/zavudev/types/functions/trigger_list_response.py b/src/zavudev/types/functions/trigger_list_response.py
new file mode 100644
index 0000000..3b82452
--- /dev/null
+++ b/src/zavudev/types/functions/trigger_list_response.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["TriggerListResponse", "Trigger"]
+
+
+class Trigger(BaseModel):
+ """A subscription that runs a Zavu Function when a messaging event fires."""
+
+ id: str
+
+ active: bool
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ event_type: str = FieldInfo(alias="eventType")
+ """Event type that fires the function.
+
+ See GET /v1/functions/event-types for the supported list. The special type
+ `cron` fires on a schedule instead of a messaging event and carries a `cron`
+ expression.
+ """
+
+ function_id: str = FieldInfo(alias="functionId")
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ cron: Optional[str] = None
+ """
+ 5-field cron expression (minute hour day-of-month month day-of-week), evaluated
+ in UTC. Present only on `cron` triggers.
+ """
+
+ last_run_at: Optional[datetime] = FieldInfo(alias="lastRunAt", default=None)
+ """Last time the schedule fired. Null until the first fire."""
+
+ next_run_at: Optional[datetime] = FieldInfo(alias="nextRunAt", default=None)
+ """Next scheduled fire time. Present only on `cron` triggers."""
+
+ sender_id: Optional[str] = FieldInfo(alias="senderId", default=None)
+ """Restrict the trigger to a single sender. Null means all senders in the project."""
+
+
+class TriggerListResponse(BaseModel):
+ triggers: List[Trigger]
diff --git a/src/zavudev/types/functions/trigger_update_params.py b/src/zavudev/types/functions/trigger_update_params.py
new file mode 100644
index 0000000..b424102
--- /dev/null
+++ b/src/zavudev/types/functions/trigger_update_params.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["TriggerUpdateParams"]
+
+
+class TriggerUpdateParams(TypedDict, total=False):
+ active: Required[bool]
diff --git a/src/zavudev/types/functions/trigger_update_response.py b/src/zavudev/types/functions/trigger_update_response.py
new file mode 100644
index 0000000..f3e1609
--- /dev/null
+++ b/src/zavudev/types/functions/trigger_update_response.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["TriggerUpdateResponse"]
+
+
+class TriggerUpdateResponse(BaseModel):
+ active: bool
+
+ ok: bool
diff --git a/src/zavudev/types/introspect_validate_email_params.py b/src/zavudev/types/introspect_validate_email_params.py
new file mode 100644
index 0000000..4a96f5a
--- /dev/null
+++ b/src/zavudev/types/introspect_validate_email_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+from .._types import SequenceNotStr
+
+__all__ = ["IntrospectValidateEmailParams"]
+
+
+class IntrospectValidateEmailParams(TypedDict, total=False):
+ email: str
+ """Single email address to validate."""
+
+ emails: SequenceNotStr[str]
+ """Batch of email addresses to validate (max 100)."""
diff --git a/src/zavudev/types/introspect_validate_email_response.py b/src/zavudev/types/introspect_validate_email_response.py
new file mode 100644
index 0000000..d120245
--- /dev/null
+++ b/src/zavudev/types/introspect_validate_email_response.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["IntrospectValidateEmailResponse", "Result", "Summary"]
+
+
+class Result(BaseModel):
+ domain: Optional[str] = None
+ """Domain part of the address. Null when the syntax is invalid."""
+
+ email: str
+ """The address exactly as submitted."""
+
+ normalized: Optional[str] = None
+ """Lowercased, trimmed form of the address. Null when the syntax is invalid."""
+
+ reasons: List[
+ Literal[
+ "invalid_syntax",
+ "domain_not_found",
+ "domain_no_mx",
+ "disposable_domain",
+ "role_address",
+ "suppressed_hard_bounce",
+ "suppressed_soft_bounce",
+ "suppressed_complaint",
+ "suppressed_manual",
+ "suppressed_unsubscribe",
+ ]
+ ]
+ """Signals behind the verdict. Empty for a clean `deliverable` address."""
+
+ verdict: Literal["deliverable", "risky", "undeliverable"]
+ """Validation verdict.
+
+ - `deliverable`: nothing suggests the address will bounce.
+ - `risky`: sendable, but a signal predicts elevated bounce/complaint odds (role
+ address, disposable domain, MX-less domain, prior soft bounce).
+ - `undeliverable`: will bounce or is blocked (invalid syntax, dead domain, or
+ the address is on your suppression list after a hard bounce/complaint).
+ """
+
+
+class Summary(BaseModel):
+ deliverable: int
+
+ risky: int
+
+ total: int
+
+ undeliverable: int
+
+
+class IntrospectValidateEmailResponse(BaseModel):
+ results: List[Result]
+ """One result per submitted address, in the same order."""
+
+ summary: Summary
diff --git a/src/zavudev/types/message.py b/src/zavudev/types/message.py
index c7be492..867948e 100644
--- a/src/zavudev/types/message.py
+++ b/src/zavudev/types/message.py
@@ -2,6 +2,7 @@
from typing import Dict, Optional
from datetime import datetime
+from typing_extensions import Literal
from pydantic import Field as FieldInfo
@@ -22,6 +23,13 @@ class Message(BaseModel):
created_at: datetime = FieldInfo(alias="createdAt")
+ direction: Literal["inbound", "outbound"]
+ """Who sent the message.
+
+ Needed to render a thread: `status` cannot tell the two apart, because an
+ inbound message is also stored as `delivered`.
+ """
+
message_type: MessageType = FieldInfo(alias="messageType")
"""Type of message.
diff --git a/src/zavudev/types/message_content.py b/src/zavudev/types/message_content.py
index e64f440..a91e24d 100644
--- a/src/zavudev/types/message_content.py
+++ b/src/zavudev/types/message_content.py
@@ -7,7 +7,7 @@
from .._models import BaseModel
-__all__ = ["MessageContent", "Button", "Contact", "Section", "SectionRow"]
+__all__ = ["MessageContent", "Button", "Contact", "Referral", "Section", "SectionRow"]
class Button(BaseModel):
@@ -22,6 +22,51 @@ class Contact(BaseModel):
phones: Optional[List[str]] = None
+class Referral(BaseModel):
+ """
+ Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came from.
+
+ WhatsApp only. Present on the **first inbound message** of a conversation opened from a Meta ad or post, and on no message after it — so store it when it arrives rather than expecting it again. Organic conversations never carry it.
+
+ Field names are camelCased to match the rest of this API; Meta sends them as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are omitted: a `post` source has no click id, and an image ad has no `videoUrl`.
+ """
+
+ body: Optional[str] = None
+ """Body copy of the ad or post."""
+
+ ctwa_clid: Optional[str] = FieldInfo(alias="ctwaClid", default=None)
+ """Click-to-WhatsApp click identifier.
+
+ This is the value Meta's Conversions API needs to credit a conversion back to
+ the ad that produced the conversation. Present on `ad` sources; a `post` source
+ has none.
+ """
+
+ headline: Optional[str] = None
+ """Headline of the ad or post."""
+
+ image_url: Optional[str] = FieldInfo(alias="imageUrl", default=None)
+ """Image of the ad. Present when `mediaType` is `image`."""
+
+ media_type: Optional[Literal["image", "video"]] = FieldInfo(alias="mediaType", default=None)
+ """Type of media on the ad, when it had any."""
+
+ source_id: Optional[str] = FieldInfo(alias="sourceId", default=None)
+ """Identifier of the ad or post that produced the click."""
+
+ source_type: Optional[Literal["ad", "post"]] = FieldInfo(alias="sourceType", default=None)
+ """Where the click came from."""
+
+ source_url: Optional[str] = FieldInfo(alias="sourceUrl", default=None)
+ """Meta permalink to the ad or post."""
+
+ thumbnail_url: Optional[str] = FieldInfo(alias="thumbnailUrl", default=None)
+ """Thumbnail of the ad media."""
+
+ video_url: Optional[str] = FieldInfo(alias="videoUrl", default=None)
+ """Video of the ad. Present when `mediaType` is `video`."""
+
+
class SectionRow(BaseModel):
id: str
@@ -106,6 +151,20 @@ class MessageContent(BaseModel):
react_to_message_id: Optional[str] = FieldInfo(alias="reactToMessageId", default=None)
"""Message ID to react to."""
+ referral: Optional[Referral] = None
+ """
+ Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came
+ from.
+
+ WhatsApp only. Present on the **first inbound message** of a conversation opened
+ from a Meta ad or post, and on no message after it — so store it when it arrives
+ rather than expecting it again. Organic conversations never carry it.
+
+ Field names are camelCased to match the rest of this API; Meta sends them as
+ snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are
+ omitted: a `post` source has no click id, and an image ad has no `videoUrl`.
+ """
+
reply_to_from: Optional[str] = FieldInfo(alias="replyToFrom", default=None)
"""Sender of the quoted message (phone number in E.164 format)."""
diff --git a/src/zavudev/types/message_content_param.py b/src/zavudev/types/message_content_param.py
index 797d711..4287c08 100644
--- a/src/zavudev/types/message_content_param.py
+++ b/src/zavudev/types/message_content_param.py
@@ -8,7 +8,7 @@
from .._types import SequenceNotStr
from .._utils import PropertyInfo
-__all__ = ["MessageContentParam", "Button", "Contact", "Section", "SectionRow"]
+__all__ = ["MessageContentParam", "Button", "Contact", "Referral", "Section", "SectionRow"]
class Button(TypedDict, total=False):
@@ -23,6 +23,51 @@ class Contact(TypedDict, total=False):
phones: SequenceNotStr[str]
+class Referral(TypedDict, total=False):
+ """
+ Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came from.
+
+ WhatsApp only. Present on the **first inbound message** of a conversation opened from a Meta ad or post, and on no message after it — so store it when it arrives rather than expecting it again. Organic conversations never carry it.
+
+ Field names are camelCased to match the rest of this API; Meta sends them as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are omitted: a `post` source has no click id, and an image ad has no `videoUrl`.
+ """
+
+ body: str
+ """Body copy of the ad or post."""
+
+ ctwa_clid: Annotated[str, PropertyInfo(alias="ctwaClid")]
+ """Click-to-WhatsApp click identifier.
+
+ This is the value Meta's Conversions API needs to credit a conversion back to
+ the ad that produced the conversation. Present on `ad` sources; a `post` source
+ has none.
+ """
+
+ headline: str
+ """Headline of the ad or post."""
+
+ image_url: Annotated[str, PropertyInfo(alias="imageUrl")]
+ """Image of the ad. Present when `mediaType` is `image`."""
+
+ media_type: Annotated[Literal["image", "video"], PropertyInfo(alias="mediaType")]
+ """Type of media on the ad, when it had any."""
+
+ source_id: Annotated[str, PropertyInfo(alias="sourceId")]
+ """Identifier of the ad or post that produced the click."""
+
+ source_type: Annotated[Literal["ad", "post"], PropertyInfo(alias="sourceType")]
+ """Where the click came from."""
+
+ source_url: Annotated[str, PropertyInfo(alias="sourceUrl")]
+ """Meta permalink to the ad or post."""
+
+ thumbnail_url: Annotated[str, PropertyInfo(alias="thumbnailUrl")]
+ """Thumbnail of the ad media."""
+
+ video_url: Annotated[str, PropertyInfo(alias="videoUrl")]
+ """Video of the ad. Present when `mediaType` is `video`."""
+
+
class SectionRow(TypedDict, total=False):
id: Required[str]
@@ -105,6 +150,20 @@ class MessageContentParam(TypedDict, total=False):
react_to_message_id: Annotated[str, PropertyInfo(alias="reactToMessageId")]
"""Message ID to react to."""
+ referral: Referral
+ """
+ Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation came
+ from.
+
+ WhatsApp only. Present on the **first inbound message** of a conversation opened
+ from a Meta ad or post, and on no message after it — so store it when it arrives
+ rather than expecting it again. Organic conversations never carry it.
+
+ Field names are camelCased to match the rest of this API; Meta sends them as
+ snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not apply are
+ omitted: a `post` source has no click id, and an image ad has no `videoUrl`.
+ """
+
reply_to_from: Annotated[str, PropertyInfo(alias="replyToFrom")]
"""Sender of the quoted message (phone number in E.164 format)."""
diff --git a/src/zavudev/types/message_list_attachments_response.py b/src/zavudev/types/message_list_attachments_response.py
new file mode 100644
index 0000000..81d7a3f
--- /dev/null
+++ b/src/zavudev/types/message_list_attachments_response.py
@@ -0,0 +1,49 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["MessageListAttachmentsResponse", "Item"]
+
+
+class Item(BaseModel):
+ """A stored file attachment for an email message (inbound or outbound)."""
+
+ id: str
+
+ content_id: Optional[str] = FieldInfo(alias="contentId", default=None)
+ """
+ Content-ID for inline attachments (referenced in the HTML body as
+ `cid:`). Null for regular attachments.
+ """
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ download_url: Optional[str] = FieldInfo(alias="downloadUrl", default=None)
+ """Short-lived signed URL to download the attachment bytes.
+
+ Freshly generated on each request and expires; do not cache it. Null if the
+ stored file is no longer available.
+ """
+
+ filename: str
+
+ is_inline: bool = FieldInfo(alias="isInline")
+ """
+ Whether the attachment is inline (embedded in the HTML body) rather than a
+ regular attachment.
+ """
+
+ mime_type: str = FieldInfo(alias="mimeType")
+ """MIME type of the attachment."""
+
+ size: int
+ """Size of the attachment in bytes."""
+
+
+class MessageListAttachmentsResponse(BaseModel):
+ items: List[Item]
diff --git a/src/zavudev/types/senders/__init__.py b/src/zavudev/types/senders/__init__.py
index e53045e..45c47a4 100644
--- a/src/zavudev/types/senders/__init__.py
+++ b/src/zavudev/types/senders/__init__.py
@@ -12,7 +12,9 @@
from .whats_app_sync_status import WhatsAppSyncStatus as WhatsAppSyncStatus
from .agent_execution_status import AgentExecutionStatus as AgentExecutionStatus
from .whats_app_sync_history import WhatsAppSyncHistory as WhatsAppSyncHistory
+from .telegram_connect_params import TelegramConnectParams as TelegramConnectParams
from .whats_app_sync_contacts import WhatsAppSyncContacts as WhatsAppSyncContacts
+from .telegram_connect_response import TelegramConnectResponse as TelegramConnectResponse
from .whatsapp_sync_retrieve_response import WhatsappSyncRetrieveResponse as WhatsappSyncRetrieveResponse
from .whatsapp_sync_start_history_sync_response import (
WhatsappSyncStartHistorySyncResponse as WhatsappSyncStartHistorySyncResponse,
diff --git a/src/zavudev/types/senders/agent/__init__.py b/src/zavudev/types/senders/agent/__init__.py
index 2990008..056d3e6 100644
--- a/src/zavudev/types/senders/agent/__init__.py
+++ b/src/zavudev/types/senders/agent/__init__.py
@@ -31,9 +31,11 @@
from .tool_retrieve_response import ToolRetrieveResponse as ToolRetrieveResponse
from .flow_duplicate_response import FlowDuplicateResponse as FlowDuplicateResponse
from .knowledge_base_list_params import KnowledgeBaseListParams as KnowledgeBaseListParams
+from .tool_list_test_runs_params import ToolListTestRunsParams as ToolListTestRunsParams
from .execution_retrieve_response import ExecutionRetrieveResponse as ExecutionRetrieveResponse
from .knowledge_base_create_params import KnowledgeBaseCreateParams as KnowledgeBaseCreateParams
from .knowledge_base_update_params import KnowledgeBaseUpdateParams as KnowledgeBaseUpdateParams
+from .tool_list_test_runs_response import ToolListTestRunsResponse as ToolListTestRunsResponse
from .knowledge_base_create_response import KnowledgeBaseCreateResponse as KnowledgeBaseCreateResponse
from .knowledge_base_update_response import KnowledgeBaseUpdateResponse as KnowledgeBaseUpdateResponse
from .knowledge_base_retrieve_response import KnowledgeBaseRetrieveResponse as KnowledgeBaseRetrieveResponse
diff --git a/src/zavudev/types/senders/agent/knowledge_bases/__init__.py b/src/zavudev/types/senders/agent/knowledge_bases/__init__.py
index c2f0655..e4d0620 100644
--- a/src/zavudev/types/senders/agent/knowledge_bases/__init__.py
+++ b/src/zavudev/types/senders/agent/knowledge_bases/__init__.py
@@ -5,3 +5,6 @@
from .document_list_params import DocumentListParams as DocumentListParams
from .document_create_params import DocumentCreateParams as DocumentCreateParams
from .document_create_response import DocumentCreateResponse as DocumentCreateResponse
+from .document_update_document_params import DocumentUpdateDocumentParams as DocumentUpdateDocumentParams
+from .document_update_document_response import DocumentUpdateDocumentResponse as DocumentUpdateDocumentResponse
+from .document_retrieve_document_response import DocumentRetrieveDocumentResponse as DocumentRetrieveDocumentResponse
diff --git a/src/zavudev/types/senders/agent/knowledge_bases/document_retrieve_document_response.py b/src/zavudev/types/senders/agent/knowledge_bases/document_retrieve_document_response.py
new file mode 100644
index 0000000..607c7a4
--- /dev/null
+++ b/src/zavudev/types/senders/agent/knowledge_bases/document_retrieve_document_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ....._models import BaseModel
+from ..agent_document import AgentDocument
+
+__all__ = ["DocumentRetrieveDocumentResponse"]
+
+
+class DocumentRetrieveDocumentResponse(BaseModel):
+ document: AgentDocument
diff --git a/src/zavudev/types/senders/agent/knowledge_bases/document_update_document_params.py b/src/zavudev/types/senders/agent/knowledge_bases/document_update_document_params.py
new file mode 100644
index 0000000..b8d949b
--- /dev/null
+++ b/src/zavudev/types/senders/agent/knowledge_bases/document_update_document_params.py
@@ -0,0 +1,19 @@
+# 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
+
+__all__ = ["DocumentUpdateDocumentParams"]
+
+
+class DocumentUpdateDocumentParams(TypedDict, total=False):
+ sender_id: Required[Annotated[str, PropertyInfo(alias="senderId")]]
+
+ kb_id: Required[Annotated[str, PropertyInfo(alias="kbId")]]
+
+ content: str
+
+ title: str
diff --git a/src/zavudev/types/senders/agent/knowledge_bases/document_update_document_response.py b/src/zavudev/types/senders/agent/knowledge_bases/document_update_document_response.py
new file mode 100644
index 0000000..bd095bb
--- /dev/null
+++ b/src/zavudev/types/senders/agent/knowledge_bases/document_update_document_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ....._models import BaseModel
+from ..agent_document import AgentDocument
+
+__all__ = ["DocumentUpdateDocumentResponse"]
+
+
+class DocumentUpdateDocumentResponse(BaseModel):
+ document: AgentDocument
diff --git a/src/zavudev/types/senders/agent/tool_list_test_runs_params.py b/src/zavudev/types/senders/agent/tool_list_test_runs_params.py
new file mode 100644
index 0000000..f60ecc6
--- /dev/null
+++ b/src/zavudev/types/senders/agent/tool_list_test_runs_params.py
@@ -0,0 +1,15 @@
+# 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
+
+__all__ = ["ToolListTestRunsParams"]
+
+
+class ToolListTestRunsParams(TypedDict, total=False):
+ sender_id: Required[Annotated[str, PropertyInfo(alias="senderId")]]
+
+ limit: int
diff --git a/src/zavudev/types/senders/agent/tool_list_test_runs_response.py b/src/zavudev/types/senders/agent/tool_list_test_runs_response.py
new file mode 100644
index 0000000..00a1cc8
--- /dev/null
+++ b/src/zavudev/types/senders/agent/tool_list_test_runs_response.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = ["ToolListTestRunsResponse", "Item"]
+
+
+class Item(BaseModel):
+ """One run of a tool triggered from the test endpoint.
+
+ Recorded so a test is verifiable after the fact rather than only visible in the response.
+ """
+
+ id: str
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ duration_ms: int = FieldInfo(alias="durationMs")
+
+ success: bool
+ """Whether the tool returned without error.
+
+ A tool that answered with a non-2xx status is a failed run, not an error of this
+ endpoint.
+ """
+
+ tool_id: str = FieldInfo(alias="toolId")
+
+ error: Optional[str] = None
+ """Why the run failed, when it did."""
+
+ params: Optional[Dict[str, object]] = None
+ """The parameters the tool was called with."""
+
+ response: Optional[str] = None
+ """The tool's response body, truncated."""
+
+ status_code: Optional[int] = FieldInfo(alias="statusCode", default=None)
+ """HTTP status the tool's webhook returned.
+
+ Absent for tools that do not go over HTTP.
+ """
+
+
+class ToolListTestRunsResponse(BaseModel):
+ items: List[Item]
diff --git a/src/zavudev/types/senders/agent/tools/__init__.py b/src/zavudev/types/senders/agent/tools/__init__.py
new file mode 100644
index 0000000..f8ee8b1
--- /dev/null
+++ b/src/zavudev/types/senders/agent/tools/__init__.py
@@ -0,0 +1,3 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
diff --git a/src/zavudev/types/senders/telegram_connect_params.py b/src/zavudev/types/senders/telegram_connect_params.py
new file mode 100644
index 0000000..c527645
--- /dev/null
+++ b/src/zavudev/types/senders/telegram_connect_params.py
@@ -0,0 +1,14 @@
+# 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
+
+__all__ = ["TelegramConnectParams"]
+
+
+class TelegramConnectParams(TypedDict, total=False):
+ bot_token: Required[Annotated[str, PropertyInfo(alias="botToken")]]
+ """Bot token from @BotFather."""
diff --git a/src/zavudev/types/senders/telegram_connect_response.py b/src/zavudev/types/senders/telegram_connect_response.py
new file mode 100644
index 0000000..30cc83a
--- /dev/null
+++ b/src/zavudev/types/senders/telegram_connect_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["TelegramConnectResponse", "Telegram"]
+
+
+class Telegram(BaseModel):
+ connected: bool
+
+ bot_id: Optional[str] = FieldInfo(alias="botId", default=None)
+
+ bot_username: Optional[str] = FieldInfo(alias="botUsername", default=None)
+
+
+class TelegramConnectResponse(BaseModel):
+ telegram: Telegram
diff --git a/src/zavudev/types/template_sync_params.py b/src/zavudev/types/template_sync_params.py
new file mode 100644
index 0000000..99f6550
--- /dev/null
+++ b/src/zavudev/types/template_sync_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["TemplateSyncParams"]
+
+
+class TemplateSyncParams(TypedDict, total=False):
+ sender_id: Annotated[str, PropertyInfo(alias="senderId")]
+ """Sync only the WhatsApp Business Account attached to this sender.
+
+ If omitted, every WhatsApp sender in the project is synced.
+ """
diff --git a/src/zavudev/types/template_sync_response.py b/src/zavudev/types/template_sync_response.py
new file mode 100644
index 0000000..8fa51e6
--- /dev/null
+++ b/src/zavudev/types/template_sync_response.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["TemplateSyncResponse"]
+
+
+class TemplateSyncResponse(BaseModel):
+ accounts_synced: int = FieldInfo(alias="accountsSynced")
+ """WhatsApp Business Accounts reconciled in this call."""
+
+ errors: List[str]
+ """Problems hit while syncing.
+
+ Non-empty with a 200 means part of the sync did not complete — the rest still
+ did.
+ """
+
+ imported: int
+ """Templates that existed on Meta and were created in Zavu by this call."""
+
+ linked: int
+ """
+ Existing Zavu templates that were matched to a Meta template by name and bound
+ to its Meta ID.
+ """
+
+ skipped: int
+ """
+ Meta templates left alone: already linked to a Zavu template, or
+ rejected/disabled on Meta.
+ """
+
+ updated: int
+ """Templates whose approval status changed to match Meta."""
diff --git a/src/zavudev/types/url_escalate_params.py b/src/zavudev/types/url_escalate_params.py
new file mode 100644
index 0000000..de52733
--- /dev/null
+++ b/src/zavudev/types/url_escalate_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["URLEscalateParams"]
+
+
+class URLEscalateParams(TypedDict, total=False):
+ reason: Required[str]
+ """Why the URL should be reviewed manually."""
diff --git a/src/zavudev/types/url_escalate_response.py b/src/zavudev/types/url_escalate_response.py
new file mode 100644
index 0000000..8192228
--- /dev/null
+++ b/src/zavudev/types/url_escalate_response.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+from .verified_url import VerifiedURL
+
+__all__ = ["URLEscalateResponse"]
+
+
+class URLEscalateResponse(BaseModel):
+ message: str
+
+ url: VerifiedURL
diff --git a/tests/api_resources/agents/__init__.py b/tests/api_resources/agents/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/agents/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/agents/test_senders.py b/tests/api_resources/agents/test_senders.py
new file mode 100644
index 0000000..51f8780
--- /dev/null
+++ b/tests/api_resources/agents/test_senders.py
@@ -0,0 +1,220 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types.agents import SenderConnectResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSenders:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_connect(self, client: Zavudev) -> None:
+ sender = client.agents.senders.connect(
+ agent_id="agentId",
+ sender_id="senderId",
+ )
+ assert_matches_type(SenderConnectResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_connect(self, client: Zavudev) -> None:
+ response = client.agents.senders.with_raw_response.connect(
+ agent_id="agentId",
+ sender_id="senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sender = response.parse()
+ assert_matches_type(SenderConnectResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_connect(self, client: Zavudev) -> None:
+ with client.agents.senders.with_streaming_response.connect(
+ agent_id="agentId",
+ sender_id="senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sender = response.parse()
+ assert_matches_type(SenderConnectResponse, sender, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_connect(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.agents.senders.with_raw_response.connect(
+ agent_id="",
+ sender_id="senderId",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_disconnect(self, client: Zavudev) -> None:
+ sender = client.agents.senders.disconnect(
+ sender_id="senderId",
+ agent_id="agentId",
+ )
+ assert sender is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_disconnect(self, client: Zavudev) -> None:
+ response = client.agents.senders.with_raw_response.disconnect(
+ sender_id="senderId",
+ agent_id="agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sender = response.parse()
+ assert sender is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_disconnect(self, client: Zavudev) -> None:
+ with client.agents.senders.with_streaming_response.disconnect(
+ sender_id="senderId",
+ agent_id="agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sender = response.parse()
+ assert sender is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_disconnect(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.agents.senders.with_raw_response.disconnect(
+ sender_id="senderId",
+ agent_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.agents.senders.with_raw_response.disconnect(
+ sender_id="",
+ agent_id="agentId",
+ )
+
+
+class TestAsyncSenders:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_connect(self, async_client: AsyncZavudev) -> None:
+ sender = await async_client.agents.senders.connect(
+ agent_id="agentId",
+ sender_id="senderId",
+ )
+ assert_matches_type(SenderConnectResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_connect(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.senders.with_raw_response.connect(
+ agent_id="agentId",
+ sender_id="senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sender = await response.parse()
+ assert_matches_type(SenderConnectResponse, sender, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_connect(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.senders.with_streaming_response.connect(
+ agent_id="agentId",
+ sender_id="senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sender = await response.parse()
+ assert_matches_type(SenderConnectResponse, sender, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_connect(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.agents.senders.with_raw_response.connect(
+ agent_id="",
+ sender_id="senderId",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_disconnect(self, async_client: AsyncZavudev) -> None:
+ sender = await async_client.agents.senders.disconnect(
+ sender_id="senderId",
+ agent_id="agentId",
+ )
+ assert sender is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_disconnect(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.senders.with_raw_response.disconnect(
+ sender_id="senderId",
+ agent_id="agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sender = await response.parse()
+ assert sender is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_disconnect(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.senders.with_streaming_response.disconnect(
+ sender_id="senderId",
+ agent_id="agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sender = await response.parse()
+ assert sender is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_disconnect(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.agents.senders.with_raw_response.disconnect(
+ sender_id="senderId",
+ agent_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.agents.senders.with_raw_response.disconnect(
+ sender_id="",
+ agent_id="agentId",
+ )
diff --git a/tests/api_resources/functions/test_git_link.py b/tests/api_resources/functions/test_git_link.py
new file mode 100644
index 0000000..34051dc
--- /dev/null
+++ b/tests/api_resources/functions/test_git_link.py
@@ -0,0 +1,513 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types.functions import (
+ GitLinkLinkResponse,
+ GitLinkUpdateResponse,
+ GitLinkRetrieveResponse,
+ GitLinkDeployNowResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestGitLink:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.retrieve(
+ "functionId",
+ )
+ assert_matches_type(GitLinkRetrieveResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Zavudev) -> None:
+ response = client.functions.git_link.with_raw_response.retrieve(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = response.parse()
+ assert_matches_type(GitLinkRetrieveResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Zavudev) -> None:
+ with client.functions.git_link.with_streaming_response.retrieve(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = response.parse()
+ assert_matches_type(GitLinkRetrieveResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.git_link.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.update(
+ function_id="functionId",
+ )
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update_with_all_params(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.update(
+ function_id="functionId",
+ auto_deploy=False,
+ branch="branch",
+ root_dir="rootDir",
+ )
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Zavudev) -> None:
+ response = client.functions.git_link.with_raw_response.update(
+ function_id="functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = response.parse()
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Zavudev) -> None:
+ with client.functions.git_link.with_streaming_response.update(
+ function_id="functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = response.parse()
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_update(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.git_link.with_raw_response.update(
+ function_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_deploy_now(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.deploy_now(
+ "functionId",
+ )
+ assert_matches_type(GitLinkDeployNowResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_deploy_now(self, client: Zavudev) -> None:
+ response = client.functions.git_link.with_raw_response.deploy_now(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = response.parse()
+ assert_matches_type(GitLinkDeployNowResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_deploy_now(self, client: Zavudev) -> None:
+ with client.functions.git_link.with_streaming_response.deploy_now(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = response.parse()
+ assert_matches_type(GitLinkDeployNowResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_deploy_now(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.git_link.with_raw_response.deploy_now(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_link(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ )
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_link_with_all_params(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ auto_deploy=True,
+ branch="main",
+ root_dir="apps/bot",
+ )
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_link(self, client: Zavudev) -> None:
+ response = client.functions.git_link.with_raw_response.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = response.parse()
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_link(self, client: Zavudev) -> None:
+ with client.functions.git_link.with_streaming_response.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = response.parse()
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_link(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.git_link.with_raw_response.link(
+ function_id="",
+ owner="acme",
+ repo="order-bot",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_unlink(self, client: Zavudev) -> None:
+ git_link = client.functions.git_link.unlink(
+ "functionId",
+ )
+ assert git_link is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_unlink(self, client: Zavudev) -> None:
+ response = client.functions.git_link.with_raw_response.unlink(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = response.parse()
+ assert git_link is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_unlink(self, client: Zavudev) -> None:
+ with client.functions.git_link.with_streaming_response.unlink(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = response.parse()
+ assert git_link is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_unlink(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.git_link.with_raw_response.unlink(
+ "",
+ )
+
+
+class TestAsyncGitLink:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.retrieve(
+ "functionId",
+ )
+ assert_matches_type(GitLinkRetrieveResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.git_link.with_raw_response.retrieve(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = await response.parse()
+ assert_matches_type(GitLinkRetrieveResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.git_link.with_streaming_response.retrieve(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = await response.parse()
+ assert_matches_type(GitLinkRetrieveResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.git_link.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.update(
+ function_id="functionId",
+ )
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.update(
+ function_id="functionId",
+ auto_deploy=False,
+ branch="branch",
+ root_dir="rootDir",
+ )
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.git_link.with_raw_response.update(
+ function_id="functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = await response.parse()
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.git_link.with_streaming_response.update(
+ function_id="functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = await response.parse()
+ assert_matches_type(GitLinkUpdateResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.git_link.with_raw_response.update(
+ function_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_deploy_now(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.deploy_now(
+ "functionId",
+ )
+ assert_matches_type(GitLinkDeployNowResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_deploy_now(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.git_link.with_raw_response.deploy_now(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = await response.parse()
+ assert_matches_type(GitLinkDeployNowResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_deploy_now(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.git_link.with_streaming_response.deploy_now(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = await response.parse()
+ assert_matches_type(GitLinkDeployNowResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_deploy_now(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.git_link.with_raw_response.deploy_now(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_link(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ )
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_link_with_all_params(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ auto_deploy=True,
+ branch="main",
+ root_dir="apps/bot",
+ )
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_link(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.git_link.with_raw_response.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = await response.parse()
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_link(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.git_link.with_streaming_response.link(
+ function_id="functionId",
+ owner="acme",
+ repo="order-bot",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = await response.parse()
+ assert_matches_type(GitLinkLinkResponse, git_link, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_link(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.git_link.with_raw_response.link(
+ function_id="",
+ owner="acme",
+ repo="order-bot",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_unlink(self, async_client: AsyncZavudev) -> None:
+ git_link = await async_client.functions.git_link.unlink(
+ "functionId",
+ )
+ assert git_link is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_unlink(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.git_link.with_raw_response.unlink(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ git_link = await response.parse()
+ assert git_link is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_unlink(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.git_link.with_streaming_response.unlink(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ git_link = await response.parse()
+ assert git_link is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_unlink(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.git_link.with_raw_response.unlink(
+ "",
+ )
diff --git a/tests/api_resources/functions/test_triggers.py b/tests/api_resources/functions/test_triggers.py
new file mode 100644
index 0000000..bc6339e
--- /dev/null
+++ b/tests/api_resources/functions/test_triggers.py
@@ -0,0 +1,410 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types.functions import (
+ TriggerListResponse,
+ TriggerCreateResponse,
+ TriggerUpdateResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTriggers:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Zavudev) -> None:
+ trigger = client.functions.triggers.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ )
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Zavudev) -> None:
+ trigger = client.functions.triggers.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ cron="0 9 * * 1-5",
+ )
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Zavudev) -> None:
+ response = client.functions.triggers.with_raw_response.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = response.parse()
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Zavudev) -> None:
+ with client.functions.triggers.with_streaming_response.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = response.parse()
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_create(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.triggers.with_raw_response.create(
+ function_id="",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Zavudev) -> None:
+ trigger = client.functions.triggers.update(
+ trigger_id="triggerId",
+ active=True,
+ )
+ assert_matches_type(TriggerUpdateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Zavudev) -> None:
+ response = client.functions.triggers.with_raw_response.update(
+ trigger_id="triggerId",
+ active=True,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = response.parse()
+ assert_matches_type(TriggerUpdateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Zavudev) -> None:
+ with client.functions.triggers.with_streaming_response.update(
+ trigger_id="triggerId",
+ active=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = response.parse()
+ assert_matches_type(TriggerUpdateResponse, trigger, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_update(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `trigger_id` but received ''"):
+ client.functions.triggers.with_raw_response.update(
+ trigger_id="",
+ active=True,
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Zavudev) -> None:
+ trigger = client.functions.triggers.list(
+ "functionId",
+ )
+ assert_matches_type(TriggerListResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Zavudev) -> None:
+ response = client.functions.triggers.with_raw_response.list(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = response.parse()
+ assert_matches_type(TriggerListResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Zavudev) -> None:
+ with client.functions.triggers.with_streaming_response.list(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = response.parse()
+ assert_matches_type(TriggerListResponse, trigger, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_list(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.triggers.with_raw_response.list(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Zavudev) -> None:
+ trigger = client.functions.triggers.delete(
+ "triggerId",
+ )
+ assert trigger is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Zavudev) -> None:
+ response = client.functions.triggers.with_raw_response.delete(
+ "triggerId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = response.parse()
+ assert trigger is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Zavudev) -> None:
+ with client.functions.triggers.with_streaming_response.delete(
+ "triggerId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = response.parse()
+ assert trigger is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `trigger_id` but received ''"):
+ client.functions.triggers.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncTriggers:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncZavudev) -> None:
+ trigger = await async_client.functions.triggers.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ )
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncZavudev) -> None:
+ trigger = await async_client.functions.triggers.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ cron="0 9 * * 1-5",
+ )
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.triggers.with_raw_response.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = await response.parse()
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.triggers.with_streaming_response.create(
+ function_id="functionId",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = await response.parse()
+ assert_matches_type(TriggerCreateResponse, trigger, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.triggers.with_raw_response.create(
+ function_id="",
+ event_types=["message.inbound"],
+ sender_ids=[None],
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncZavudev) -> None:
+ trigger = await async_client.functions.triggers.update(
+ trigger_id="triggerId",
+ active=True,
+ )
+ assert_matches_type(TriggerUpdateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.triggers.with_raw_response.update(
+ trigger_id="triggerId",
+ active=True,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = await response.parse()
+ assert_matches_type(TriggerUpdateResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.triggers.with_streaming_response.update(
+ trigger_id="triggerId",
+ active=True,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = await response.parse()
+ assert_matches_type(TriggerUpdateResponse, trigger, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `trigger_id` but received ''"):
+ await async_client.functions.triggers.with_raw_response.update(
+ trigger_id="",
+ active=True,
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncZavudev) -> None:
+ trigger = await async_client.functions.triggers.list(
+ "functionId",
+ )
+ assert_matches_type(TriggerListResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.triggers.with_raw_response.list(
+ "functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = await response.parse()
+ assert_matches_type(TriggerListResponse, trigger, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.triggers.with_streaming_response.list(
+ "functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = await response.parse()
+ assert_matches_type(TriggerListResponse, trigger, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.triggers.with_raw_response.list(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncZavudev) -> None:
+ trigger = await async_client.functions.triggers.delete(
+ "triggerId",
+ )
+ assert trigger is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.triggers.with_raw_response.delete(
+ "triggerId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ trigger = await response.parse()
+ assert trigger is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.triggers.with_streaming_response.delete(
+ "triggerId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ trigger = await response.parse()
+ assert trigger is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `trigger_id` but received ''"):
+ await async_client.functions.triggers.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/senders/agent/knowledge_bases/test_documents.py b/tests/api_resources/senders/agent/knowledge_bases/test_documents.py
index e181e4d..8f77a07 100644
--- a/tests/api_resources/senders/agent/knowledge_bases/test_documents.py
+++ b/tests/api_resources/senders/agent/knowledge_bases/test_documents.py
@@ -13,6 +13,8 @@
from zavudev.types.senders.agent import AgentDocument
from zavudev.types.senders.agent.knowledge_bases import (
DocumentCreateResponse,
+ DocumentUpdateDocumentResponse,
+ DocumentRetrieveDocumentResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -210,6 +212,146 @@ def test_path_params_delete(self, client: Zavudev) -> None:
kb_id="kbId",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve_document(self, client: Zavudev) -> None:
+ document = client.senders.agent.knowledge_bases.documents.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+ assert_matches_type(DocumentRetrieveDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve_document(self, client: Zavudev) -> None:
+ response = client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = response.parse()
+ assert_matches_type(DocumentRetrieveDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve_document(self, client: Zavudev) -> None:
+ with client.senders.agent.knowledge_bases.documents.with_streaming_response.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = response.parse()
+ assert_matches_type(DocumentRetrieveDocumentResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve_document(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="docId",
+ sender_id="",
+ kb_id="kbId",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `kb_id` but received ''"):
+ client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `doc_id` but received ''"):
+ client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update_document(self, client: Zavudev) -> None:
+ document = client.senders.agent.knowledge_bases.documents.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update_document_with_all_params(self, client: Zavudev) -> None:
+ document = client.senders.agent.knowledge_bases.documents.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ content="content",
+ title="title",
+ )
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_update_document(self, client: Zavudev) -> None:
+ response = client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = response.parse()
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_update_document(self, client: Zavudev) -> None:
+ with client.senders.agent.knowledge_bases.documents.with_streaming_response.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = response.parse()
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_update_document(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="docId",
+ sender_id="",
+ kb_id="kbId",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `kb_id` but received ''"):
+ client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `doc_id` but received ''"):
+ client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
class TestAsyncDocuments:
parametrize = pytest.mark.parametrize(
@@ -404,3 +546,143 @@ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None:
sender_id="senderId",
kb_id="kbId",
)
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve_document(self, async_client: AsyncZavudev) -> None:
+ document = await async_client.senders.agent.knowledge_bases.documents.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+ assert_matches_type(DocumentRetrieveDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve_document(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = await response.parse()
+ assert_matches_type(DocumentRetrieveDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve_document(self, async_client: AsyncZavudev) -> None:
+ async with async_client.senders.agent.knowledge_bases.documents.with_streaming_response.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = await response.parse()
+ assert_matches_type(DocumentRetrieveDocumentResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve_document(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="docId",
+ sender_id="",
+ kb_id="kbId",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `kb_id` but received ''"):
+ await async_client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `doc_id` but received ''"):
+ await async_client.senders.agent.knowledge_bases.documents.with_raw_response.retrieve_document(
+ doc_id="",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update_document(self, async_client: AsyncZavudev) -> None:
+ document = await async_client.senders.agent.knowledge_bases.documents.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update_document_with_all_params(self, async_client: AsyncZavudev) -> None:
+ document = await async_client.senders.agent.knowledge_bases.documents.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ content="content",
+ title="title",
+ )
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_update_document(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ document = await response.parse()
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_update_document(self, async_client: AsyncZavudev) -> None:
+ async with async_client.senders.agent.knowledge_bases.documents.with_streaming_response.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="kbId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ document = await response.parse()
+ assert_matches_type(DocumentUpdateDocumentResponse, document, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_update_document(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="docId",
+ sender_id="",
+ kb_id="kbId",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `kb_id` but received ''"):
+ await async_client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="docId",
+ sender_id="senderId",
+ kb_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `doc_id` but received ''"):
+ await async_client.senders.agent.knowledge_bases.documents.with_raw_response.update_document(
+ doc_id="",
+ sender_id="senderId",
+ kb_id="kbId",
+ )
diff --git a/tests/api_resources/senders/agent/test_tools.py b/tests/api_resources/senders/agent/test_tools.py
index c14f560..1b9cbf4 100644
--- a/tests/api_resources/senders/agent/test_tools.py
+++ b/tests/api_resources/senders/agent/test_tools.py
@@ -16,6 +16,7 @@
ToolCreateResponse,
ToolUpdateResponse,
ToolRetrieveResponse,
+ ToolListTestRunsResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -354,6 +355,68 @@ def test_path_params_delete(self, client: Zavudev) -> None:
sender_id="senderId",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_test_runs(self, client: Zavudev) -> None:
+ tool = client.senders.agent.tools.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_test_runs_with_all_params(self, client: Zavudev) -> None:
+ tool = client.senders.agent.tools.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ limit=100,
+ )
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_test_runs(self, client: Zavudev) -> None:
+ response = client.senders.agent.tools.with_raw_response.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tool = response.parse()
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_test_runs(self, client: Zavudev) -> None:
+ with client.senders.agent.tools.with_streaming_response.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tool = response.parse()
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_list_test_runs(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.senders.agent.tools.with_raw_response.list_test_runs(
+ tool_id="toolId",
+ sender_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tool_id` but received ''"):
+ client.senders.agent.tools.with_raw_response.list_test_runs(
+ tool_id="",
+ sender_id="senderId",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_test(self, client: Zavudev) -> None:
@@ -747,6 +810,68 @@ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None:
sender_id="senderId",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_test_runs(self, async_client: AsyncZavudev) -> None:
+ tool = await async_client.senders.agent.tools.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_test_runs_with_all_params(self, async_client: AsyncZavudev) -> None:
+ tool = await async_client.senders.agent.tools.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ limit=100,
+ )
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_test_runs(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.senders.agent.tools.with_raw_response.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tool = await response.parse()
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_test_runs(self, async_client: AsyncZavudev) -> None:
+ async with async_client.senders.agent.tools.with_streaming_response.list_test_runs(
+ tool_id="toolId",
+ sender_id="senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tool = await response.parse()
+ assert_matches_type(ToolListTestRunsResponse, tool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_list_test_runs(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.senders.agent.tools.with_raw_response.list_test_runs(
+ tool_id="toolId",
+ sender_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tool_id` but received ''"):
+ await async_client.senders.agent.tools.with_raw_response.list_test_runs(
+ tool_id="",
+ sender_id="senderId",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_test(self, async_client: AsyncZavudev) -> None:
diff --git a/tests/api_resources/senders/agent/tools/__init__.py b/tests/api_resources/senders/agent/tools/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/senders/agent/tools/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/senders/agent/tools/test_webhook.py b/tests/api_resources/senders/agent/tools/test_webhook.py
new file mode 100644
index 0000000..3f92955
--- /dev/null
+++ b/tests/api_resources/senders/agent/tools/test_webhook.py
@@ -0,0 +1,128 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types import WebhookSecretResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestWebhook:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_rotate_secret(self, client: Zavudev) -> None:
+ webhook = client.senders.agent.tools.webhook.rotate_secret(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+ assert_matches_type(WebhookSecretResponse, webhook, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_rotate_secret(self, client: Zavudev) -> None:
+ response = client.senders.agent.tools.webhook.with_raw_response.rotate_secret(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookSecretResponse, webhook, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_rotate_secret(self, client: Zavudev) -> None:
+ with client.senders.agent.tools.webhook.with_streaming_response.rotate_secret(
+ tool_id="toolId",
+ sender_id="senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookSecretResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_rotate_secret(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.senders.agent.tools.webhook.with_raw_response.rotate_secret(
+ tool_id="toolId",
+ sender_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tool_id` but received ''"):
+ client.senders.agent.tools.webhook.with_raw_response.rotate_secret(
+ tool_id="",
+ sender_id="senderId",
+ )
+
+
+class TestAsyncWebhook:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_rotate_secret(self, async_client: AsyncZavudev) -> None:
+ webhook = await async_client.senders.agent.tools.webhook.rotate_secret(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+ assert_matches_type(WebhookSecretResponse, webhook, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_rotate_secret(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.senders.agent.tools.webhook.with_raw_response.rotate_secret(
+ tool_id="toolId",
+ sender_id="senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookSecretResponse, webhook, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_rotate_secret(self, async_client: AsyncZavudev) -> None:
+ async with async_client.senders.agent.tools.webhook.with_streaming_response.rotate_secret(
+ tool_id="toolId",
+ sender_id="senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookSecretResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_rotate_secret(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.senders.agent.tools.webhook.with_raw_response.rotate_secret(
+ tool_id="toolId",
+ sender_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tool_id` but received ''"):
+ await async_client.senders.agent.tools.webhook.with_raw_response.rotate_secret(
+ tool_id="",
+ sender_id="senderId",
+ )
diff --git a/tests/api_resources/senders/test_telegram.py b/tests/api_resources/senders/test_telegram.py
new file mode 100644
index 0000000..76ed36b
--- /dev/null
+++ b/tests/api_resources/senders/test_telegram.py
@@ -0,0 +1,200 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types.senders import TelegramConnectResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTelegram:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_connect(self, client: Zavudev) -> None:
+ telegram = client.senders.telegram.connect(
+ sender_id="senderId",
+ bot_token="botToken",
+ )
+ assert_matches_type(TelegramConnectResponse, telegram, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_connect(self, client: Zavudev) -> None:
+ response = client.senders.telegram.with_raw_response.connect(
+ sender_id="senderId",
+ bot_token="botToken",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ telegram = response.parse()
+ assert_matches_type(TelegramConnectResponse, telegram, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_connect(self, client: Zavudev) -> None:
+ with client.senders.telegram.with_streaming_response.connect(
+ sender_id="senderId",
+ bot_token="botToken",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ telegram = response.parse()
+ assert_matches_type(TelegramConnectResponse, telegram, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_connect(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.senders.telegram.with_raw_response.connect(
+ sender_id="",
+ bot_token="botToken",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_disconnect(self, client: Zavudev) -> None:
+ telegram = client.senders.telegram.disconnect(
+ "senderId",
+ )
+ assert telegram is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_disconnect(self, client: Zavudev) -> None:
+ response = client.senders.telegram.with_raw_response.disconnect(
+ "senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ telegram = response.parse()
+ assert telegram is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_disconnect(self, client: Zavudev) -> None:
+ with client.senders.telegram.with_streaming_response.disconnect(
+ "senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ telegram = response.parse()
+ assert telegram is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_disconnect(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ client.senders.telegram.with_raw_response.disconnect(
+ "",
+ )
+
+
+class TestAsyncTelegram:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_connect(self, async_client: AsyncZavudev) -> None:
+ telegram = await async_client.senders.telegram.connect(
+ sender_id="senderId",
+ bot_token="botToken",
+ )
+ assert_matches_type(TelegramConnectResponse, telegram, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_connect(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.senders.telegram.with_raw_response.connect(
+ sender_id="senderId",
+ bot_token="botToken",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ telegram = await response.parse()
+ assert_matches_type(TelegramConnectResponse, telegram, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_connect(self, async_client: AsyncZavudev) -> None:
+ async with async_client.senders.telegram.with_streaming_response.connect(
+ sender_id="senderId",
+ bot_token="botToken",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ telegram = await response.parse()
+ assert_matches_type(TelegramConnectResponse, telegram, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_connect(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.senders.telegram.with_raw_response.connect(
+ sender_id="",
+ bot_token="botToken",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_disconnect(self, async_client: AsyncZavudev) -> None:
+ telegram = await async_client.senders.telegram.disconnect(
+ "senderId",
+ )
+ assert telegram is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_disconnect(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.senders.telegram.with_raw_response.disconnect(
+ "senderId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ telegram = await response.parse()
+ assert telegram is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_disconnect(self, async_client: AsyncZavudev) -> None:
+ async with async_client.senders.telegram.with_streaming_response.disconnect(
+ "senderId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ telegram = await response.parse()
+ assert telegram is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_disconnect(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `sender_id` but received ''"):
+ await async_client.senders.telegram.with_raw_response.disconnect(
+ "",
+ )
diff --git a/tests/api_resources/test_agent_templates.py b/tests/api_resources/test_agent_templates.py
new file mode 100644
index 0000000..fe73f68
--- /dev/null
+++ b/tests/api_resources/test_agent_templates.py
@@ -0,0 +1,164 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types import AgentTemplateListResponse, AgentTemplateRetrieveResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAgentTemplates:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Zavudev) -> None:
+ agent_template = client.agent_templates.retrieve(
+ "fermi",
+ )
+ assert_matches_type(AgentTemplateRetrieveResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Zavudev) -> None:
+ response = client.agent_templates.with_raw_response.retrieve(
+ "fermi",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent_template = response.parse()
+ assert_matches_type(AgentTemplateRetrieveResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Zavudev) -> None:
+ with client.agent_templates.with_streaming_response.retrieve(
+ "fermi",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent_template = response.parse()
+ assert_matches_type(AgentTemplateRetrieveResponse, agent_template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `template_id` but received ''"):
+ client.agent_templates.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Zavudev) -> None:
+ agent_template = client.agent_templates.list()
+ assert_matches_type(AgentTemplateListResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Zavudev) -> None:
+ response = client.agent_templates.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent_template = response.parse()
+ assert_matches_type(AgentTemplateListResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Zavudev) -> None:
+ with client.agent_templates.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent_template = response.parse()
+ assert_matches_type(AgentTemplateListResponse, agent_template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncAgentTemplates:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncZavudev) -> None:
+ agent_template = await async_client.agent_templates.retrieve(
+ "fermi",
+ )
+ assert_matches_type(AgentTemplateRetrieveResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agent_templates.with_raw_response.retrieve(
+ "fermi",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent_template = await response.parse()
+ assert_matches_type(AgentTemplateRetrieveResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agent_templates.with_streaming_response.retrieve(
+ "fermi",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent_template = await response.parse()
+ assert_matches_type(AgentTemplateRetrieveResponse, agent_template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `template_id` but received ''"):
+ await async_client.agent_templates.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncZavudev) -> None:
+ agent_template = await async_client.agent_templates.list()
+ assert_matches_type(AgentTemplateListResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agent_templates.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent_template = await response.parse()
+ assert_matches_type(AgentTemplateListResponse, agent_template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agent_templates.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent_template = await response.parse()
+ assert_matches_type(AgentTemplateListResponse, agent_template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py
new file mode 100644
index 0000000..bf2c9b2
--- /dev/null
+++ b/tests/api_resources/test_agents.py
@@ -0,0 +1,792 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types import (
+ AgentTestResponse,
+ AgentCreateResponse,
+ AgentUpdateResponse,
+ AgentRetrieveResponse,
+ AgentListVoicesResponse,
+)
+from zavudev.pagination import SyncCursor, AsyncCursor
+from zavudev.types.senders.agent import Agent
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAgents:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Zavudev) -> None:
+ agent = client.agents.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ )
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Zavudev) -> None:
+ agent = client.agents.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ context_window_messages=1,
+ include_contact_metadata=True,
+ max_tokens=1,
+ temperature=0,
+ trigger_on_channels=["string"],
+ trigger_on_message_types=["string"],
+ voice={
+ "enabled": True,
+ "greeting": "Hi, thanks for calling Acme. How can I help you today?",
+ "greetings": {"es": "Hola, soy Atlas. Preguntame lo que quieras."},
+ "interruptible": True,
+ "language": "en",
+ "max_call_duration_minutes": 1,
+ "max_idle_seconds": 5,
+ "model": "openai/gpt-4o",
+ "record_calls": True,
+ "stt_model": "sttModel",
+ "stt_provider": "sttProvider",
+ "transfer_phone_number": "+14155551234",
+ "tts_provider": "ttsProvider",
+ "tts_voice_id": "aria",
+ "voicemail_action": "hangup",
+ "voicemail_message": "voicemailMessage",
+ "voice_speed": 0.5,
+ },
+ )
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Zavudev) -> None:
+ agent = client.agents.retrieve(
+ "agentId",
+ )
+ assert_matches_type(AgentRetrieveResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.retrieve(
+ "agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentRetrieveResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.retrieve(
+ "agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentRetrieveResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.agents.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Zavudev) -> None:
+ agent = client.agents.update(
+ agent_id="agentId",
+ )
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update_with_all_params(self, client: Zavudev) -> None:
+ agent = client.agents.update(
+ agent_id="agentId",
+ api_key="apiKey",
+ context_window_messages=1,
+ enabled=True,
+ include_contact_metadata=True,
+ max_tokens=1,
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ temperature=0,
+ trigger_on_channels=["string"],
+ trigger_on_message_types=["string"],
+ voice={
+ "enabled": True,
+ "greeting": "Hi, thanks for calling Acme. How can I help you today?",
+ "greetings": {"es": "Hola, soy Atlas. Preguntame lo que quieras."},
+ "interruptible": True,
+ "language": "en",
+ "max_call_duration_minutes": 1,
+ "max_idle_seconds": 5,
+ "model": "openai/gpt-4o",
+ "record_calls": True,
+ "stt_model": "sttModel",
+ "stt_provider": "sttProvider",
+ "transfer_phone_number": "+14155551234",
+ "tts_provider": "ttsProvider",
+ "tts_voice_id": "aria",
+ "voicemail_action": "hangup",
+ "voicemail_message": "voicemailMessage",
+ "voice_speed": 0.5,
+ },
+ )
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.update(
+ agent_id="agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.update(
+ agent_id="agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_update(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.agents.with_raw_response.update(
+ agent_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Zavudev) -> None:
+ agent = client.agents.list()
+ assert_matches_type(SyncCursor[Agent], agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Zavudev) -> None:
+ agent = client.agents.list(
+ cursor="cursor",
+ limit=100,
+ )
+ assert_matches_type(SyncCursor[Agent], agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(SyncCursor[Agent], agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(SyncCursor[Agent], agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Zavudev) -> None:
+ agent = client.agents.delete(
+ "agentId",
+ )
+ assert agent is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.delete(
+ "agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert agent is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.delete(
+ "agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert agent is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.agents.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_voices(self, client: Zavudev) -> None:
+ agent = client.agents.list_voices()
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_voices_with_all_params(self, client: Zavudev) -> None:
+ agent = client.agents.list_voices(
+ language="es",
+ )
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_voices(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.list_voices()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_voices(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.list_voices() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_test(self, client: Zavudev) -> None:
+ agent = client.agents.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ )
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_test_with_all_params(self, client: Zavudev) -> None:
+ agent = client.agents.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ execute_tools=True,
+ history=[
+ {
+ "content": "content",
+ "role": "user",
+ }
+ ],
+ use_knowledge_base=True,
+ )
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_test(self, client: Zavudev) -> None:
+ response = client.agents.with_raw_response.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_test(self, client: Zavudev) -> None:
+ with client.agents.with_streaming_response.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_test(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ client.agents.with_raw_response.test(
+ agent_id="",
+ message="Where is order ORD-12345?",
+ )
+
+
+class TestAsyncAgents:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ )
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ context_window_messages=1,
+ include_contact_metadata=True,
+ max_tokens=1,
+ temperature=0,
+ trigger_on_channels=["string"],
+ trigger_on_message_types=["string"],
+ voice={
+ "enabled": True,
+ "greeting": "Hi, thanks for calling Acme. How can I help you today?",
+ "greetings": {"es": "Hola, soy Atlas. Preguntame lo que quieras."},
+ "interruptible": True,
+ "language": "en",
+ "max_call_duration_minutes": 1,
+ "max_idle_seconds": 5,
+ "model": "openai/gpt-4o",
+ "record_calls": True,
+ "stt_model": "sttModel",
+ "stt_provider": "sttProvider",
+ "transfer_phone_number": "+14155551234",
+ "tts_provider": "ttsProvider",
+ "tts_voice_id": "aria",
+ "voicemail_action": "hangup",
+ "voicemail_message": "voicemailMessage",
+ "voice_speed": 0.5,
+ },
+ )
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.create(
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentCreateResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.retrieve(
+ "agentId",
+ )
+ assert_matches_type(AgentRetrieveResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.retrieve(
+ "agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentRetrieveResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.retrieve(
+ "agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentRetrieveResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.agents.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.update(
+ agent_id="agentId",
+ )
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.update(
+ agent_id="agentId",
+ api_key="apiKey",
+ context_window_messages=1,
+ enabled=True,
+ include_contact_metadata=True,
+ max_tokens=1,
+ model="model",
+ name="name",
+ provider="openai",
+ system_prompt="systemPrompt",
+ temperature=0,
+ trigger_on_channels=["string"],
+ trigger_on_message_types=["string"],
+ voice={
+ "enabled": True,
+ "greeting": "Hi, thanks for calling Acme. How can I help you today?",
+ "greetings": {"es": "Hola, soy Atlas. Preguntame lo que quieras."},
+ "interruptible": True,
+ "language": "en",
+ "max_call_duration_minutes": 1,
+ "max_idle_seconds": 5,
+ "model": "openai/gpt-4o",
+ "record_calls": True,
+ "stt_model": "sttModel",
+ "stt_provider": "sttProvider",
+ "transfer_phone_number": "+14155551234",
+ "tts_provider": "ttsProvider",
+ "tts_voice_id": "aria",
+ "voicemail_action": "hangup",
+ "voicemail_message": "voicemailMessage",
+ "voice_speed": 0.5,
+ },
+ )
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.update(
+ agent_id="agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.update(
+ agent_id="agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentUpdateResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.agents.with_raw_response.update(
+ agent_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.list()
+ assert_matches_type(AsyncCursor[Agent], agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.list(
+ cursor="cursor",
+ limit=100,
+ )
+ assert_matches_type(AsyncCursor[Agent], agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AsyncCursor[Agent], agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AsyncCursor[Agent], agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.delete(
+ "agentId",
+ )
+ assert agent is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.delete(
+ "agentId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert agent is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.delete(
+ "agentId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert agent is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.agents.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_voices(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.list_voices()
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_voices_with_all_params(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.list_voices(
+ language="es",
+ )
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_voices(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.list_voices()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_voices(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.list_voices() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentListVoicesResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_test(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ )
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_test_with_all_params(self, async_client: AsyncZavudev) -> None:
+ agent = await async_client.agents.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ execute_tools=True,
+ history=[
+ {
+ "content": "content",
+ "role": "user",
+ }
+ ],
+ use_knowledge_base=True,
+ )
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_test(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.agents.with_raw_response.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_test(self, async_client: AsyncZavudev) -> None:
+ async with async_client.agents.with_streaming_response.test(
+ agent_id="agentId",
+ message="Where is order ORD-12345?",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentTestResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_test(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"):
+ await async_client.agents.with_raw_response.test(
+ agent_id="",
+ message="Where is order ORD-12345?",
+ )
diff --git a/tests/api_resources/test_calls.py b/tests/api_resources/test_calls.py
new file mode 100644
index 0000000..4e2eabe
--- /dev/null
+++ b/tests/api_resources/test_calls.py
@@ -0,0 +1,370 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types import (
+ CallListResponse,
+ CallCreateResponse,
+ CallHangupResponse,
+ CallRetrieveResponse,
+)
+from zavudev.pagination import SyncCursor, AsyncCursor
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestCalls:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Zavudev) -> None:
+ call = client.calls.create(
+ to="+56912345678",
+ )
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Zavudev) -> None:
+ call = client.calls.create(
+ to="+56912345678",
+ greeting="greeting",
+ language="es-ES",
+ max_duration_minutes=1,
+ metadata={"foo": "string"},
+ sender_id="sender_12345",
+ )
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Zavudev) -> None:
+ response = client.calls.with_raw_response.create(
+ to="+56912345678",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = response.parse()
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Zavudev) -> None:
+ with client.calls.with_streaming_response.create(
+ to="+56912345678",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = response.parse()
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Zavudev) -> None:
+ call = client.calls.retrieve(
+ "callId",
+ )
+ assert_matches_type(CallRetrieveResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Zavudev) -> None:
+ response = client.calls.with_raw_response.retrieve(
+ "callId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = response.parse()
+ assert_matches_type(CallRetrieveResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Zavudev) -> None:
+ with client.calls.with_streaming_response.retrieve(
+ "callId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = response.parse()
+ assert_matches_type(CallRetrieveResponse, call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"):
+ client.calls.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Zavudev) -> None:
+ call = client.calls.list()
+ assert_matches_type(SyncCursor[CallListResponse], call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Zavudev) -> None:
+ call = client.calls.list(
+ cursor="cursor",
+ direction="inbound",
+ limit=100,
+ status="queued",
+ )
+ assert_matches_type(SyncCursor[CallListResponse], call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Zavudev) -> None:
+ response = client.calls.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = response.parse()
+ assert_matches_type(SyncCursor[CallListResponse], call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Zavudev) -> None:
+ with client.calls.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = response.parse()
+ assert_matches_type(SyncCursor[CallListResponse], call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_hangup(self, client: Zavudev) -> None:
+ call = client.calls.hangup(
+ "callId",
+ )
+ assert_matches_type(CallHangupResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_hangup(self, client: Zavudev) -> None:
+ response = client.calls.with_raw_response.hangup(
+ "callId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = response.parse()
+ assert_matches_type(CallHangupResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_hangup(self, client: Zavudev) -> None:
+ with client.calls.with_streaming_response.hangup(
+ "callId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = response.parse()
+ assert_matches_type(CallHangupResponse, call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_hangup(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"):
+ client.calls.with_raw_response.hangup(
+ "",
+ )
+
+
+class TestAsyncCalls:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncZavudev) -> None:
+ call = await async_client.calls.create(
+ to="+56912345678",
+ )
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncZavudev) -> None:
+ call = await async_client.calls.create(
+ to="+56912345678",
+ greeting="greeting",
+ language="es-ES",
+ max_duration_minutes=1,
+ metadata={"foo": "string"},
+ sender_id="sender_12345",
+ )
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.calls.with_raw_response.create(
+ to="+56912345678",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = await response.parse()
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncZavudev) -> None:
+ async with async_client.calls.with_streaming_response.create(
+ to="+56912345678",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = await response.parse()
+ assert_matches_type(CallCreateResponse, call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncZavudev) -> None:
+ call = await async_client.calls.retrieve(
+ "callId",
+ )
+ assert_matches_type(CallRetrieveResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.calls.with_raw_response.retrieve(
+ "callId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = await response.parse()
+ assert_matches_type(CallRetrieveResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ async with async_client.calls.with_streaming_response.retrieve(
+ "callId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = await response.parse()
+ assert_matches_type(CallRetrieveResponse, call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"):
+ await async_client.calls.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncZavudev) -> None:
+ call = await async_client.calls.list()
+ assert_matches_type(AsyncCursor[CallListResponse], call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncZavudev) -> None:
+ call = await async_client.calls.list(
+ cursor="cursor",
+ direction="inbound",
+ limit=100,
+ status="queued",
+ )
+ assert_matches_type(AsyncCursor[CallListResponse], call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.calls.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = await response.parse()
+ assert_matches_type(AsyncCursor[CallListResponse], call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None:
+ async with async_client.calls.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = await response.parse()
+ assert_matches_type(AsyncCursor[CallListResponse], call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_hangup(self, async_client: AsyncZavudev) -> None:
+ call = await async_client.calls.hangup(
+ "callId",
+ )
+ assert_matches_type(CallHangupResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_hangup(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.calls.with_raw_response.hangup(
+ "callId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ call = await response.parse()
+ assert_matches_type(CallHangupResponse, call, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_hangup(self, async_client: AsyncZavudev) -> None:
+ async with async_client.calls.with_streaming_response.hangup(
+ "callId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ call = await response.parse()
+ assert_matches_type(CallHangupResponse, call, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_hangup(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"):
+ await async_client.calls.with_raw_response.hangup(
+ "",
+ )
diff --git a/tests/api_resources/test_contacts.py b/tests/api_resources/test_contacts.py
index 2ac18c2..c1a3c02 100644
--- a/tests/api_resources/test_contacts.py
+++ b/tests/api_resources/test_contacts.py
@@ -143,6 +143,7 @@ def test_method_update_with_all_params(self, client: Zavudev) -> None:
contact = client.contacts.update(
contact_id="contactId",
default_channel="sms",
+ display_name="John Doe",
metadata={"foo": "string"},
)
assert_matches_type(Contact, contact, path=["response"])
@@ -194,6 +195,8 @@ def test_method_list_with_all_params(self, client: Zavudev) -> None:
cursor="cursor",
limit=100,
phone_number="phoneNumber",
+ search="search",
+ tag=["string"],
)
assert_matches_type(SyncCursor[Contact], contact, path=["response"])
@@ -261,48 +264,6 @@ def test_path_params_delete(self, client: Zavudev) -> None:
"",
)
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_dismiss_merge_suggestion(self, client: Zavudev) -> None:
- contact = client.contacts.dismiss_merge_suggestion(
- "contactId",
- )
- assert contact is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_raw_response_dismiss_merge_suggestion(self, client: Zavudev) -> None:
- response = client.contacts.with_raw_response.dismiss_merge_suggestion(
- "contactId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- contact = response.parse()
- assert contact is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_streaming_response_dismiss_merge_suggestion(self, client: Zavudev) -> None:
- with client.contacts.with_streaming_response.dismiss_merge_suggestion(
- "contactId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- contact = response.parse()
- assert contact is None
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_path_params_dismiss_merge_suggestion(self, client: Zavudev) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `contact_id` but received ''"):
- client.contacts.with_raw_response.dismiss_merge_suggestion(
- "",
- )
-
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_merge(self, client: Zavudev) -> None:
@@ -520,6 +481,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncZavudev) -
contact = await async_client.contacts.update(
contact_id="contactId",
default_channel="sms",
+ display_name="John Doe",
metadata={"foo": "string"},
)
assert_matches_type(Contact, contact, path=["response"])
@@ -571,6 +533,8 @@ async def test_method_list_with_all_params(self, async_client: AsyncZavudev) ->
cursor="cursor",
limit=100,
phone_number="phoneNumber",
+ search="search",
+ tag=["string"],
)
assert_matches_type(AsyncCursor[Contact], contact, path=["response"])
@@ -638,48 +602,6 @@ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None:
"",
)
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_dismiss_merge_suggestion(self, async_client: AsyncZavudev) -> None:
- contact = await async_client.contacts.dismiss_merge_suggestion(
- "contactId",
- )
- assert contact is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_dismiss_merge_suggestion(self, async_client: AsyncZavudev) -> None:
- response = await async_client.contacts.with_raw_response.dismiss_merge_suggestion(
- "contactId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- contact = await response.parse()
- assert contact is None
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_dismiss_merge_suggestion(self, async_client: AsyncZavudev) -> None:
- async with async_client.contacts.with_streaming_response.dismiss_merge_suggestion(
- "contactId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- contact = await response.parse()
- assert contact is None
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_path_params_dismiss_merge_suggestion(self, async_client: AsyncZavudev) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `contact_id` but received ''"):
- await async_client.contacts.with_raw_response.dismiss_merge_suggestion(
- "",
- )
-
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_merge(self, async_client: AsyncZavudev) -> None:
diff --git a/tests/api_resources/test_conversations.py b/tests/api_resources/test_conversations.py
new file mode 100644
index 0000000..f82cb2b
--- /dev/null
+++ b/tests/api_resources/test_conversations.py
@@ -0,0 +1,382 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types import (
+ Message,
+ ConversationListResponse,
+ ConversationRetrieveResponse,
+ ConversationMarkAsReadResponse,
+)
+from zavudev.pagination import SyncCursor, AsyncCursor
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestConversations:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Zavudev) -> None:
+ conversation = client.conversations.retrieve(
+ "conversationId",
+ )
+ assert_matches_type(ConversationRetrieveResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Zavudev) -> None:
+ response = client.conversations.with_raw_response.retrieve(
+ "conversationId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = response.parse()
+ assert_matches_type(ConversationRetrieveResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Zavudev) -> None:
+ with client.conversations.with_streaming_response.retrieve(
+ "conversationId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = response.parse()
+ assert_matches_type(ConversationRetrieveResponse, conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"):
+ client.conversations.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Zavudev) -> None:
+ conversation = client.conversations.list()
+ assert_matches_type(SyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Zavudev) -> None:
+ conversation = client.conversations.list(
+ channel="sms",
+ cursor="cursor",
+ limit=100,
+ search="+56912345678",
+ sender_id="senderId",
+ )
+ assert_matches_type(SyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Zavudev) -> None:
+ response = client.conversations.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = response.parse()
+ assert_matches_type(SyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Zavudev) -> None:
+ with client.conversations.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = response.parse()
+ assert_matches_type(SyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_messages(self, client: Zavudev) -> None:
+ conversation = client.conversations.list_messages(
+ conversation_id="conversationId",
+ )
+ assert_matches_type(SyncCursor[Message], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_messages_with_all_params(self, client: Zavudev) -> None:
+ conversation = client.conversations.list_messages(
+ conversation_id="conversationId",
+ cursor="cursor",
+ limit=100,
+ )
+ assert_matches_type(SyncCursor[Message], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_messages(self, client: Zavudev) -> None:
+ response = client.conversations.with_raw_response.list_messages(
+ conversation_id="conversationId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = response.parse()
+ assert_matches_type(SyncCursor[Message], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_messages(self, client: Zavudev) -> None:
+ with client.conversations.with_streaming_response.list_messages(
+ conversation_id="conversationId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = response.parse()
+ assert_matches_type(SyncCursor[Message], conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_list_messages(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"):
+ client.conversations.with_raw_response.list_messages(
+ conversation_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_mark_as_read(self, client: Zavudev) -> None:
+ conversation = client.conversations.mark_as_read(
+ "conversationId",
+ )
+ assert_matches_type(ConversationMarkAsReadResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_mark_as_read(self, client: Zavudev) -> None:
+ response = client.conversations.with_raw_response.mark_as_read(
+ "conversationId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = response.parse()
+ assert_matches_type(ConversationMarkAsReadResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_mark_as_read(self, client: Zavudev) -> None:
+ with client.conversations.with_streaming_response.mark_as_read(
+ "conversationId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = response.parse()
+ assert_matches_type(ConversationMarkAsReadResponse, conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_mark_as_read(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"):
+ client.conversations.with_raw_response.mark_as_read(
+ "",
+ )
+
+
+class TestAsyncConversations:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncZavudev) -> None:
+ conversation = await async_client.conversations.retrieve(
+ "conversationId",
+ )
+ assert_matches_type(ConversationRetrieveResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.conversations.with_raw_response.retrieve(
+ "conversationId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = await response.parse()
+ assert_matches_type(ConversationRetrieveResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ async with async_client.conversations.with_streaming_response.retrieve(
+ "conversationId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = await response.parse()
+ assert_matches_type(ConversationRetrieveResponse, conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"):
+ await async_client.conversations.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncZavudev) -> None:
+ conversation = await async_client.conversations.list()
+ assert_matches_type(AsyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncZavudev) -> None:
+ conversation = await async_client.conversations.list(
+ channel="sms",
+ cursor="cursor",
+ limit=100,
+ search="+56912345678",
+ sender_id="senderId",
+ )
+ assert_matches_type(AsyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.conversations.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = await response.parse()
+ assert_matches_type(AsyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None:
+ async with async_client.conversations.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = await response.parse()
+ assert_matches_type(AsyncCursor[ConversationListResponse], conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_messages(self, async_client: AsyncZavudev) -> None:
+ conversation = await async_client.conversations.list_messages(
+ conversation_id="conversationId",
+ )
+ assert_matches_type(AsyncCursor[Message], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_messages_with_all_params(self, async_client: AsyncZavudev) -> None:
+ conversation = await async_client.conversations.list_messages(
+ conversation_id="conversationId",
+ cursor="cursor",
+ limit=100,
+ )
+ assert_matches_type(AsyncCursor[Message], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_messages(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.conversations.with_raw_response.list_messages(
+ conversation_id="conversationId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = await response.parse()
+ assert_matches_type(AsyncCursor[Message], conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_messages(self, async_client: AsyncZavudev) -> None:
+ async with async_client.conversations.with_streaming_response.list_messages(
+ conversation_id="conversationId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = await response.parse()
+ assert_matches_type(AsyncCursor[Message], conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_list_messages(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"):
+ await async_client.conversations.with_raw_response.list_messages(
+ conversation_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_mark_as_read(self, async_client: AsyncZavudev) -> None:
+ conversation = await async_client.conversations.mark_as_read(
+ "conversationId",
+ )
+ assert_matches_type(ConversationMarkAsReadResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_mark_as_read(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.conversations.with_raw_response.mark_as_read(
+ "conversationId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ conversation = await response.parse()
+ assert_matches_type(ConversationMarkAsReadResponse, conversation, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_mark_as_read(self, async_client: AsyncZavudev) -> None:
+ async with async_client.conversations.with_streaming_response.mark_as_read(
+ "conversationId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ conversation = await response.parse()
+ assert_matches_type(ConversationMarkAsReadResponse, conversation, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_mark_as_read(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"):
+ await async_client.conversations.with_raw_response.mark_as_read(
+ "",
+ )
diff --git a/tests/api_resources/test_email_domains.py b/tests/api_resources/test_email_domains.py
new file mode 100644
index 0000000..bbe07fe
--- /dev/null
+++ b/tests/api_resources/test_email_domains.py
@@ -0,0 +1,405 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from zavudev import Zavudev, AsyncZavudev
+from tests.utils import assert_matches_type
+from zavudev.types import (
+ EmailDomainListResponse,
+ EmailDomainCreateResponse,
+ EmailDomainVerifyResponse,
+ EmailDomainRetrieveResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestEmailDomains:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Zavudev) -> None:
+ email_domain = client.email_domains.create(
+ domain="example.com",
+ )
+ assert_matches_type(EmailDomainCreateResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Zavudev) -> None:
+ response = client.email_domains.with_raw_response.create(
+ domain="example.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainCreateResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Zavudev) -> None:
+ with client.email_domains.with_streaming_response.create(
+ domain="example.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainCreateResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Zavudev) -> None:
+ email_domain = client.email_domains.retrieve(
+ "domainId",
+ )
+ assert_matches_type(EmailDomainRetrieveResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Zavudev) -> None:
+ response = client.email_domains.with_raw_response.retrieve(
+ "domainId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainRetrieveResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Zavudev) -> None:
+ with client.email_domains.with_streaming_response.retrieve(
+ "domainId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainRetrieveResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
+ client.email_domains.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Zavudev) -> None:
+ email_domain = client.email_domains.list()
+ assert_matches_type(EmailDomainListResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Zavudev) -> None:
+ response = client.email_domains.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainListResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Zavudev) -> None:
+ with client.email_domains.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainListResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Zavudev) -> None:
+ email_domain = client.email_domains.delete(
+ "domainId",
+ )
+ assert email_domain is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Zavudev) -> None:
+ response = client.email_domains.with_raw_response.delete(
+ "domainId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = response.parse()
+ assert email_domain is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Zavudev) -> None:
+ with client.email_domains.with_streaming_response.delete(
+ "domainId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = response.parse()
+ assert email_domain is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
+ client.email_domains.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_verify(self, client: Zavudev) -> None:
+ email_domain = client.email_domains.verify(
+ "domainId",
+ )
+ assert_matches_type(EmailDomainVerifyResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_verify(self, client: Zavudev) -> None:
+ response = client.email_domains.with_raw_response.verify(
+ "domainId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainVerifyResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_verify(self, client: Zavudev) -> None:
+ with client.email_domains.with_streaming_response.verify(
+ "domainId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = response.parse()
+ assert_matches_type(EmailDomainVerifyResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_verify(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
+ client.email_domains.with_raw_response.verify(
+ "",
+ )
+
+
+class TestAsyncEmailDomains:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncZavudev) -> None:
+ email_domain = await async_client.email_domains.create(
+ domain="example.com",
+ )
+ assert_matches_type(EmailDomainCreateResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.email_domains.with_raw_response.create(
+ domain="example.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainCreateResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncZavudev) -> None:
+ async with async_client.email_domains.with_streaming_response.create(
+ domain="example.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainCreateResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncZavudev) -> None:
+ email_domain = await async_client.email_domains.retrieve(
+ "domainId",
+ )
+ assert_matches_type(EmailDomainRetrieveResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.email_domains.with_raw_response.retrieve(
+ "domainId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainRetrieveResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncZavudev) -> None:
+ async with async_client.email_domains.with_streaming_response.retrieve(
+ "domainId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainRetrieveResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
+ await async_client.email_domains.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncZavudev) -> None:
+ email_domain = await async_client.email_domains.list()
+ assert_matches_type(EmailDomainListResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.email_domains.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainListResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None:
+ async with async_client.email_domains.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainListResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncZavudev) -> None:
+ email_domain = await async_client.email_domains.delete(
+ "domainId",
+ )
+ assert email_domain is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.email_domains.with_raw_response.delete(
+ "domainId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = await response.parse()
+ assert email_domain is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncZavudev) -> None:
+ async with async_client.email_domains.with_streaming_response.delete(
+ "domainId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = await response.parse()
+ assert email_domain is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
+ await async_client.email_domains.with_raw_response.delete(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_verify(self, async_client: AsyncZavudev) -> None:
+ email_domain = await async_client.email_domains.verify(
+ "domainId",
+ )
+ assert_matches_type(EmailDomainVerifyResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_verify(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.email_domains.with_raw_response.verify(
+ "domainId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainVerifyResponse, email_domain, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_verify(self, async_client: AsyncZavudev) -> None:
+ async with async_client.email_domains.with_streaming_response.verify(
+ "domainId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email_domain = await response.parse()
+ assert_matches_type(EmailDomainVerifyResponse, email_domain, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_verify(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
+ await async_client.email_domains.with_raw_response.verify(
+ "",
+ )
diff --git a/tests/api_resources/test_functions.py b/tests/api_resources/test_functions.py
index 162babc..2418e74 100644
--- a/tests/api_resources/test_functions.py
+++ b/tests/api_resources/test_functions.py
@@ -17,6 +17,9 @@
FunctionRetrieveResponse,
FunctionTailLogsResponse,
FunctionGetDeploymentResponse,
+ FunctionListEventTypesResponse,
+ FunctionListDeploymentsResponse,
+ FunctionRollbackDeploymentResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -324,6 +327,131 @@ def test_path_params_get_deployment(self, client: Zavudev) -> None:
"",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_deployments(self, client: Zavudev) -> None:
+ function = client.functions.list_deployments(
+ function_id="functionId",
+ )
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_deployments_with_all_params(self, client: Zavudev) -> None:
+ function = client.functions.list_deployments(
+ function_id="functionId",
+ limit=100,
+ )
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_deployments(self, client: Zavudev) -> None:
+ response = client.functions.with_raw_response.list_deployments(
+ function_id="functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ function = response.parse()
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_deployments(self, client: Zavudev) -> None:
+ with client.functions.with_streaming_response.list_deployments(
+ function_id="functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ function = response.parse()
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_list_deployments(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.with_raw_response.list_deployments(
+ function_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_event_types(self, client: Zavudev) -> None:
+ function = client.functions.list_event_types()
+ assert_matches_type(FunctionListEventTypesResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_event_types(self, client: Zavudev) -> None:
+ response = client.functions.with_raw_response.list_event_types()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ function = response.parse()
+ assert_matches_type(FunctionListEventTypesResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_event_types(self, client: Zavudev) -> None:
+ with client.functions.with_streaming_response.list_event_types() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ function = response.parse()
+ assert_matches_type(FunctionListEventTypesResponse, function, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_rollback_deployment(self, client: Zavudev) -> None:
+ function = client.functions.rollback_deployment(
+ function_id="functionId",
+ deployment_id="fnd_abc123",
+ )
+ assert_matches_type(FunctionRollbackDeploymentResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_rollback_deployment(self, client: Zavudev) -> None:
+ response = client.functions.with_raw_response.rollback_deployment(
+ function_id="functionId",
+ deployment_id="fnd_abc123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ function = response.parse()
+ assert_matches_type(FunctionRollbackDeploymentResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_rollback_deployment(self, client: Zavudev) -> None:
+ with client.functions.with_streaming_response.rollback_deployment(
+ function_id="functionId",
+ deployment_id="fnd_abc123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ function = response.parse()
+ assert_matches_type(FunctionRollbackDeploymentResponse, function, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_rollback_deployment(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ client.functions.with_raw_response.rollback_deployment(
+ function_id="",
+ deployment_id="fnd_abc123",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_tail_logs(self, client: Zavudev) -> None:
@@ -684,6 +812,131 @@ async def test_path_params_get_deployment(self, async_client: AsyncZavudev) -> N
"",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_deployments(self, async_client: AsyncZavudev) -> None:
+ function = await async_client.functions.list_deployments(
+ function_id="functionId",
+ )
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_deployments_with_all_params(self, async_client: AsyncZavudev) -> None:
+ function = await async_client.functions.list_deployments(
+ function_id="functionId",
+ limit=100,
+ )
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_deployments(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.with_raw_response.list_deployments(
+ function_id="functionId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ function = await response.parse()
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_deployments(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.with_streaming_response.list_deployments(
+ function_id="functionId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ function = await response.parse()
+ assert_matches_type(FunctionListDeploymentsResponse, function, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_list_deployments(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.with_raw_response.list_deployments(
+ function_id="",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_event_types(self, async_client: AsyncZavudev) -> None:
+ function = await async_client.functions.list_event_types()
+ assert_matches_type(FunctionListEventTypesResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_event_types(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.with_raw_response.list_event_types()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ function = await response.parse()
+ assert_matches_type(FunctionListEventTypesResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_event_types(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.with_streaming_response.list_event_types() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ function = await response.parse()
+ assert_matches_type(FunctionListEventTypesResponse, function, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_rollback_deployment(self, async_client: AsyncZavudev) -> None:
+ function = await async_client.functions.rollback_deployment(
+ function_id="functionId",
+ deployment_id="fnd_abc123",
+ )
+ assert_matches_type(FunctionRollbackDeploymentResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_rollback_deployment(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.functions.with_raw_response.rollback_deployment(
+ function_id="functionId",
+ deployment_id="fnd_abc123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ function = await response.parse()
+ assert_matches_type(FunctionRollbackDeploymentResponse, function, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_rollback_deployment(self, async_client: AsyncZavudev) -> None:
+ async with async_client.functions.with_streaming_response.rollback_deployment(
+ function_id="functionId",
+ deployment_id="fnd_abc123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ function = await response.parse()
+ assert_matches_type(FunctionRollbackDeploymentResponse, function, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_rollback_deployment(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `function_id` but received ''"):
+ await async_client.functions.with_raw_response.rollback_deployment(
+ function_id="",
+ deployment_id="fnd_abc123",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_tail_logs(self, async_client: AsyncZavudev) -> None:
diff --git a/tests/api_resources/test_introspect.py b/tests/api_resources/test_introspect.py
index 5e2275a..531fcaf 100644
--- a/tests/api_resources/test_introspect.py
+++ b/tests/api_resources/test_introspect.py
@@ -9,7 +9,10 @@
from zavudev import Zavudev, AsyncZavudev
from tests.utils import assert_matches_type
-from zavudev.types import IntrospectValidatePhoneResponse
+from zavudev.types import (
+ IntrospectValidateEmailResponse,
+ IntrospectValidatePhoneResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -17,6 +20,43 @@
class TestIntrospect:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_validate_email(self, client: Zavudev) -> None:
+ introspect = client.introspect.validate_email()
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_validate_email_with_all_params(self, client: Zavudev) -> None:
+ introspect = client.introspect.validate_email(
+ email="maria@example.com",
+ emails=["maria@example.com", "info@deaddomain.example"],
+ )
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_validate_email(self, client: Zavudev) -> None:
+ response = client.introspect.with_raw_response.validate_email()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ introspect = response.parse()
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_validate_email(self, client: Zavudev) -> None:
+ with client.introspect.with_streaming_response.validate_email() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ introspect = response.parse()
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_validate_phone(self, client: Zavudev) -> None:
@@ -57,6 +97,43 @@ class TestAsyncIntrospect:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_validate_email(self, async_client: AsyncZavudev) -> None:
+ introspect = await async_client.introspect.validate_email()
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_validate_email_with_all_params(self, async_client: AsyncZavudev) -> None:
+ introspect = await async_client.introspect.validate_email(
+ email="maria@example.com",
+ emails=["maria@example.com", "info@deaddomain.example"],
+ )
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_validate_email(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.introspect.with_raw_response.validate_email()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ introspect = await response.parse()
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_validate_email(self, async_client: AsyncZavudev) -> None:
+ async with async_client.introspect.with_streaming_response.validate_email() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ introspect = await response.parse()
+ assert_matches_type(IntrospectValidateEmailResponse, introspect, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_validate_phone(self, async_client: AsyncZavudev) -> None:
diff --git a/tests/api_resources/test_messages.py b/tests/api_resources/test_messages.py
index 5286b19..3a01e57 100644
--- a/tests/api_resources/test_messages.py
+++ b/tests/api_resources/test_messages.py
@@ -13,6 +13,7 @@
Message,
MessageResponse,
MessageShowTypingResponse,
+ MessageListAttachmentsResponse,
)
from zavudev.pagination import SyncCursor, AsyncCursor
@@ -104,6 +105,48 @@ def test_streaming_response_list(self, client: Zavudev) -> None:
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_attachments(self, client: Zavudev) -> None:
+ message = client.messages.list_attachments(
+ "messageId",
+ )
+ assert_matches_type(MessageListAttachmentsResponse, message, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_attachments(self, client: Zavudev) -> None:
+ response = client.messages.with_raw_response.list_attachments(
+ "messageId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ message = response.parse()
+ assert_matches_type(MessageListAttachmentsResponse, message, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_attachments(self, client: Zavudev) -> None:
+ with client.messages.with_streaming_response.list_attachments(
+ "messageId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ message = response.parse()
+ assert_matches_type(MessageListAttachmentsResponse, message, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_list_attachments(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"):
+ client.messages.with_raw_response.list_attachments(
+ "",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_react(self, client: Zavudev) -> None:
@@ -213,6 +256,18 @@ def test_method_send_with_all_params(self, client: Zavudev) -> None:
"media_url": "https://example.com/image.jpg",
"mime_type": "image/jpeg",
"react_to_message_id": "reactToMessageId",
+ "referral": {
+ "body": "body",
+ "ctwa_clid": "ARIzZm9vYmFyY3R3YWNsaWQ",
+ "headline": "headline",
+ "image_url": "https://example.com",
+ "media_type": "image",
+ "source_id": "120210000000000000",
+ "source_type": "ad",
+ "source_url": "https://example.com",
+ "thumbnail_url": "https://example.com",
+ "video_url": "https://example.com",
+ },
"reply_to_from": "replyToFrom",
"reply_to_message_id": "replyToMessageId",
"reply_to_message_type": "replyToMessageType",
@@ -416,6 +471,48 @@ async def test_streaming_response_list(self, async_client: AsyncZavudev) -> None
assert cast(Any, response.is_closed) is True
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_attachments(self, async_client: AsyncZavudev) -> None:
+ message = await async_client.messages.list_attachments(
+ "messageId",
+ )
+ assert_matches_type(MessageListAttachmentsResponse, message, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_attachments(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.messages.with_raw_response.list_attachments(
+ "messageId",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ message = await response.parse()
+ assert_matches_type(MessageListAttachmentsResponse, message, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_attachments(self, async_client: AsyncZavudev) -> None:
+ async with async_client.messages.with_streaming_response.list_attachments(
+ "messageId",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ message = await response.parse()
+ assert_matches_type(MessageListAttachmentsResponse, message, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_list_attachments(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"):
+ await async_client.messages.with_raw_response.list_attachments(
+ "",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_react(self, async_client: AsyncZavudev) -> None:
@@ -525,6 +622,18 @@ async def test_method_send_with_all_params(self, async_client: AsyncZavudev) ->
"media_url": "https://example.com/image.jpg",
"mime_type": "image/jpeg",
"react_to_message_id": "reactToMessageId",
+ "referral": {
+ "body": "body",
+ "ctwa_clid": "ARIzZm9vYmFyY3R3YWNsaWQ",
+ "headline": "headline",
+ "image_url": "https://example.com",
+ "media_type": "image",
+ "source_id": "120210000000000000",
+ "source_type": "ad",
+ "source_url": "https://example.com",
+ "thumbnail_url": "https://example.com",
+ "video_url": "https://example.com",
+ },
"reply_to_from": "replyToFrom",
"reply_to_message_id": "replyToMessageId",
"reply_to_message_type": "replyToMessageType",
diff --git a/tests/api_resources/test_templates.py b/tests/api_resources/test_templates.py
index 08b20cc..ad12187 100644
--- a/tests/api_resources/test_templates.py
+++ b/tests/api_resources/test_templates.py
@@ -11,6 +11,7 @@
from tests.utils import assert_matches_type
from zavudev.types import (
Template,
+ TemplateSyncResponse,
)
from zavudev.pagination import SyncCursor, AsyncCursor
@@ -269,6 +270,42 @@ def test_path_params_submit(self, client: Zavudev) -> None:
sender_id="sender_abc123",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_sync(self, client: Zavudev) -> None:
+ template = client.templates.sync()
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_sync_with_all_params(self, client: Zavudev) -> None:
+ template = client.templates.sync(
+ sender_id="sender_12345",
+ )
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_sync(self, client: Zavudev) -> None:
+ response = client.templates.with_raw_response.sync()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = response.parse()
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_sync(self, client: Zavudev) -> None:
+ with client.templates.with_streaming_response.sync() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = response.parse()
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
class TestAsyncTemplates:
parametrize = pytest.mark.parametrize(
@@ -523,3 +560,39 @@ async def test_path_params_submit(self, async_client: AsyncZavudev) -> None:
template_id="",
sender_id="sender_abc123",
)
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_sync(self, async_client: AsyncZavudev) -> None:
+ template = await async_client.templates.sync()
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_sync_with_all_params(self, async_client: AsyncZavudev) -> None:
+ template = await async_client.templates.sync(
+ sender_id="sender_12345",
+ )
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_sync(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.templates.with_raw_response.sync()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ template = await response.parse()
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_sync(self, async_client: AsyncZavudev) -> None:
+ async with async_client.templates.with_streaming_response.sync() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ template = await response.parse()
+ assert_matches_type(TemplateSyncResponse, template, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_urls.py b/tests/api_resources/test_urls.py
index 01bc745..6a5ac2b 100644
--- a/tests/api_resources/test_urls.py
+++ b/tests/api_resources/test_urls.py
@@ -11,6 +11,7 @@
from tests.utils import assert_matches_type
from zavudev.types import (
VerifiedURL,
+ URLEscalateResponse,
URLRetrieveDetailsResponse,
URLSubmitForVerificationResponse,
)
@@ -22,6 +23,52 @@
class TestURLs:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_escalate(self, client: Zavudev) -> None:
+ url = client.urls.escalate(
+ url_id="urlId",
+ reason="This is our official landing page and was rejected in error.",
+ )
+ assert_matches_type(URLEscalateResponse, url, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_escalate(self, client: Zavudev) -> None:
+ response = client.urls.with_raw_response.escalate(
+ url_id="urlId",
+ reason="This is our official landing page and was rejected in error.",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ url = response.parse()
+ assert_matches_type(URLEscalateResponse, url, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_escalate(self, client: Zavudev) -> None:
+ with client.urls.with_streaming_response.escalate(
+ url_id="urlId",
+ reason="This is our official landing page and was rejected in error.",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ url = response.parse()
+ assert_matches_type(URLEscalateResponse, url, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_path_params_escalate(self, client: Zavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `url_id` but received ''"):
+ client.urls.with_raw_response.escalate(
+ url_id="",
+ reason="This is our official landing page and was rejected in error.",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_list_verified(self, client: Zavudev) -> None:
@@ -142,6 +189,52 @@ class TestAsyncURLs:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_escalate(self, async_client: AsyncZavudev) -> None:
+ url = await async_client.urls.escalate(
+ url_id="urlId",
+ reason="This is our official landing page and was rejected in error.",
+ )
+ assert_matches_type(URLEscalateResponse, url, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_escalate(self, async_client: AsyncZavudev) -> None:
+ response = await async_client.urls.with_raw_response.escalate(
+ url_id="urlId",
+ reason="This is our official landing page and was rejected in error.",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ url = await response.parse()
+ assert_matches_type(URLEscalateResponse, url, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_escalate(self, async_client: AsyncZavudev) -> None:
+ async with async_client.urls.with_streaming_response.escalate(
+ url_id="urlId",
+ reason="This is our official landing page and was rejected in error.",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ url = await response.parse()
+ assert_matches_type(URLEscalateResponse, url, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_path_params_escalate(self, async_client: AsyncZavudev) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `url_id` but received ''"):
+ await async_client.urls.with_raw_response.escalate(
+ url_id="",
+ reason="This is our official landing page and was rejected in error.",
+ )
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_list_verified(self, async_client: AsyncZavudev) -> None: