diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6db19b9..4ad3fef 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.17.0" + ".": "0.18.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 576e5df..f1de74b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 40 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-628db0b9b7c9da594fa6ad6ce9d95f4ecad92c9e0313f2f1f9977216494dbc5d.yml -openapi_spec_hash: 1773341fbff31b84d2cbcdb37eaad877 -config_hash: b090c2bdd7a719c56c825edddc587737 +configured_endpoints: 58 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-ee4b9d190e3aaa146b08bc0ffed1c802dc353c3fdc37fc0097f2350ab3714b70.yml +openapi_spec_hash: 0dad8b2e562ba7ce879425ab92169d85 +config_hash: b70b11b10fc614f91f1c6f028b40780f diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a605d7..b1e8e50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.18.0 (2026-02-05) + +Full Changelog: [v0.17.0...v0.18.0](https://github.com/ArkHQ-io/ark-python/compare/v0.17.0...v0.18.0) + +### Features + +* **api:** add Credentials endpoint ([0ff55ca](https://github.com/ArkHQ-io/ark-python/commit/0ff55caabeb38ad0046cf489c28f8dad3caddfc2)) +* **api:** add Platform webhooks ([f0a53a7](https://github.com/ArkHQ-io/ark-python/commit/f0a53a79606bece5fd5cf9b2dad7a580b8bae94e)) +* **api:** endpoint updates ([0e54a04](https://github.com/ArkHQ-io/ark-python/commit/0e54a042195b1134b7c5cba9ee2ca4b98f35b361)) +* **api:** standardization improvements ([b52928d](https://github.com/ArkHQ-io/ark-python/commit/b52928d6147770b7c3a68001dad6d3861ff43967)) +* **api:** tenant usage ([09a4d02](https://github.com/ArkHQ-io/ark-python/commit/09a4d02d5acdb3b2a20ca62930e7c644bc5968c9)) + ## 0.17.0 (2026-02-03) Full Changelog: [v0.16.0...v0.17.0](https://github.com/ArkHQ-io/ark-python/compare/v0.16.0...v0.17.0) diff --git a/README.md b/README.md index bcf106d..b51d0b6 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ response = client.emails.send( to=["user@example.com"], html="
client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse
+- client.emails.retrieve(email_id, \*\*params) -> EmailRetrieveResponse
- client.emails.list(\*\*params) -> SyncPageNumberPagination[EmailListResponse]
-- client.emails.retrieve_deliveries(id) -> EmailRetrieveDeliveriesResponse
-- client.emails.retry(id) -> EmailRetryResponse
+- client.emails.retrieve_deliveries(email_id) -> EmailRetrieveDeliveriesResponse
+- client.emails.retry(email_id) -> EmailRetryResponse
- client.emails.send(\*\*params) -> EmailSendResponse
- client.emails.send_batch(\*\*params) -> EmailSendBatchResponse
- client.emails.send_raw(\*\*params) -> EmailSendRawResponse
-# Domains
+# Logs
+
+Types:
+
+```python
+from ark.types import LogEntry, LogEntryDetail, LogRetrieveResponse
+```
+
+Methods:
+
+- client.logs.retrieve(request_id) -> LogRetrieveResponse
+- client.logs.list(\*\*params) -> SyncPageNumberPagination[LogEntry]
+
+# Usage
Types:
```python
from ark.types import (
+ EmailCounts,
+ EmailRates,
+ OrgUsageSummary,
+ TenantUsageItem,
+ UsagePeriod,
+ UsageExportResponse,
+)
+```
+
+Methods:
+
+- client.usage.retrieve(\*\*params) -> OrgUsageSummary
+- client.usage.export(\*\*params) -> UsageExportResponse
+- client.usage.list_tenants(\*\*params) -> SyncPageNumberPagination[TenantUsageItem]
+
+# Limits
+
+Types:
+
+```python
+from ark.types import LimitsData, LimitRetrieveResponse
+```
+
+Methods:
+
+- client.limits.retrieve() -> LimitRetrieveResponse
+
+# Tenants
+
+Types:
+
+```python
+from ark.types import (
+ Tenant,
+ TenantCreateResponse,
+ TenantRetrieveResponse,
+ TenantUpdateResponse,
+ TenantDeleteResponse,
+)
+```
+
+Methods:
+
+- client.tenants.create(\*\*params) -> TenantCreateResponse
+- client.tenants.retrieve(tenant_id) -> TenantRetrieveResponse
+- client.tenants.update(tenant_id, \*\*params) -> TenantUpdateResponse
+- client.tenants.list(\*\*params) -> SyncPageNumberPagination[Tenant]
+- client.tenants.delete(tenant_id) -> TenantDeleteResponse
+
+## Credentials
+
+Types:
+
+```python
+from ark.types.tenants import (
+ CredentialCreateResponse,
+ CredentialRetrieveResponse,
+ CredentialUpdateResponse,
+ CredentialListResponse,
+ CredentialDeleteResponse,
+)
+```
+
+Methods:
+
+- client.tenants.credentials.create(tenant_id, \*\*params) -> CredentialCreateResponse
+- client.tenants.credentials.retrieve(credential_id, \*, tenant_id, \*\*params) -> CredentialRetrieveResponse
+- client.tenants.credentials.update(credential_id, \*, tenant_id, \*\*params) -> CredentialUpdateResponse
+- client.tenants.credentials.list(tenant_id, \*\*params) -> SyncPageNumberPagination[CredentialListResponse]
+- client.tenants.credentials.delete(credential_id, \*, tenant_id) -> CredentialDeleteResponse
+
+## Domains
+
+Types:
+
+```python
+from ark.types.tenants import (
DNSRecord,
DomainCreateResponse,
DomainRetrieveResponse,
@@ -47,40 +137,38 @@ from ark.types import (
Methods:
-- client.domains.create(\*\*params) -> DomainCreateResponse
-- client.domains.retrieve(domain_id) -> DomainRetrieveResponse
-- client.domains.list() -> DomainListResponse
-- client.domains.delete(domain_id) -> DomainDeleteResponse
-- client.domains.verify(domain_id) -> DomainVerifyResponse
+- client.tenants.domains.create(tenant_id, \*\*params) -> DomainCreateResponse
+- client.tenants.domains.retrieve(domain_id, \*, tenant_id) -> DomainRetrieveResponse
+- client.tenants.domains.list(tenant_id) -> DomainListResponse
+- client.tenants.domains.delete(domain_id, \*, tenant_id) -> DomainDeleteResponse
+- client.tenants.domains.verify(domain_id, \*, tenant_id) -> DomainVerifyResponse
-# Suppressions
+## Suppressions
Types:
```python
-from ark.types import (
+from ark.types.tenants import (
SuppressionCreateResponse,
SuppressionRetrieveResponse,
SuppressionListResponse,
SuppressionDeleteResponse,
- SuppressionBulkCreateResponse,
)
```
Methods:
-- client.suppressions.create(\*\*params) -> SuppressionCreateResponse
-- client.suppressions.retrieve(email) -> SuppressionRetrieveResponse
-- client.suppressions.list(\*\*params) -> SyncPageNumberPagination[SuppressionListResponse]
-- client.suppressions.delete(email) -> SuppressionDeleteResponse
-- client.suppressions.bulk_create(\*\*params) -> SuppressionBulkCreateResponse
+- client.tenants.suppressions.create(tenant_id, \*\*params) -> SuppressionCreateResponse
+- client.tenants.suppressions.retrieve(email, \*, tenant_id) -> SuppressionRetrieveResponse
+- client.tenants.suppressions.list(tenant_id, \*\*params) -> SyncPageNumberPagination[SuppressionListResponse]
+- client.tenants.suppressions.delete(email, \*, tenant_id) -> SuppressionDeleteResponse
-# Webhooks
+## Webhooks
Types:
```python
-from ark.types import (
+from ark.types.tenants import (
WebhookCreateResponse,
WebhookRetrieveResponse,
WebhookUpdateResponse,
@@ -95,22 +183,22 @@ from ark.types import (
Methods:
-- client.webhooks.create(\*\*params) -> WebhookCreateResponse
-- client.webhooks.retrieve(webhook_id) -> WebhookRetrieveResponse
-- client.webhooks.update(webhook_id, \*\*params) -> WebhookUpdateResponse
-- client.webhooks.list() -> WebhookListResponse
-- client.webhooks.delete(webhook_id) -> WebhookDeleteResponse
-- client.webhooks.list_deliveries(webhook_id, \*\*params) -> WebhookListDeliveriesResponse
-- client.webhooks.replay_delivery(delivery_id, \*, webhook_id) -> WebhookReplayDeliveryResponse
-- client.webhooks.retrieve_delivery(delivery_id, \*, webhook_id) -> WebhookRetrieveDeliveryResponse
-- client.webhooks.test(webhook_id, \*\*params) -> WebhookTestResponse
+- client.tenants.webhooks.create(tenant_id, \*\*params) -> WebhookCreateResponse
+- client.tenants.webhooks.retrieve(webhook_id, \*, tenant_id) -> WebhookRetrieveResponse
+- client.tenants.webhooks.update(webhook_id, \*, tenant_id, \*\*params) -> WebhookUpdateResponse
+- client.tenants.webhooks.list(tenant_id) -> WebhookListResponse
+- client.tenants.webhooks.delete(webhook_id, \*, tenant_id) -> WebhookDeleteResponse
+- client.tenants.webhooks.list_deliveries(webhook_id, \*, tenant_id, \*\*params) -> WebhookListDeliveriesResponse
+- client.tenants.webhooks.replay_delivery(delivery_id, \*, tenant_id, webhook_id) -> WebhookReplayDeliveryResponse
+- client.tenants.webhooks.retrieve_delivery(delivery_id, \*, tenant_id, webhook_id) -> WebhookRetrieveDeliveryResponse
+- client.tenants.webhooks.test(webhook_id, \*, tenant_id, \*\*params) -> WebhookTestResponse
-# Tracking
+## Tracking
Types:
```python
-from ark.types import (
+from ark.types.tenants import (
TrackDomain,
TrackingCreateResponse,
TrackingRetrieveResponse,
@@ -123,56 +211,59 @@ from ark.types import (
Methods:
-- client.tracking.create(\*\*params) -> TrackingCreateResponse
-- client.tracking.retrieve(tracking_id) -> TrackingRetrieveResponse
-- client.tracking.update(tracking_id, \*\*params) -> TrackingUpdateResponse
-- client.tracking.list() -> TrackingListResponse
-- client.tracking.delete(tracking_id) -> TrackingDeleteResponse
-- client.tracking.verify(tracking_id) -> TrackingVerifyResponse
+- client.tenants.tracking.create(tenant_id, \*\*params) -> TrackingCreateResponse
+- client.tenants.tracking.retrieve(tracking_id, \*, tenant_id) -> TrackingRetrieveResponse
+- client.tenants.tracking.update(tracking_id, \*, tenant_id, \*\*params) -> TrackingUpdateResponse
+- client.tenants.tracking.list(tenant_id) -> TrackingListResponse
+- client.tenants.tracking.delete(tracking_id, \*, tenant_id) -> TrackingDeleteResponse
+- client.tenants.tracking.verify(tracking_id, \*, tenant_id) -> TrackingVerifyResponse
-# Logs
+## Usage
Types:
```python
-from ark.types import LogEntry, LogEntryDetail, LogRetrieveResponse
+from ark.types.tenants import (
+ TenantUsage,
+ TenantUsageTimeseries,
+ UsageRetrieveResponse,
+ UsageRetrieveTimeseriesResponse,
+)
```
Methods:
-- client.logs.retrieve(request_id) -> LogRetrieveResponse
-- client.logs.list(\*\*params) -> SyncPageNumberPagination[LogEntry]
-
-# Usage
-
-Types:
-
-```python
-from ark.types import UsageRetrieveResponse
-```
-
-Methods:
+- client.tenants.usage.retrieve(tenant_id, \*\*params) -> UsageRetrieveResponse
+- client.tenants.usage.retrieve_timeseries(tenant_id, \*\*params) -> UsageRetrieveTimeseriesResponse
-- client.usage.retrieve() -> UsageRetrieveResponse
+# Platform
-# Tenants
+## Webhooks
Types:
```python
-from ark.types import (
- Tenant,
- TenantCreateResponse,
- TenantRetrieveResponse,
- TenantUpdateResponse,
- TenantDeleteResponse,
+from ark.types.platform import (
+ WebhookCreateResponse,
+ WebhookRetrieveResponse,
+ WebhookUpdateResponse,
+ WebhookListResponse,
+ WebhookDeleteResponse,
+ WebhookListDeliveriesResponse,
+ WebhookReplayDeliveryResponse,
+ WebhookRetrieveDeliveryResponse,
+ WebhookTestResponse,
)
```
Methods:
-- client.tenants.create(\*\*params) -> TenantCreateResponse
-- client.tenants.retrieve(tenant_id) -> TenantRetrieveResponse
-- client.tenants.update(tenant_id, \*\*params) -> TenantUpdateResponse
-- client.tenants.list(\*\*params) -> SyncPageNumberPagination[Tenant]
-- client.tenants.delete(tenant_id) -> TenantDeleteResponse
+- client.platform.webhooks.create(\*\*params) -> WebhookCreateResponse
+- client.platform.webhooks.retrieve(webhook_id) -> WebhookRetrieveResponse
+- client.platform.webhooks.update(webhook_id, \*\*params) -> WebhookUpdateResponse
+- client.platform.webhooks.list() -> WebhookListResponse
+- client.platform.webhooks.delete(webhook_id) -> WebhookDeleteResponse
+- client.platform.webhooks.list_deliveries(\*\*params) -> SyncPageNumberPagination[WebhookListDeliveriesResponse]
+- client.platform.webhooks.replay_delivery(delivery_id) -> WebhookReplayDeliveryResponse
+- client.platform.webhooks.retrieve_delivery(delivery_id) -> WebhookRetrieveDeliveryResponse
+- client.platform.webhooks.test(webhook_id, \*\*params) -> WebhookTestResponse
diff --git a/pyproject.toml b/pyproject.toml
index 1fc344a..8c5da08 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "ark-email"
-version = "0.17.0"
+version = "0.18.0"
description = "The official Python library for the ark API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/ark/_client.py b/src/ark/_client.py
index 51fb70d..40de787 100644
--- a/src/ark/_client.py
+++ b/src/ark/_client.py
@@ -31,15 +31,13 @@
)
if TYPE_CHECKING:
- from .resources import logs, usage, emails, domains, tenants, tracking, webhooks, suppressions
+ from .resources import logs, usage, emails, limits, tenants, platform
from .resources.logs import LogsResource, AsyncLogsResource
from .resources.usage import UsageResource, AsyncUsageResource
from .resources.emails import EmailsResource, AsyncEmailsResource
- from .resources.domains import DomainsResource, AsyncDomainsResource
- from .resources.tenants import TenantsResource, AsyncTenantsResource
- from .resources.tracking import TrackingResource, AsyncTrackingResource
- from .resources.webhooks import WebhooksResource, AsyncWebhooksResource
- from .resources.suppressions import SuppressionsResource, AsyncSuppressionsResource
+ from .resources.limits import LimitsResource, AsyncLimitsResource
+ from .resources.tenants.tenants import TenantsResource, AsyncTenantsResource
+ from .resources.platform.platform import PlatformResource, AsyncPlatformResource
__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Ark", "AsyncArk", "Client", "AsyncClient"]
@@ -105,30 +103,6 @@ def emails(self) -> EmailsResource:
return EmailsResource(self)
- @cached_property
- def domains(self) -> DomainsResource:
- from .resources.domains import DomainsResource
-
- return DomainsResource(self)
-
- @cached_property
- def suppressions(self) -> SuppressionsResource:
- from .resources.suppressions import SuppressionsResource
-
- return SuppressionsResource(self)
-
- @cached_property
- def webhooks(self) -> WebhooksResource:
- from .resources.webhooks import WebhooksResource
-
- return WebhooksResource(self)
-
- @cached_property
- def tracking(self) -> TrackingResource:
- from .resources.tracking import TrackingResource
-
- return TrackingResource(self)
-
@cached_property
def logs(self) -> LogsResource:
from .resources.logs import LogsResource
@@ -141,12 +115,24 @@ def usage(self) -> UsageResource:
return UsageResource(self)
+ @cached_property
+ def limits(self) -> LimitsResource:
+ from .resources.limits import LimitsResource
+
+ return LimitsResource(self)
+
@cached_property
def tenants(self) -> TenantsResource:
from .resources.tenants import TenantsResource
return TenantsResource(self)
+ @cached_property
+ def platform(self) -> PlatformResource:
+ from .resources.platform import PlatformResource
+
+ return PlatformResource(self)
+
@cached_property
def with_raw_response(self) -> ArkWithRawResponse:
return ArkWithRawResponse(self)
@@ -321,30 +307,6 @@ def emails(self) -> AsyncEmailsResource:
return AsyncEmailsResource(self)
- @cached_property
- def domains(self) -> AsyncDomainsResource:
- from .resources.domains import AsyncDomainsResource
-
- return AsyncDomainsResource(self)
-
- @cached_property
- def suppressions(self) -> AsyncSuppressionsResource:
- from .resources.suppressions import AsyncSuppressionsResource
-
- return AsyncSuppressionsResource(self)
-
- @cached_property
- def webhooks(self) -> AsyncWebhooksResource:
- from .resources.webhooks import AsyncWebhooksResource
-
- return AsyncWebhooksResource(self)
-
- @cached_property
- def tracking(self) -> AsyncTrackingResource:
- from .resources.tracking import AsyncTrackingResource
-
- return AsyncTrackingResource(self)
-
@cached_property
def logs(self) -> AsyncLogsResource:
from .resources.logs import AsyncLogsResource
@@ -357,12 +319,24 @@ def usage(self) -> AsyncUsageResource:
return AsyncUsageResource(self)
+ @cached_property
+ def limits(self) -> AsyncLimitsResource:
+ from .resources.limits import AsyncLimitsResource
+
+ return AsyncLimitsResource(self)
+
@cached_property
def tenants(self) -> AsyncTenantsResource:
from .resources.tenants import AsyncTenantsResource
return AsyncTenantsResource(self)
+ @cached_property
+ def platform(self) -> AsyncPlatformResource:
+ from .resources.platform import AsyncPlatformResource
+
+ return AsyncPlatformResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncArkWithRawResponse:
return AsyncArkWithRawResponse(self)
@@ -488,30 +462,6 @@ def emails(self) -> emails.EmailsResourceWithRawResponse:
return EmailsResourceWithRawResponse(self._client.emails)
- @cached_property
- def domains(self) -> domains.DomainsResourceWithRawResponse:
- from .resources.domains import DomainsResourceWithRawResponse
-
- return DomainsResourceWithRawResponse(self._client.domains)
-
- @cached_property
- def suppressions(self) -> suppressions.SuppressionsResourceWithRawResponse:
- from .resources.suppressions import SuppressionsResourceWithRawResponse
-
- return SuppressionsResourceWithRawResponse(self._client.suppressions)
-
- @cached_property
- def webhooks(self) -> webhooks.WebhooksResourceWithRawResponse:
- from .resources.webhooks import WebhooksResourceWithRawResponse
-
- return WebhooksResourceWithRawResponse(self._client.webhooks)
-
- @cached_property
- def tracking(self) -> tracking.TrackingResourceWithRawResponse:
- from .resources.tracking import TrackingResourceWithRawResponse
-
- return TrackingResourceWithRawResponse(self._client.tracking)
-
@cached_property
def logs(self) -> logs.LogsResourceWithRawResponse:
from .resources.logs import LogsResourceWithRawResponse
@@ -524,12 +474,24 @@ def usage(self) -> usage.UsageResourceWithRawResponse:
return UsageResourceWithRawResponse(self._client.usage)
+ @cached_property
+ def limits(self) -> limits.LimitsResourceWithRawResponse:
+ from .resources.limits import LimitsResourceWithRawResponse
+
+ return LimitsResourceWithRawResponse(self._client.limits)
+
@cached_property
def tenants(self) -> tenants.TenantsResourceWithRawResponse:
from .resources.tenants import TenantsResourceWithRawResponse
return TenantsResourceWithRawResponse(self._client.tenants)
+ @cached_property
+ def platform(self) -> platform.PlatformResourceWithRawResponse:
+ from .resources.platform import PlatformResourceWithRawResponse
+
+ return PlatformResourceWithRawResponse(self._client.platform)
+
class AsyncArkWithRawResponse:
_client: AsyncArk
@@ -543,30 +505,6 @@ def emails(self) -> emails.AsyncEmailsResourceWithRawResponse:
return AsyncEmailsResourceWithRawResponse(self._client.emails)
- @cached_property
- def domains(self) -> domains.AsyncDomainsResourceWithRawResponse:
- from .resources.domains import AsyncDomainsResourceWithRawResponse
-
- return AsyncDomainsResourceWithRawResponse(self._client.domains)
-
- @cached_property
- def suppressions(self) -> suppressions.AsyncSuppressionsResourceWithRawResponse:
- from .resources.suppressions import AsyncSuppressionsResourceWithRawResponse
-
- return AsyncSuppressionsResourceWithRawResponse(self._client.suppressions)
-
- @cached_property
- def webhooks(self) -> webhooks.AsyncWebhooksResourceWithRawResponse:
- from .resources.webhooks import AsyncWebhooksResourceWithRawResponse
-
- return AsyncWebhooksResourceWithRawResponse(self._client.webhooks)
-
- @cached_property
- def tracking(self) -> tracking.AsyncTrackingResourceWithRawResponse:
- from .resources.tracking import AsyncTrackingResourceWithRawResponse
-
- return AsyncTrackingResourceWithRawResponse(self._client.tracking)
-
@cached_property
def logs(self) -> logs.AsyncLogsResourceWithRawResponse:
from .resources.logs import AsyncLogsResourceWithRawResponse
@@ -579,12 +517,24 @@ def usage(self) -> usage.AsyncUsageResourceWithRawResponse:
return AsyncUsageResourceWithRawResponse(self._client.usage)
+ @cached_property
+ def limits(self) -> limits.AsyncLimitsResourceWithRawResponse:
+ from .resources.limits import AsyncLimitsResourceWithRawResponse
+
+ return AsyncLimitsResourceWithRawResponse(self._client.limits)
+
@cached_property
def tenants(self) -> tenants.AsyncTenantsResourceWithRawResponse:
from .resources.tenants import AsyncTenantsResourceWithRawResponse
return AsyncTenantsResourceWithRawResponse(self._client.tenants)
+ @cached_property
+ def platform(self) -> platform.AsyncPlatformResourceWithRawResponse:
+ from .resources.platform import AsyncPlatformResourceWithRawResponse
+
+ return AsyncPlatformResourceWithRawResponse(self._client.platform)
+
class ArkWithStreamedResponse:
_client: Ark
@@ -598,30 +548,6 @@ def emails(self) -> emails.EmailsResourceWithStreamingResponse:
return EmailsResourceWithStreamingResponse(self._client.emails)
- @cached_property
- def domains(self) -> domains.DomainsResourceWithStreamingResponse:
- from .resources.domains import DomainsResourceWithStreamingResponse
-
- return DomainsResourceWithStreamingResponse(self._client.domains)
-
- @cached_property
- def suppressions(self) -> suppressions.SuppressionsResourceWithStreamingResponse:
- from .resources.suppressions import SuppressionsResourceWithStreamingResponse
-
- return SuppressionsResourceWithStreamingResponse(self._client.suppressions)
-
- @cached_property
- def webhooks(self) -> webhooks.WebhooksResourceWithStreamingResponse:
- from .resources.webhooks import WebhooksResourceWithStreamingResponse
-
- return WebhooksResourceWithStreamingResponse(self._client.webhooks)
-
- @cached_property
- def tracking(self) -> tracking.TrackingResourceWithStreamingResponse:
- from .resources.tracking import TrackingResourceWithStreamingResponse
-
- return TrackingResourceWithStreamingResponse(self._client.tracking)
-
@cached_property
def logs(self) -> logs.LogsResourceWithStreamingResponse:
from .resources.logs import LogsResourceWithStreamingResponse
@@ -634,12 +560,24 @@ def usage(self) -> usage.UsageResourceWithStreamingResponse:
return UsageResourceWithStreamingResponse(self._client.usage)
+ @cached_property
+ def limits(self) -> limits.LimitsResourceWithStreamingResponse:
+ from .resources.limits import LimitsResourceWithStreamingResponse
+
+ return LimitsResourceWithStreamingResponse(self._client.limits)
+
@cached_property
def tenants(self) -> tenants.TenantsResourceWithStreamingResponse:
from .resources.tenants import TenantsResourceWithStreamingResponse
return TenantsResourceWithStreamingResponse(self._client.tenants)
+ @cached_property
+ def platform(self) -> platform.PlatformResourceWithStreamingResponse:
+ from .resources.platform import PlatformResourceWithStreamingResponse
+
+ return PlatformResourceWithStreamingResponse(self._client.platform)
+
class AsyncArkWithStreamedResponse:
_client: AsyncArk
@@ -653,30 +591,6 @@ def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse:
return AsyncEmailsResourceWithStreamingResponse(self._client.emails)
- @cached_property
- def domains(self) -> domains.AsyncDomainsResourceWithStreamingResponse:
- from .resources.domains import AsyncDomainsResourceWithStreamingResponse
-
- return AsyncDomainsResourceWithStreamingResponse(self._client.domains)
-
- @cached_property
- def suppressions(self) -> suppressions.AsyncSuppressionsResourceWithStreamingResponse:
- from .resources.suppressions import AsyncSuppressionsResourceWithStreamingResponse
-
- return AsyncSuppressionsResourceWithStreamingResponse(self._client.suppressions)
-
- @cached_property
- def webhooks(self) -> webhooks.AsyncWebhooksResourceWithStreamingResponse:
- from .resources.webhooks import AsyncWebhooksResourceWithStreamingResponse
-
- return AsyncWebhooksResourceWithStreamingResponse(self._client.webhooks)
-
- @cached_property
- def tracking(self) -> tracking.AsyncTrackingResourceWithStreamingResponse:
- from .resources.tracking import AsyncTrackingResourceWithStreamingResponse
-
- return AsyncTrackingResourceWithStreamingResponse(self._client.tracking)
-
@cached_property
def logs(self) -> logs.AsyncLogsResourceWithStreamingResponse:
from .resources.logs import AsyncLogsResourceWithStreamingResponse
@@ -689,12 +603,24 @@ def usage(self) -> usage.AsyncUsageResourceWithStreamingResponse:
return AsyncUsageResourceWithStreamingResponse(self._client.usage)
+ @cached_property
+ def limits(self) -> limits.AsyncLimitsResourceWithStreamingResponse:
+ from .resources.limits import AsyncLimitsResourceWithStreamingResponse
+
+ return AsyncLimitsResourceWithStreamingResponse(self._client.limits)
+
@cached_property
def tenants(self) -> tenants.AsyncTenantsResourceWithStreamingResponse:
from .resources.tenants import AsyncTenantsResourceWithStreamingResponse
return AsyncTenantsResourceWithStreamingResponse(self._client.tenants)
+ @cached_property
+ def platform(self) -> platform.AsyncPlatformResourceWithStreamingResponse:
+ from .resources.platform import AsyncPlatformResourceWithStreamingResponse
+
+ return AsyncPlatformResourceWithStreamingResponse(self._client.platform)
+
Client = Ark
diff --git a/src/ark/_version.py b/src/ark/_version.py
index 8bd9d9e..53cb1de 100644
--- a/src/ark/_version.py
+++ b/src/ark/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "ark"
-__version__ = "0.17.0" # x-release-please-version
+__version__ = "0.18.0" # x-release-please-version
diff --git a/src/ark/resources/__init__.py b/src/ark/resources/__init__.py
index f3812e4..9908f11 100644
--- a/src/ark/resources/__init__.py
+++ b/src/ark/resources/__init__.py
@@ -24,13 +24,13 @@
EmailsResourceWithStreamingResponse,
AsyncEmailsResourceWithStreamingResponse,
)
-from .domains import (
- DomainsResource,
- AsyncDomainsResource,
- DomainsResourceWithRawResponse,
- AsyncDomainsResourceWithRawResponse,
- DomainsResourceWithStreamingResponse,
- AsyncDomainsResourceWithStreamingResponse,
+from .limits import (
+ LimitsResource,
+ AsyncLimitsResource,
+ LimitsResourceWithRawResponse,
+ AsyncLimitsResourceWithRawResponse,
+ LimitsResourceWithStreamingResponse,
+ AsyncLimitsResourceWithStreamingResponse,
)
from .tenants import (
TenantsResource,
@@ -40,29 +40,13 @@
TenantsResourceWithStreamingResponse,
AsyncTenantsResourceWithStreamingResponse,
)
-from .tracking import (
- TrackingResource,
- AsyncTrackingResource,
- TrackingResourceWithRawResponse,
- AsyncTrackingResourceWithRawResponse,
- TrackingResourceWithStreamingResponse,
- AsyncTrackingResourceWithStreamingResponse,
-)
-from .webhooks import (
- WebhooksResource,
- AsyncWebhooksResource,
- WebhooksResourceWithRawResponse,
- AsyncWebhooksResourceWithRawResponse,
- WebhooksResourceWithStreamingResponse,
- AsyncWebhooksResourceWithStreamingResponse,
-)
-from .suppressions import (
- SuppressionsResource,
- AsyncSuppressionsResource,
- SuppressionsResourceWithRawResponse,
- AsyncSuppressionsResourceWithRawResponse,
- SuppressionsResourceWithStreamingResponse,
- AsyncSuppressionsResourceWithStreamingResponse,
+from .platform import (
+ PlatformResource,
+ AsyncPlatformResource,
+ PlatformResourceWithRawResponse,
+ AsyncPlatformResourceWithRawResponse,
+ PlatformResourceWithStreamingResponse,
+ AsyncPlatformResourceWithStreamingResponse,
)
__all__ = [
@@ -72,30 +56,6 @@
"AsyncEmailsResourceWithRawResponse",
"EmailsResourceWithStreamingResponse",
"AsyncEmailsResourceWithStreamingResponse",
- "DomainsResource",
- "AsyncDomainsResource",
- "DomainsResourceWithRawResponse",
- "AsyncDomainsResourceWithRawResponse",
- "DomainsResourceWithStreamingResponse",
- "AsyncDomainsResourceWithStreamingResponse",
- "SuppressionsResource",
- "AsyncSuppressionsResource",
- "SuppressionsResourceWithRawResponse",
- "AsyncSuppressionsResourceWithRawResponse",
- "SuppressionsResourceWithStreamingResponse",
- "AsyncSuppressionsResourceWithStreamingResponse",
- "WebhooksResource",
- "AsyncWebhooksResource",
- "WebhooksResourceWithRawResponse",
- "AsyncWebhooksResourceWithRawResponse",
- "WebhooksResourceWithStreamingResponse",
- "AsyncWebhooksResourceWithStreamingResponse",
- "TrackingResource",
- "AsyncTrackingResource",
- "TrackingResourceWithRawResponse",
- "AsyncTrackingResourceWithRawResponse",
- "TrackingResourceWithStreamingResponse",
- "AsyncTrackingResourceWithStreamingResponse",
"LogsResource",
"AsyncLogsResource",
"LogsResourceWithRawResponse",
@@ -108,10 +68,22 @@
"AsyncUsageResourceWithRawResponse",
"UsageResourceWithStreamingResponse",
"AsyncUsageResourceWithStreamingResponse",
+ "LimitsResource",
+ "AsyncLimitsResource",
+ "LimitsResourceWithRawResponse",
+ "AsyncLimitsResourceWithRawResponse",
+ "LimitsResourceWithStreamingResponse",
+ "AsyncLimitsResourceWithStreamingResponse",
"TenantsResource",
"AsyncTenantsResource",
"TenantsResourceWithRawResponse",
"AsyncTenantsResourceWithRawResponse",
"TenantsResourceWithStreamingResponse",
"AsyncTenantsResourceWithStreamingResponse",
+ "PlatformResource",
+ "AsyncPlatformResource",
+ "PlatformResourceWithRawResponse",
+ "AsyncPlatformResourceWithRawResponse",
+ "PlatformResourceWithStreamingResponse",
+ "AsyncPlatformResourceWithStreamingResponse",
]
diff --git a/src/ark/resources/emails.py b/src/ark/resources/emails.py
index 4169d3a..77631ce 100644
--- a/src/ark/resources/emails.py
+++ b/src/ark/resources/emails.py
@@ -59,7 +59,7 @@ def with_streaming_response(self) -> EmailsResourceWithStreamingResponse:
def retrieve(
self,
- id: str,
+ email_id: str,
*,
expand: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -96,10 +96,10 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not email_id:
+ raise ValueError(f"Expected a non-empty value for `email_id` but received {email_id!r}")
return self._get(
- f"/emails/{id}",
+ f"/emails/{email_id}",
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -137,7 +137,7 @@ def list(
**Related endpoints:**
- - `GET /emails/{id}` - Get full details of a specific email
+ - `GET /emails/{emailId}` - Get full details of a specific email
- `POST /emails` - Send a new email
Args:
@@ -200,7 +200,7 @@ def list(
def retrieve_deliveries(
self,
- id: str,
+ email_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.
@@ -243,8 +243,8 @@ def retrieve_deliveries(
### Can Retry Manually
- Indicates whether you can call `POST /emails/{id}/retry` to manually retry the
- email. This is `true` when the raw message content is still available (not
+ Indicates whether you can call `POST /emails/{emailId}/retry` to manually retry
+ the email. This is `true` when the raw message content is still available (not
expired due to retention policy).
Args:
@@ -256,10 +256,10 @@ def retrieve_deliveries(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not email_id:
+ raise ValueError(f"Expected a non-empty value for `email_id` but received {email_id!r}")
return self._get(
- f"/emails/{id}/deliveries",
+ f"/emails/{email_id}/deliveries",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -268,7 +268,7 @@ def retrieve_deliveries(
def retry(
self,
- id: str,
+ email_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.
@@ -293,10 +293,10 @@ def retry(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not email_id:
+ raise ValueError(f"Expected a non-empty value for `email_id` but received {email_id!r}")
return self._post(
- f"/emails/{id}/retry",
+ f"/emails/{email_id}/retry",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -339,9 +339,9 @@ def send(
**Related endpoints:**
- - `GET /emails/{id}` - Track delivery status
- - `GET /emails/{id}/deliveries` - View delivery attempts
- - `POST /emails/{id}/retry` - Retry failed delivery
+ - `GET /emails/{emailId}` - Track delivery status
+ - `GET /emails/{emailId}/deliveries` - View delivery attempts
+ - `POST /emails/{emailId}/retry` - Retry failed delivery
Args:
from_: Sender email address. Must be from a verified domain OR use sandbox mode.
@@ -575,7 +575,7 @@ def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse:
async def retrieve(
self,
- id: str,
+ email_id: str,
*,
expand: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -612,10 +612,10 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not email_id:
+ raise ValueError(f"Expected a non-empty value for `email_id` but received {email_id!r}")
return await self._get(
- f"/emails/{id}",
+ f"/emails/{email_id}",
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -653,7 +653,7 @@ def list(
**Related endpoints:**
- - `GET /emails/{id}` - Get full details of a specific email
+ - `GET /emails/{emailId}` - Get full details of a specific email
- `POST /emails` - Send a new email
Args:
@@ -716,7 +716,7 @@ def list(
async def retrieve_deliveries(
self,
- id: str,
+ email_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.
@@ -759,8 +759,8 @@ async def retrieve_deliveries(
### Can Retry Manually
- Indicates whether you can call `POST /emails/{id}/retry` to manually retry the
- email. This is `true` when the raw message content is still available (not
+ Indicates whether you can call `POST /emails/{emailId}/retry` to manually retry
+ the email. This is `true` when the raw message content is still available (not
expired due to retention policy).
Args:
@@ -772,10 +772,10 @@ async def retrieve_deliveries(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not email_id:
+ raise ValueError(f"Expected a non-empty value for `email_id` but received {email_id!r}")
return await self._get(
- f"/emails/{id}/deliveries",
+ f"/emails/{email_id}/deliveries",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -784,7 +784,7 @@ async def retrieve_deliveries(
async def retry(
self,
- id: str,
+ email_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.
@@ -809,10 +809,10 @@ async def retry(
timeout: Override the client-level default timeout for this request, in seconds
"""
- if not id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ if not email_id:
+ raise ValueError(f"Expected a non-empty value for `email_id` but received {email_id!r}")
return await self._post(
- f"/emails/{id}/retry",
+ f"/emails/{email_id}/retry",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -855,9 +855,9 @@ async def send(
**Related endpoints:**
- - `GET /emails/{id}` - Track delivery status
- - `GET /emails/{id}/deliveries` - View delivery attempts
- - `POST /emails/{id}/retry` - Retry failed delivery
+ - `GET /emails/{emailId}` - Track delivery status
+ - `GET /emails/{emailId}/deliveries` - View delivery attempts
+ - `POST /emails/{emailId}/retry` - Retry failed delivery
Args:
from_: Sender email address. Must be from a verified domain OR use sandbox mode.
diff --git a/src/ark/resources/limits.py b/src/ark/resources/limits.py
new file mode 100644
index 0000000..8dd9db6
--- /dev/null
+++ b/src/ark/resources/limits.py
@@ -0,0 +1,171 @@
+# 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 .._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.limit_retrieve_response import LimitRetrieveResponse
+
+__all__ = ["LimitsResource", "AsyncLimitsResource"]
+
+
+class LimitsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> LimitsResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return LimitsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LimitsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return LimitsResourceWithStreamingResponse(self)
+
+ def retrieve(
+ 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,
+ ) -> LimitRetrieveResponse:
+ """
+ Returns current rate limit and send limit information for your account.
+
+ This endpoint is the recommended way to check your account's operational limits.
+ Use `/usage` endpoints for historical usage analytics.
+
+ **Response includes:**
+
+ - `rateLimit` - API request rate limit (requests per second)
+ - `sendLimit` - Email sending limit (emails per hour)
+ - `billing` - Credit balance and auto-recharge configuration
+
+ **Notes:**
+
+ - This request counts against your rate limit
+ - `sendLimit` may be null if the service is temporarily unavailable
+ - `billing` is null if billing is not configured
+ - Send limit resets at the top of each hour
+ """
+ return self._get(
+ "/limits",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LimitRetrieveResponse,
+ )
+
+
+class AsyncLimitsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncLimitsResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncLimitsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLimitsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return AsyncLimitsResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ 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,
+ ) -> LimitRetrieveResponse:
+ """
+ Returns current rate limit and send limit information for your account.
+
+ This endpoint is the recommended way to check your account's operational limits.
+ Use `/usage` endpoints for historical usage analytics.
+
+ **Response includes:**
+
+ - `rateLimit` - API request rate limit (requests per second)
+ - `sendLimit` - Email sending limit (emails per hour)
+ - `billing` - Credit balance and auto-recharge configuration
+
+ **Notes:**
+
+ - This request counts against your rate limit
+ - `sendLimit` may be null if the service is temporarily unavailable
+ - `billing` is null if billing is not configured
+ - Send limit resets at the top of each hour
+ """
+ return await self._get(
+ "/limits",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LimitRetrieveResponse,
+ )
+
+
+class LimitsResourceWithRawResponse:
+ def __init__(self, limits: LimitsResource) -> None:
+ self._limits = limits
+
+ self.retrieve = to_raw_response_wrapper(
+ limits.retrieve,
+ )
+
+
+class AsyncLimitsResourceWithRawResponse:
+ def __init__(self, limits: AsyncLimitsResource) -> None:
+ self._limits = limits
+
+ self.retrieve = async_to_raw_response_wrapper(
+ limits.retrieve,
+ )
+
+
+class LimitsResourceWithStreamingResponse:
+ def __init__(self, limits: LimitsResource) -> None:
+ self._limits = limits
+
+ self.retrieve = to_streamed_response_wrapper(
+ limits.retrieve,
+ )
+
+
+class AsyncLimitsResourceWithStreamingResponse:
+ def __init__(self, limits: AsyncLimitsResource) -> None:
+ self._limits = limits
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ limits.retrieve,
+ )
diff --git a/src/ark/resources/platform/__init__.py b/src/ark/resources/platform/__init__.py
new file mode 100644
index 0000000..17e4578
--- /dev/null
+++ b/src/ark/resources/platform/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .platform import (
+ PlatformResource,
+ AsyncPlatformResource,
+ PlatformResourceWithRawResponse,
+ AsyncPlatformResourceWithRawResponse,
+ PlatformResourceWithStreamingResponse,
+ AsyncPlatformResourceWithStreamingResponse,
+)
+from .webhooks import (
+ WebhooksResource,
+ AsyncWebhooksResource,
+ WebhooksResourceWithRawResponse,
+ AsyncWebhooksResourceWithRawResponse,
+ WebhooksResourceWithStreamingResponse,
+ AsyncWebhooksResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "WebhooksResource",
+ "AsyncWebhooksResource",
+ "WebhooksResourceWithRawResponse",
+ "AsyncWebhooksResourceWithRawResponse",
+ "WebhooksResourceWithStreamingResponse",
+ "AsyncWebhooksResourceWithStreamingResponse",
+ "PlatformResource",
+ "AsyncPlatformResource",
+ "PlatformResourceWithRawResponse",
+ "AsyncPlatformResourceWithRawResponse",
+ "PlatformResourceWithStreamingResponse",
+ "AsyncPlatformResourceWithStreamingResponse",
+]
diff --git a/src/ark/resources/platform/platform.py b/src/ark/resources/platform/platform.py
new file mode 100644
index 0000000..5d6e945
--- /dev/null
+++ b/src/ark/resources/platform/platform.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .webhooks import (
+ WebhooksResource,
+ AsyncWebhooksResource,
+ WebhooksResourceWithRawResponse,
+ AsyncWebhooksResourceWithRawResponse,
+ WebhooksResourceWithStreamingResponse,
+ AsyncWebhooksResourceWithStreamingResponse,
+)
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+
+__all__ = ["PlatformResource", "AsyncPlatformResource"]
+
+
+class PlatformResource(SyncAPIResource):
+ @cached_property
+ def webhooks(self) -> WebhooksResource:
+ return WebhooksResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> PlatformResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return PlatformResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> PlatformResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return PlatformResourceWithStreamingResponse(self)
+
+
+class AsyncPlatformResource(AsyncAPIResource):
+ @cached_property
+ def webhooks(self) -> AsyncWebhooksResource:
+ return AsyncWebhooksResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncPlatformResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncPlatformResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncPlatformResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return AsyncPlatformResourceWithStreamingResponse(self)
+
+
+class PlatformResourceWithRawResponse:
+ def __init__(self, platform: PlatformResource) -> None:
+ self._platform = platform
+
+ @cached_property
+ def webhooks(self) -> WebhooksResourceWithRawResponse:
+ return WebhooksResourceWithRawResponse(self._platform.webhooks)
+
+
+class AsyncPlatformResourceWithRawResponse:
+ def __init__(self, platform: AsyncPlatformResource) -> None:
+ self._platform = platform
+
+ @cached_property
+ def webhooks(self) -> AsyncWebhooksResourceWithRawResponse:
+ return AsyncWebhooksResourceWithRawResponse(self._platform.webhooks)
+
+
+class PlatformResourceWithStreamingResponse:
+ def __init__(self, platform: PlatformResource) -> None:
+ self._platform = platform
+
+ @cached_property
+ def webhooks(self) -> WebhooksResourceWithStreamingResponse:
+ return WebhooksResourceWithStreamingResponse(self._platform.webhooks)
+
+
+class AsyncPlatformResourceWithStreamingResponse:
+ def __init__(self, platform: AsyncPlatformResource) -> None:
+ self._platform = platform
+
+ @cached_property
+ def webhooks(self) -> AsyncWebhooksResourceWithStreamingResponse:
+ return AsyncWebhooksResourceWithStreamingResponse(self._platform.webhooks)
diff --git a/src/ark/resources/platform/webhooks.py b/src/ark/resources/platform/webhooks.py
new file mode 100644
index 0000000..7a30429
--- /dev/null
+++ b/src/ark/resources/platform/webhooks.py
@@ -0,0 +1,1140 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import 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 SyncPageNumberPagination, AsyncPageNumberPagination
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.platform import (
+ webhook_test_params,
+ webhook_create_params,
+ webhook_update_params,
+ webhook_list_deliveries_params,
+)
+from ...types.platform.webhook_list_response import WebhookListResponse
+from ...types.platform.webhook_test_response import WebhookTestResponse
+from ...types.platform.webhook_create_response import WebhookCreateResponse
+from ...types.platform.webhook_delete_response import WebhookDeleteResponse
+from ...types.platform.webhook_update_response import WebhookUpdateResponse
+from ...types.platform.webhook_retrieve_response import WebhookRetrieveResponse
+from ...types.platform.webhook_list_deliveries_response import WebhookListDeliveriesResponse
+from ...types.platform.webhook_replay_delivery_response import WebhookReplayDeliveryResponse
+from ...types.platform.webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse
+
+__all__ = ["WebhooksResource", "AsyncWebhooksResource"]
+
+
+class WebhooksResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> WebhooksResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return WebhooksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> WebhooksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return WebhooksResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ name: str,
+ url: str,
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ | 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,
+ ) -> WebhookCreateResponse:
+ """
+ Create a platform webhook to receive email event notifications from all tenants.
+
+ Platform webhooks receive events from **all tenants** in your organization. Each
+ webhook payload includes a `tenant_id` field to identify which tenant the event
+ belongs to.
+
+ **Available events:**
+
+ - `MessageSent` - Email accepted by recipient server
+ - `MessageDeliveryFailed` - Delivery permanently failed
+ - `MessageDelayed` - Delivery temporarily failed, will retry
+ - `MessageBounced` - Email bounced
+ - `MessageHeld` - Email held for review
+ - `MessageLinkClicked` - Recipient clicked a link
+ - `MessageLoaded` - Recipient opened the email
+ - `DomainDNSError` - Domain DNS issue detected
+
+ **Webhook payload includes:**
+
+ - `event` - The event type
+ - `tenant_id` - The tenant that sent the email
+ - `timestamp` - Unix timestamp of the event
+ - `payload` - Event-specific data (message details, status, etc.)
+
+ Args:
+ name: Display name for the webhook
+
+ url: Webhook endpoint URL (must be HTTPS)
+
+ events: Events to subscribe to. Empty array means all events.
+
+ 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(
+ "/platform/webhooks",
+ body=maybe_transform(
+ {
+ "name": name,
+ "url": url,
+ "events": events,
+ },
+ webhook_create_params.WebhookCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ webhook_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,
+ ) -> WebhookRetrieveResponse:
+ """
+ Get detailed information about a specific platform 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return self._get(
+ f"/platform/webhooks/{webhook_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookRetrieveResponse,
+ )
+
+ def update(
+ self,
+ webhook_id: str,
+ *,
+ enabled: bool | Omit = omit,
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ | Omit = omit,
+ name: str | Omit = omit,
+ url: 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,
+ ) -> WebhookUpdateResponse:
+ """
+ Update a platform webhook's configuration.
+
+ You can update:
+
+ - `name` - Display name for the webhook
+ - `url` - The endpoint URL (must be HTTPS)
+ - `events` - Array of event types to receive (empty array = all events)
+ - `enabled` - Enable or disable the webhook
+
+ Args:
+ enabled: Enable or disable the webhook
+
+ events: Events to subscribe to. Empty array means all events.
+
+ name: Display name for the webhook
+
+ url: Webhook endpoint URL (must be HTTPS)
+
+ 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return self._patch(
+ f"/platform/webhooks/{webhook_id}",
+ body=maybe_transform(
+ {
+ "enabled": enabled,
+ "events": events,
+ "name": name,
+ "url": url,
+ },
+ webhook_update_params.WebhookUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookUpdateResponse,
+ )
+
+ 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,
+ ) -> WebhookListResponse:
+ """
+ Get all platform webhook endpoints configured for your organization.
+
+ Platform webhooks receive events from **all tenants** in your organization,
+ unlike tenant webhooks which only receive events for a specific tenant. This is
+ useful for centralized event processing and monitoring.
+ """
+ return self._get(
+ "/platform/webhooks",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookListResponse,
+ )
+
+ def delete(
+ self,
+ webhook_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,
+ ) -> WebhookDeleteResponse:
+ """Delete a platform webhook.
+
+ This stops all event delivery to the webhook URL.
+ This action cannot be undone.
+
+ 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return self._delete(
+ f"/platform/webhooks/{webhook_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookDeleteResponse,
+ )
+
+ def list_deliveries(
+ self,
+ *,
+ after: int | Omit = omit,
+ before: int | Omit = omit,
+ event: Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ success: bool | Omit = omit,
+ tenant_id: str | Omit = omit,
+ webhook_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,
+ ) -> SyncPageNumberPagination[WebhookListDeliveriesResponse]:
+ """
+ Get a paginated list of platform webhook delivery attempts.
+
+ Filter by:
+
+ - `webhookId` - Specific webhook
+ - `tenantId` - Specific tenant
+ - `event` - Specific event type
+ - `success` - Successful (2xx) or failed deliveries
+ - `before`/`after` - Time range (Unix timestamps)
+
+ Deliveries are returned in reverse chronological order.
+
+ Args:
+ after: Only deliveries after this Unix timestamp
+
+ before: Only deliveries before this Unix timestamp
+
+ event: Filter by event type
+
+ page: Page number (default 1)
+
+ per_page: Items per page (default 30, max 100)
+
+ success: Filter by delivery success
+
+ tenant_id: Filter by tenant ID
+
+ webhook_id: Filter by platform webhook ID
+
+ 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(
+ "/platform/webhooks/deliveries",
+ page=SyncPageNumberPagination[WebhookListDeliveriesResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "before": before,
+ "event": event,
+ "page": page,
+ "per_page": per_page,
+ "success": success,
+ "tenant_id": tenant_id,
+ "webhook_id": webhook_id,
+ },
+ webhook_list_deliveries_params.WebhookListDeliveriesParams,
+ ),
+ ),
+ model=WebhookListDeliveriesResponse,
+ )
+
+ def replay_delivery(
+ self,
+ delivery_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,
+ ) -> WebhookReplayDeliveryResponse:
+ """
+ Replay a previous platform webhook delivery.
+
+ This re-sends the original payload with a new timestamp and delivery ID. Useful
+ for recovering from temporary endpoint failures.
+
+ 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 delivery_id:
+ raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
+ return self._post(
+ f"/platform/webhooks/deliveries/{delivery_id}/replay",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookReplayDeliveryResponse,
+ )
+
+ def retrieve_delivery(
+ self,
+ delivery_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,
+ ) -> WebhookRetrieveDeliveryResponse:
+ """
+ Get detailed information about a specific platform webhook delivery.
+
+ Returns the complete request payload, headers, response, and timing info.
+
+ 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 delivery_id:
+ raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
+ return self._get(
+ f"/platform/webhooks/deliveries/{delivery_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookRetrieveDeliveryResponse,
+ )
+
+ def test(
+ self,
+ webhook_id: str,
+ *,
+ event: Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ],
+ # 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,
+ ) -> WebhookTestResponse:
+ """
+ Send a test payload to your platform webhook endpoint.
+
+ Use this to:
+
+ - Verify your webhook URL is accessible
+ - Test your payload handling code
+ - Ensure your server responds correctly
+
+ The test payload is marked with `_test: true` so you can distinguish test events
+ from real events.
+
+ Args:
+ event: Event type to simulate
+
+ 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return self._post(
+ f"/platform/webhooks/{webhook_id}/test",
+ body=maybe_transform({"event": event}, webhook_test_params.WebhookTestParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookTestResponse,
+ )
+
+
+class AsyncWebhooksResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncWebhooksResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncWebhooksResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncWebhooksResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return AsyncWebhooksResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ name: str,
+ url: str,
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ | 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,
+ ) -> WebhookCreateResponse:
+ """
+ Create a platform webhook to receive email event notifications from all tenants.
+
+ Platform webhooks receive events from **all tenants** in your organization. Each
+ webhook payload includes a `tenant_id` field to identify which tenant the event
+ belongs to.
+
+ **Available events:**
+
+ - `MessageSent` - Email accepted by recipient server
+ - `MessageDeliveryFailed` - Delivery permanently failed
+ - `MessageDelayed` - Delivery temporarily failed, will retry
+ - `MessageBounced` - Email bounced
+ - `MessageHeld` - Email held for review
+ - `MessageLinkClicked` - Recipient clicked a link
+ - `MessageLoaded` - Recipient opened the email
+ - `DomainDNSError` - Domain DNS issue detected
+
+ **Webhook payload includes:**
+
+ - `event` - The event type
+ - `tenant_id` - The tenant that sent the email
+ - `timestamp` - Unix timestamp of the event
+ - `payload` - Event-specific data (message details, status, etc.)
+
+ Args:
+ name: Display name for the webhook
+
+ url: Webhook endpoint URL (must be HTTPS)
+
+ events: Events to subscribe to. Empty array means all events.
+
+ 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(
+ "/platform/webhooks",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "url": url,
+ "events": events,
+ },
+ webhook_create_params.WebhookCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ webhook_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,
+ ) -> WebhookRetrieveResponse:
+ """
+ Get detailed information about a specific platform 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return await self._get(
+ f"/platform/webhooks/{webhook_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ webhook_id: str,
+ *,
+ enabled: bool | Omit = omit,
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ | Omit = omit,
+ name: str | Omit = omit,
+ url: 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,
+ ) -> WebhookUpdateResponse:
+ """
+ Update a platform webhook's configuration.
+
+ You can update:
+
+ - `name` - Display name for the webhook
+ - `url` - The endpoint URL (must be HTTPS)
+ - `events` - Array of event types to receive (empty array = all events)
+ - `enabled` - Enable or disable the webhook
+
+ Args:
+ enabled: Enable or disable the webhook
+
+ events: Events to subscribe to. Empty array means all events.
+
+ name: Display name for the webhook
+
+ url: Webhook endpoint URL (must be HTTPS)
+
+ 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return await self._patch(
+ f"/platform/webhooks/{webhook_id}",
+ body=await async_maybe_transform(
+ {
+ "enabled": enabled,
+ "events": events,
+ "name": name,
+ "url": url,
+ },
+ webhook_update_params.WebhookUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookUpdateResponse,
+ )
+
+ 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,
+ ) -> WebhookListResponse:
+ """
+ Get all platform webhook endpoints configured for your organization.
+
+ Platform webhooks receive events from **all tenants** in your organization,
+ unlike tenant webhooks which only receive events for a specific tenant. This is
+ useful for centralized event processing and monitoring.
+ """
+ return await self._get(
+ "/platform/webhooks",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookListResponse,
+ )
+
+ async def delete(
+ self,
+ webhook_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,
+ ) -> WebhookDeleteResponse:
+ """Delete a platform webhook.
+
+ This stops all event delivery to the webhook URL.
+ This action cannot be undone.
+
+ 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return await self._delete(
+ f"/platform/webhooks/{webhook_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookDeleteResponse,
+ )
+
+ def list_deliveries(
+ self,
+ *,
+ after: int | Omit = omit,
+ before: int | Omit = omit,
+ event: Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ | Omit = omit,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ success: bool | Omit = omit,
+ tenant_id: str | Omit = omit,
+ webhook_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[WebhookListDeliveriesResponse, AsyncPageNumberPagination[WebhookListDeliveriesResponse]]:
+ """
+ Get a paginated list of platform webhook delivery attempts.
+
+ Filter by:
+
+ - `webhookId` - Specific webhook
+ - `tenantId` - Specific tenant
+ - `event` - Specific event type
+ - `success` - Successful (2xx) or failed deliveries
+ - `before`/`after` - Time range (Unix timestamps)
+
+ Deliveries are returned in reverse chronological order.
+
+ Args:
+ after: Only deliveries after this Unix timestamp
+
+ before: Only deliveries before this Unix timestamp
+
+ event: Filter by event type
+
+ page: Page number (default 1)
+
+ per_page: Items per page (default 30, max 100)
+
+ success: Filter by delivery success
+
+ tenant_id: Filter by tenant ID
+
+ webhook_id: Filter by platform webhook ID
+
+ 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(
+ "/platform/webhooks/deliveries",
+ page=AsyncPageNumberPagination[WebhookListDeliveriesResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "after": after,
+ "before": before,
+ "event": event,
+ "page": page,
+ "per_page": per_page,
+ "success": success,
+ "tenant_id": tenant_id,
+ "webhook_id": webhook_id,
+ },
+ webhook_list_deliveries_params.WebhookListDeliveriesParams,
+ ),
+ ),
+ model=WebhookListDeliveriesResponse,
+ )
+
+ async def replay_delivery(
+ self,
+ delivery_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,
+ ) -> WebhookReplayDeliveryResponse:
+ """
+ Replay a previous platform webhook delivery.
+
+ This re-sends the original payload with a new timestamp and delivery ID. Useful
+ for recovering from temporary endpoint failures.
+
+ 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 delivery_id:
+ raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
+ return await self._post(
+ f"/platform/webhooks/deliveries/{delivery_id}/replay",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookReplayDeliveryResponse,
+ )
+
+ async def retrieve_delivery(
+ self,
+ delivery_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,
+ ) -> WebhookRetrieveDeliveryResponse:
+ """
+ Get detailed information about a specific platform webhook delivery.
+
+ Returns the complete request payload, headers, response, and timing info.
+
+ 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 delivery_id:
+ raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
+ return await self._get(
+ f"/platform/webhooks/deliveries/{delivery_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookRetrieveDeliveryResponse,
+ )
+
+ async def test(
+ self,
+ webhook_id: str,
+ *,
+ event: Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ],
+ # 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,
+ ) -> WebhookTestResponse:
+ """
+ Send a test payload to your platform webhook endpoint.
+
+ Use this to:
+
+ - Verify your webhook URL is accessible
+ - Test your payload handling code
+ - Ensure your server responds correctly
+
+ The test payload is marked with `_test: true` so you can distinguish test events
+ from real events.
+
+ Args:
+ event: Event type to simulate
+
+ 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 webhook_id:
+ raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
+ return await self._post(
+ f"/platform/webhooks/{webhook_id}/test",
+ body=await async_maybe_transform({"event": event}, webhook_test_params.WebhookTestParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WebhookTestResponse,
+ )
+
+
+class WebhooksResourceWithRawResponse:
+ def __init__(self, webhooks: WebhooksResource) -> None:
+ self._webhooks = webhooks
+
+ self.create = to_raw_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ webhooks.update,
+ )
+ self.list = to_raw_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ webhooks.delete,
+ )
+ self.list_deliveries = to_raw_response_wrapper(
+ webhooks.list_deliveries,
+ )
+ self.replay_delivery = to_raw_response_wrapper(
+ webhooks.replay_delivery,
+ )
+ self.retrieve_delivery = to_raw_response_wrapper(
+ webhooks.retrieve_delivery,
+ )
+ self.test = to_raw_response_wrapper(
+ webhooks.test,
+ )
+
+
+class AsyncWebhooksResourceWithRawResponse:
+ def __init__(self, webhooks: AsyncWebhooksResource) -> None:
+ self._webhooks = webhooks
+
+ self.create = async_to_raw_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ webhooks.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ webhooks.delete,
+ )
+ self.list_deliveries = async_to_raw_response_wrapper(
+ webhooks.list_deliveries,
+ )
+ self.replay_delivery = async_to_raw_response_wrapper(
+ webhooks.replay_delivery,
+ )
+ self.retrieve_delivery = async_to_raw_response_wrapper(
+ webhooks.retrieve_delivery,
+ )
+ self.test = async_to_raw_response_wrapper(
+ webhooks.test,
+ )
+
+
+class WebhooksResourceWithStreamingResponse:
+ def __init__(self, webhooks: WebhooksResource) -> None:
+ self._webhooks = webhooks
+
+ self.create = to_streamed_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ webhooks.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ webhooks.delete,
+ )
+ self.list_deliveries = to_streamed_response_wrapper(
+ webhooks.list_deliveries,
+ )
+ self.replay_delivery = to_streamed_response_wrapper(
+ webhooks.replay_delivery,
+ )
+ self.retrieve_delivery = to_streamed_response_wrapper(
+ webhooks.retrieve_delivery,
+ )
+ self.test = to_streamed_response_wrapper(
+ webhooks.test,
+ )
+
+
+class AsyncWebhooksResourceWithStreamingResponse:
+ def __init__(self, webhooks: AsyncWebhooksResource) -> None:
+ self._webhooks = webhooks
+
+ self.create = async_to_streamed_response_wrapper(
+ webhooks.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ webhooks.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ webhooks.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ webhooks.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ webhooks.delete,
+ )
+ self.list_deliveries = async_to_streamed_response_wrapper(
+ webhooks.list_deliveries,
+ )
+ self.replay_delivery = async_to_streamed_response_wrapper(
+ webhooks.replay_delivery,
+ )
+ self.retrieve_delivery = async_to_streamed_response_wrapper(
+ webhooks.retrieve_delivery,
+ )
+ self.test = async_to_streamed_response_wrapper(
+ webhooks.test,
+ )
diff --git a/src/ark/resources/tenants/__init__.py b/src/ark/resources/tenants/__init__.py
new file mode 100644
index 0000000..31a4e7e
--- /dev/null
+++ b/src/ark/resources/tenants/__init__.py
@@ -0,0 +1,103 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .usage import (
+ UsageResource,
+ AsyncUsageResource,
+ UsageResourceWithRawResponse,
+ AsyncUsageResourceWithRawResponse,
+ UsageResourceWithStreamingResponse,
+ AsyncUsageResourceWithStreamingResponse,
+)
+from .domains import (
+ DomainsResource,
+ AsyncDomainsResource,
+ DomainsResourceWithRawResponse,
+ AsyncDomainsResourceWithRawResponse,
+ DomainsResourceWithStreamingResponse,
+ AsyncDomainsResourceWithStreamingResponse,
+)
+from .tenants import (
+ TenantsResource,
+ AsyncTenantsResource,
+ TenantsResourceWithRawResponse,
+ AsyncTenantsResourceWithRawResponse,
+ TenantsResourceWithStreamingResponse,
+ AsyncTenantsResourceWithStreamingResponse,
+)
+from .tracking import (
+ TrackingResource,
+ AsyncTrackingResource,
+ TrackingResourceWithRawResponse,
+ AsyncTrackingResourceWithRawResponse,
+ TrackingResourceWithStreamingResponse,
+ AsyncTrackingResourceWithStreamingResponse,
+)
+from .webhooks import (
+ WebhooksResource,
+ AsyncWebhooksResource,
+ WebhooksResourceWithRawResponse,
+ AsyncWebhooksResourceWithRawResponse,
+ WebhooksResourceWithStreamingResponse,
+ AsyncWebhooksResourceWithStreamingResponse,
+)
+from .credentials import (
+ CredentialsResource,
+ AsyncCredentialsResource,
+ CredentialsResourceWithRawResponse,
+ AsyncCredentialsResourceWithRawResponse,
+ CredentialsResourceWithStreamingResponse,
+ AsyncCredentialsResourceWithStreamingResponse,
+)
+from .suppressions import (
+ SuppressionsResource,
+ AsyncSuppressionsResource,
+ SuppressionsResourceWithRawResponse,
+ AsyncSuppressionsResourceWithRawResponse,
+ SuppressionsResourceWithStreamingResponse,
+ AsyncSuppressionsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "CredentialsResource",
+ "AsyncCredentialsResource",
+ "CredentialsResourceWithRawResponse",
+ "AsyncCredentialsResourceWithRawResponse",
+ "CredentialsResourceWithStreamingResponse",
+ "AsyncCredentialsResourceWithStreamingResponse",
+ "DomainsResource",
+ "AsyncDomainsResource",
+ "DomainsResourceWithRawResponse",
+ "AsyncDomainsResourceWithRawResponse",
+ "DomainsResourceWithStreamingResponse",
+ "AsyncDomainsResourceWithStreamingResponse",
+ "SuppressionsResource",
+ "AsyncSuppressionsResource",
+ "SuppressionsResourceWithRawResponse",
+ "AsyncSuppressionsResourceWithRawResponse",
+ "SuppressionsResourceWithStreamingResponse",
+ "AsyncSuppressionsResourceWithStreamingResponse",
+ "WebhooksResource",
+ "AsyncWebhooksResource",
+ "WebhooksResourceWithRawResponse",
+ "AsyncWebhooksResourceWithRawResponse",
+ "WebhooksResourceWithStreamingResponse",
+ "AsyncWebhooksResourceWithStreamingResponse",
+ "TrackingResource",
+ "AsyncTrackingResource",
+ "TrackingResourceWithRawResponse",
+ "AsyncTrackingResourceWithRawResponse",
+ "TrackingResourceWithStreamingResponse",
+ "AsyncTrackingResourceWithStreamingResponse",
+ "UsageResource",
+ "AsyncUsageResource",
+ "UsageResourceWithRawResponse",
+ "AsyncUsageResourceWithRawResponse",
+ "UsageResourceWithStreamingResponse",
+ "AsyncUsageResourceWithStreamingResponse",
+ "TenantsResource",
+ "AsyncTenantsResource",
+ "TenantsResourceWithRawResponse",
+ "AsyncTenantsResourceWithRawResponse",
+ "TenantsResourceWithStreamingResponse",
+ "AsyncTenantsResourceWithStreamingResponse",
+]
diff --git a/src/ark/resources/tenants/credentials.py b/src/ark/resources/tenants/credentials.py
new file mode 100644
index 0000000..2affa6a
--- /dev/null
+++ b/src/ark/resources/tenants/credentials.py
@@ -0,0 +1,679 @@
+# 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 Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import 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 SyncPageNumberPagination, AsyncPageNumberPagination
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.tenants import (
+ credential_list_params,
+ credential_create_params,
+ credential_update_params,
+ credential_retrieve_params,
+)
+from ...types.tenants.credential_list_response import CredentialListResponse
+from ...types.tenants.credential_create_response import CredentialCreateResponse
+from ...types.tenants.credential_delete_response import CredentialDeleteResponse
+from ...types.tenants.credential_update_response import CredentialUpdateResponse
+from ...types.tenants.credential_retrieve_response import CredentialRetrieveResponse
+
+__all__ = ["CredentialsResource", "AsyncCredentialsResource"]
+
+
+class CredentialsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> CredentialsResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return CredentialsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> CredentialsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return CredentialsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ tenant_id: str,
+ *,
+ name: str,
+ type: Literal["smtp", "api"],
+ # 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,
+ ) -> CredentialCreateResponse:
+ """Create a new SMTP or API credential for a tenant.
+
+ The credential can be used to
+ send emails via Ark on behalf of the tenant.
+
+ **Important:** The credential key is only returned once at creation time. Store
+ it securely - you cannot retrieve it again.
+
+ **Credential Types:**
+
+ - `smtp` - For SMTP-based email sending. Returns both `key` and `smtpUsername`.
+ - `api` - For API-based email sending. Returns only `key`.
+
+ Args:
+ name: Name for the credential. Can only contain letters, numbers, hyphens, and
+ underscores. Max 50 characters.
+
+ type:
+ Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._post(
+ f"/tenants/{tenant_id}/credentials",
+ body=maybe_transform(
+ {
+ "name": name,
+ "type": type,
+ },
+ credential_create_params.CredentialCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CredentialCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ credential_id: int,
+ *,
+ tenant_id: str,
+ reveal: 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,
+ ) -> CredentialRetrieveResponse:
+ """
+ Get details of a specific credential.
+
+ **Revealing the key:** By default, the credential key is not returned. Pass
+ `reveal=true` to include the key in the response. Use this sparingly and only
+ when you need to retrieve the key (e.g., for configuration).
+
+ Args:
+ reveal: Set to `true` to include the credential key in the response
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._get(
+ f"/tenants/{tenant_id}/credentials/{credential_id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"reveal": reveal}, credential_retrieve_params.CredentialRetrieveParams),
+ ),
+ cast_to=CredentialRetrieveResponse,
+ )
+
+ def update(
+ self,
+ credential_id: int,
+ *,
+ tenant_id: str,
+ hold: bool | Omit = omit,
+ name: 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,
+ ) -> CredentialUpdateResponse:
+ """
+ Update a credential's name or hold status.
+
+ **Hold Status:**
+
+ - When `hold: true`, the credential is disabled and cannot be used to send
+ emails.
+ - When `hold: false`, the credential is active and can send emails.
+ - Use this to temporarily disable a credential without deleting it.
+
+ Args:
+ hold: Set to `true` to disable the credential (put on hold). Set to `false` to enable
+ the credential (release from hold).
+
+ name: New name for the credential
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._patch(
+ f"/tenants/{tenant_id}/credentials/{credential_id}",
+ body=maybe_transform(
+ {
+ "hold": hold,
+ "name": name,
+ },
+ credential_update_params.CredentialUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CredentialUpdateResponse,
+ )
+
+ def list(
+ self,
+ tenant_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ type: Literal["smtp", "api"] | 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,
+ ) -> SyncPageNumberPagination[CredentialListResponse]:
+ """List all SMTP and API credentials for a tenant.
+
+ Credentials are used to send
+ emails via Ark on behalf of the tenant.
+
+ **Security:** Credential keys are not returned in the list response. Use the
+ retrieve endpoint with `reveal=true` to get the key.
+
+ Args:
+ page: Page number (1-indexed)
+
+ per_page: Number of items per page (max 100)
+
+ type: Filter by credential type
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._get_api_list(
+ f"/tenants/{tenant_id}/credentials",
+ page=SyncPageNumberPagination[CredentialListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ "type": type,
+ },
+ credential_list_params.CredentialListParams,
+ ),
+ ),
+ model=CredentialListResponse,
+ )
+
+ def delete(
+ self,
+ credential_id: int,
+ *,
+ tenant_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,
+ ) -> CredentialDeleteResponse:
+ """Permanently delete (revoke) a credential.
+
+ The credential can no longer be used
+ to send emails.
+
+ **Warning:** This action is irreversible. If you want to temporarily disable a
+ credential, use the update endpoint to set `hold: true` instead.
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._delete(
+ f"/tenants/{tenant_id}/credentials/{credential_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CredentialDeleteResponse,
+ )
+
+
+class AsyncCredentialsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncCredentialsResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncCredentialsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncCredentialsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return AsyncCredentialsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ tenant_id: str,
+ *,
+ name: str,
+ type: Literal["smtp", "api"],
+ # 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,
+ ) -> CredentialCreateResponse:
+ """Create a new SMTP or API credential for a tenant.
+
+ The credential can be used to
+ send emails via Ark on behalf of the tenant.
+
+ **Important:** The credential key is only returned once at creation time. Store
+ it securely - you cannot retrieve it again.
+
+ **Credential Types:**
+
+ - `smtp` - For SMTP-based email sending. Returns both `key` and `smtpUsername`.
+ - `api` - For API-based email sending. Returns only `key`.
+
+ Args:
+ name: Name for the credential. Can only contain letters, numbers, hyphens, and
+ underscores. Max 50 characters.
+
+ type:
+ Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return await self._post(
+ f"/tenants/{tenant_id}/credentials",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "type": type,
+ },
+ credential_create_params.CredentialCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CredentialCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ credential_id: int,
+ *,
+ tenant_id: str,
+ reveal: 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,
+ ) -> CredentialRetrieveResponse:
+ """
+ Get details of a specific credential.
+
+ **Revealing the key:** By default, the credential key is not returned. Pass
+ `reveal=true` to include the key in the response. Use this sparingly and only
+ when you need to retrieve the key (e.g., for configuration).
+
+ Args:
+ reveal: Set to `true` to include the credential key in the response
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return await self._get(
+ f"/tenants/{tenant_id}/credentials/{credential_id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"reveal": reveal}, credential_retrieve_params.CredentialRetrieveParams
+ ),
+ ),
+ cast_to=CredentialRetrieveResponse,
+ )
+
+ async def update(
+ self,
+ credential_id: int,
+ *,
+ tenant_id: str,
+ hold: bool | Omit = omit,
+ name: 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,
+ ) -> CredentialUpdateResponse:
+ """
+ Update a credential's name or hold status.
+
+ **Hold Status:**
+
+ - When `hold: true`, the credential is disabled and cannot be used to send
+ emails.
+ - When `hold: false`, the credential is active and can send emails.
+ - Use this to temporarily disable a credential without deleting it.
+
+ Args:
+ hold: Set to `true` to disable the credential (put on hold). Set to `false` to enable
+ the credential (release from hold).
+
+ name: New name for the credential
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return await self._patch(
+ f"/tenants/{tenant_id}/credentials/{credential_id}",
+ body=await async_maybe_transform(
+ {
+ "hold": hold,
+ "name": name,
+ },
+ credential_update_params.CredentialUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CredentialUpdateResponse,
+ )
+
+ def list(
+ self,
+ tenant_id: str,
+ *,
+ page: int | Omit = omit,
+ per_page: int | Omit = omit,
+ type: Literal["smtp", "api"] | 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[CredentialListResponse, AsyncPageNumberPagination[CredentialListResponse]]:
+ """List all SMTP and API credentials for a tenant.
+
+ Credentials are used to send
+ emails via Ark on behalf of the tenant.
+
+ **Security:** Credential keys are not returned in the list response. Use the
+ retrieve endpoint with `reveal=true` to get the key.
+
+ Args:
+ page: Page number (1-indexed)
+
+ per_page: Number of items per page (max 100)
+
+ type: Filter by credential type
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._get_api_list(
+ f"/tenants/{tenant_id}/credentials",
+ page=AsyncPageNumberPagination[CredentialListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ "type": type,
+ },
+ credential_list_params.CredentialListParams,
+ ),
+ ),
+ model=CredentialListResponse,
+ )
+
+ async def delete(
+ self,
+ credential_id: int,
+ *,
+ tenant_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,
+ ) -> CredentialDeleteResponse:
+ """Permanently delete (revoke) a credential.
+
+ The credential can no longer be used
+ to send emails.
+
+ **Warning:** This action is irreversible. If you want to temporarily disable a
+ credential, use the update endpoint to set `hold: true` instead.
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return await self._delete(
+ f"/tenants/{tenant_id}/credentials/{credential_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CredentialDeleteResponse,
+ )
+
+
+class CredentialsResourceWithRawResponse:
+ def __init__(self, credentials: CredentialsResource) -> None:
+ self._credentials = credentials
+
+ self.create = to_raw_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ credentials.update,
+ )
+ self.list = to_raw_response_wrapper(
+ credentials.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ credentials.delete,
+ )
+
+
+class AsyncCredentialsResourceWithRawResponse:
+ def __init__(self, credentials: AsyncCredentialsResource) -> None:
+ self._credentials = credentials
+
+ self.create = async_to_raw_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ credentials.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ credentials.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ credentials.delete,
+ )
+
+
+class CredentialsResourceWithStreamingResponse:
+ def __init__(self, credentials: CredentialsResource) -> None:
+ self._credentials = credentials
+
+ self.create = to_streamed_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ credentials.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ credentials.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ credentials.delete,
+ )
+
+
+class AsyncCredentialsResourceWithStreamingResponse:
+ def __init__(self, credentials: AsyncCredentialsResource) -> None:
+ self._credentials = credentials
+
+ self.create = async_to_streamed_response_wrapper(
+ credentials.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ credentials.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ credentials.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ credentials.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ credentials.delete,
+ )
diff --git a/src/ark/resources/domains.py b/src/ark/resources/tenants/domains.py
similarity index 78%
rename from src/ark/resources/domains.py
rename to src/ark/resources/tenants/domains.py
index ff0fadc..e560458 100644
--- a/src/ark/resources/domains.py
+++ b/src/ark/resources/tenants/domains.py
@@ -4,23 +4,23 @@
import httpx
-from ..types import domain_create_params
-from .._types import Body, Query, Headers, NotGiven, not_given
-from .._utils import maybe_transform, async_maybe_transform
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import 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.domain_list_response import DomainListResponse
-from ..types.domain_create_response import DomainCreateResponse
-from ..types.domain_delete_response import DomainDeleteResponse
-from ..types.domain_verify_response import DomainVerifyResponse
-from ..types.domain_retrieve_response import DomainRetrieveResponse
+from ..._base_client import make_request_options
+from ...types.tenants import domain_create_params
+from ...types.tenants.domain_list_response import DomainListResponse
+from ...types.tenants.domain_create_response import DomainCreateResponse
+from ...types.tenants.domain_delete_response import DomainDeleteResponse
+from ...types.tenants.domain_verify_response import DomainVerifyResponse
+from ...types.tenants.domain_retrieve_response import DomainRetrieveResponse
__all__ = ["DomainsResource", "AsyncDomainsResource"]
@@ -47,6 +47,7 @@ def with_streaming_response(self) -> DomainsResourceWithStreamingResponse:
def create(
self,
+ tenant_id: str,
*,
name: str,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -56,10 +57,12 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainCreateResponse:
- """Add a new domain for sending emails.
+ """Add a new sending domain to a tenant.
- Returns DNS records that must be configured
- before the domain can be verified.
+ Returns DNS records that must be
+ configured before the domain can be verified.
+
+ Each tenant gets their own isolated mail server for domain isolation.
**Required DNS records:**
@@ -67,7 +70,8 @@ def create(
- **DKIM** - TXT record for email signing
- **Return Path** - CNAME for bounce handling
- After adding DNS records, call `POST /domains/{id}/verify` to verify.
+ After adding DNS records, call
+ `POST /tenants/{tenantId}/domains/{domainId}/verify` to verify.
Args:
name: Domain name (e.g., "mail.example.com")
@@ -80,8 +84,10 @@ def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._post(
- "/domains",
+ f"/tenants/{tenant_id}/domains",
body=maybe_transform({"name": name}, domain_create_params.DomainCreateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -93,6 +99,7 @@ def retrieve(
self,
domain_id: str,
*,
+ tenant_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,
@@ -101,7 +108,7 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainRetrieveResponse:
"""
- Get detailed information about a domain including DNS record status
+ Get detailed information about a domain including DNS record status.
Args:
extra_headers: Send extra headers
@@ -112,10 +119,12 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not domain_id:
raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
return self._get(
- f"/domains/{domain_id}",
+ f"/tenants/{tenant_id}/domains/{domain_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -124,6 +133,7 @@ def retrieve(
def list(
self,
+ tenant_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.
@@ -132,9 +142,22 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainListResponse:
- """Get all sending domains with their verification status"""
+ """
+ Get all sending domains for a specific tenant with their verification 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._get(
- "/domains",
+ f"/tenants/{tenant_id}/domains",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -145,6 +168,7 @@ def delete(
self,
domain_id: str,
*,
+ tenant_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,
@@ -152,10 +176,10 @@ def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainDeleteResponse:
- """Remove a sending domain.
+ """Remove a sending domain from a tenant.
- You will no longer be able to send emails from this
- domain.
+ You will no longer be able to send emails
+ from this domain.
**Warning:** This action cannot be undone.
@@ -168,10 +192,12 @@ def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not domain_id:
raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
return self._delete(
- f"/domains/{domain_id}",
+ f"/tenants/{tenant_id}/domains/{domain_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -182,6 +208,7 @@ def verify(
self,
domain_id: str,
*,
+ tenant_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,
@@ -205,10 +232,12 @@ def verify(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not domain_id:
raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
return self._post(
- f"/domains/{domain_id}/verify",
+ f"/tenants/{tenant_id}/domains/{domain_id}/verify",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -238,6 +267,7 @@ def with_streaming_response(self) -> AsyncDomainsResourceWithStreamingResponse:
async def create(
self,
+ tenant_id: str,
*,
name: str,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -247,10 +277,12 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainCreateResponse:
- """Add a new domain for sending emails.
+ """Add a new sending domain to a tenant.
- Returns DNS records that must be configured
- before the domain can be verified.
+ Returns DNS records that must be
+ configured before the domain can be verified.
+
+ Each tenant gets their own isolated mail server for domain isolation.
**Required DNS records:**
@@ -258,7 +290,8 @@ async def create(
- **DKIM** - TXT record for email signing
- **Return Path** - CNAME for bounce handling
- After adding DNS records, call `POST /domains/{id}/verify` to verify.
+ After adding DNS records, call
+ `POST /tenants/{tenantId}/domains/{domainId}/verify` to verify.
Args:
name: Domain name (e.g., "mail.example.com")
@@ -271,8 +304,10 @@ async def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._post(
- "/domains",
+ f"/tenants/{tenant_id}/domains",
body=await async_maybe_transform({"name": name}, domain_create_params.DomainCreateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -284,6 +319,7 @@ async def retrieve(
self,
domain_id: str,
*,
+ tenant_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,
@@ -292,7 +328,7 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainRetrieveResponse:
"""
- Get detailed information about a domain including DNS record status
+ Get detailed information about a domain including DNS record status.
Args:
extra_headers: Send extra headers
@@ -303,10 +339,12 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not domain_id:
raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
return await self._get(
- f"/domains/{domain_id}",
+ f"/tenants/{tenant_id}/domains/{domain_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -315,6 +353,7 @@ async def retrieve(
async def list(
self,
+ tenant_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.
@@ -323,9 +362,22 @@ async def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainListResponse:
- """Get all sending domains with their verification status"""
+ """
+ Get all sending domains for a specific tenant with their verification 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._get(
- "/domains",
+ f"/tenants/{tenant_id}/domains",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -336,6 +388,7 @@ async def delete(
self,
domain_id: str,
*,
+ tenant_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,
@@ -343,10 +396,10 @@ async def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> DomainDeleteResponse:
- """Remove a sending domain.
+ """Remove a sending domain from a tenant.
- You will no longer be able to send emails from this
- domain.
+ You will no longer be able to send emails
+ from this domain.
**Warning:** This action cannot be undone.
@@ -359,10 +412,12 @@ async def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not domain_id:
raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
return await self._delete(
- f"/domains/{domain_id}",
+ f"/tenants/{tenant_id}/domains/{domain_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -373,6 +428,7 @@ async def verify(
self,
domain_id: str,
*,
+ tenant_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,
@@ -396,10 +452,12 @@ async def verify(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not domain_id:
raise ValueError(f"Expected a non-empty value for `domain_id` but received {domain_id!r}")
return await self._post(
- f"/domains/{domain_id}/verify",
+ f"/tenants/{tenant_id}/domains/{domain_id}/verify",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/ark/resources/suppressions.py b/src/ark/resources/tenants/suppressions.py
similarity index 73%
rename from src/ark/resources/suppressions.py
rename to src/ark/resources/tenants/suppressions.py
index 9cfca89..070b7e1 100644
--- a/src/ark/resources/suppressions.py
+++ b/src/ark/resources/tenants/suppressions.py
@@ -2,28 +2,27 @@
from __future__ import annotations
-from typing import Iterable, Optional
+from typing import Optional
import httpx
-from ..types import suppression_list_params, suppression_create_params, suppression_bulk_create_params
-from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from .._utils import maybe_transform, async_maybe_transform
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import 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 SyncPageNumberPagination, AsyncPageNumberPagination
-from .._base_client import AsyncPaginator, make_request_options
-from ..types.suppression_list_response import SuppressionListResponse
-from ..types.suppression_create_response import SuppressionCreateResponse
-from ..types.suppression_delete_response import SuppressionDeleteResponse
-from ..types.suppression_retrieve_response import SuppressionRetrieveResponse
-from ..types.suppression_bulk_create_response import SuppressionBulkCreateResponse
+from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.tenants import suppression_list_params, suppression_create_params
+from ...types.tenants.suppression_list_response import SuppressionListResponse
+from ...types.tenants.suppression_create_response import SuppressionCreateResponse
+from ...types.tenants.suppression_delete_response import SuppressionDeleteResponse
+from ...types.tenants.suppression_retrieve_response import SuppressionRetrieveResponse
__all__ = ["SuppressionsResource", "AsyncSuppressionsResource"]
@@ -50,6 +49,7 @@ def with_streaming_response(self) -> SuppressionsResourceWithStreamingResponse:
def create(
self,
+ tenant_id: str,
*,
address: str,
reason: Optional[str] | Omit = omit,
@@ -60,10 +60,10 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SuppressionCreateResponse:
- """Add an email address to the suppression list.
+ """Add an email address to the tenant's suppression list.
- The address will not receive any
- emails until removed.
+ The address will not
+ receive any emails from this tenant until removed.
Args:
address: Email address to suppress
@@ -78,8 +78,10 @@ def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._post(
- "/suppressions",
+ f"/tenants/{tenant_id}/suppressions",
body=maybe_transform(
{
"address": address,
@@ -97,6 +99,7 @@ def retrieve(
self,
email: str,
*,
+ tenant_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,
@@ -105,7 +108,7 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SuppressionRetrieveResponse:
"""
- Check if a specific email address is on the suppression list
+ Check if a specific email address is on the tenant's suppression list.
Args:
extra_headers: Send extra headers
@@ -116,10 +119,12 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not email:
raise ValueError(f"Expected a non-empty value for `email` but received {email!r}")
return self._get(
- f"/suppressions/{email}",
+ f"/tenants/{tenant_id}/suppressions/{email}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -128,6 +133,7 @@ def retrieve(
def list(
self,
+ tenant_id: str,
*,
page: int | Omit = omit,
per_page: int | Omit = omit,
@@ -138,10 +144,10 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SyncPageNumberPagination[SuppressionListResponse]:
- """Get all email addresses on the suppression list.
+ """Get all email addresses on the tenant's suppression list.
- These addresses will not
- receive any emails.
+ These addresses will
+ not receive any emails from this tenant.
Args:
extra_headers: Send extra headers
@@ -152,8 +158,10 @@ def list(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._get_api_list(
- "/suppressions",
+ f"/tenants/{tenant_id}/suppressions",
page=SyncPageNumberPagination[SuppressionListResponse],
options=make_request_options(
extra_headers=extra_headers,
@@ -175,6 +183,7 @@ def delete(
self,
email: str,
*,
+ tenant_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,
@@ -182,10 +191,10 @@ def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SuppressionDeleteResponse:
- """Remove an email address from the suppression list.
+ """Remove an email address from the tenant's suppression list.
- The address will be able to
- receive emails again.
+ The address will be
+ able to receive emails from this tenant again.
Args:
extra_headers: Send extra headers
@@ -196,50 +205,18 @@ def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not email:
raise ValueError(f"Expected a non-empty value for `email` but received {email!r}")
return self._delete(
- f"/suppressions/{email}",
+ f"/tenants/{tenant_id}/suppressions/{email}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=SuppressionDeleteResponse,
)
- def bulk_create(
- self,
- *,
- suppressions: Iterable[suppression_bulk_create_params.Suppression],
- # 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,
- ) -> SuppressionBulkCreateResponse:
- """
- Add up to 1000 email addresses to the suppression list at once
-
- 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._post(
- "/suppressions/bulk",
- body=maybe_transform(
- {"suppressions": suppressions}, suppression_bulk_create_params.SuppressionBulkCreateParams
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SuppressionBulkCreateResponse,
- )
-
class AsyncSuppressionsResource(AsyncAPIResource):
@cached_property
@@ -263,6 +240,7 @@ def with_streaming_response(self) -> AsyncSuppressionsResourceWithStreamingRespo
async def create(
self,
+ tenant_id: str,
*,
address: str,
reason: Optional[str] | Omit = omit,
@@ -273,10 +251,10 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SuppressionCreateResponse:
- """Add an email address to the suppression list.
+ """Add an email address to the tenant's suppression list.
- The address will not receive any
- emails until removed.
+ The address will not
+ receive any emails from this tenant until removed.
Args:
address: Email address to suppress
@@ -291,8 +269,10 @@ async def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._post(
- "/suppressions",
+ f"/tenants/{tenant_id}/suppressions",
body=await async_maybe_transform(
{
"address": address,
@@ -310,6 +290,7 @@ async def retrieve(
self,
email: str,
*,
+ tenant_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,
@@ -318,7 +299,7 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SuppressionRetrieveResponse:
"""
- Check if a specific email address is on the suppression list
+ Check if a specific email address is on the tenant's suppression list.
Args:
extra_headers: Send extra headers
@@ -329,10 +310,12 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not email:
raise ValueError(f"Expected a non-empty value for `email` but received {email!r}")
return await self._get(
- f"/suppressions/{email}",
+ f"/tenants/{tenant_id}/suppressions/{email}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -341,6 +324,7 @@ async def retrieve(
def list(
self,
+ tenant_id: str,
*,
page: int | Omit = omit,
per_page: int | Omit = omit,
@@ -351,10 +335,10 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AsyncPaginator[SuppressionListResponse, AsyncPageNumberPagination[SuppressionListResponse]]:
- """Get all email addresses on the suppression list.
+ """Get all email addresses on the tenant's suppression list.
- These addresses will not
- receive any emails.
+ These addresses will
+ not receive any emails from this tenant.
Args:
extra_headers: Send extra headers
@@ -365,8 +349,10 @@ def list(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._get_api_list(
- "/suppressions",
+ f"/tenants/{tenant_id}/suppressions",
page=AsyncPageNumberPagination[SuppressionListResponse],
options=make_request_options(
extra_headers=extra_headers,
@@ -388,6 +374,7 @@ async def delete(
self,
email: str,
*,
+ tenant_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,
@@ -395,10 +382,10 @@ async def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SuppressionDeleteResponse:
- """Remove an email address from the suppression list.
+ """Remove an email address from the tenant's suppression list.
- The address will be able to
- receive emails again.
+ The address will be
+ able to receive emails from this tenant again.
Args:
extra_headers: Send extra headers
@@ -409,50 +396,18 @@ async def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not email:
raise ValueError(f"Expected a non-empty value for `email` but received {email!r}")
return await self._delete(
- f"/suppressions/{email}",
+ f"/tenants/{tenant_id}/suppressions/{email}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=SuppressionDeleteResponse,
)
- async def bulk_create(
- self,
- *,
- suppressions: Iterable[suppression_bulk_create_params.Suppression],
- # 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,
- ) -> SuppressionBulkCreateResponse:
- """
- Add up to 1000 email addresses to the suppression list at once
-
- 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 await self._post(
- "/suppressions/bulk",
- body=await async_maybe_transform(
- {"suppressions": suppressions}, suppression_bulk_create_params.SuppressionBulkCreateParams
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=SuppressionBulkCreateResponse,
- )
-
class SuppressionsResourceWithRawResponse:
def __init__(self, suppressions: SuppressionsResource) -> None:
@@ -470,9 +425,6 @@ def __init__(self, suppressions: SuppressionsResource) -> None:
self.delete = to_raw_response_wrapper(
suppressions.delete,
)
- self.bulk_create = to_raw_response_wrapper(
- suppressions.bulk_create,
- )
class AsyncSuppressionsResourceWithRawResponse:
@@ -491,9 +443,6 @@ def __init__(self, suppressions: AsyncSuppressionsResource) -> None:
self.delete = async_to_raw_response_wrapper(
suppressions.delete,
)
- self.bulk_create = async_to_raw_response_wrapper(
- suppressions.bulk_create,
- )
class SuppressionsResourceWithStreamingResponse:
@@ -512,9 +461,6 @@ def __init__(self, suppressions: SuppressionsResource) -> None:
self.delete = to_streamed_response_wrapper(
suppressions.delete,
)
- self.bulk_create = to_streamed_response_wrapper(
- suppressions.bulk_create,
- )
class AsyncSuppressionsResourceWithStreamingResponse:
@@ -533,6 +479,3 @@ def __init__(self, suppressions: AsyncSuppressionsResource) -> None:
self.delete = async_to_streamed_response_wrapper(
suppressions.delete,
)
- self.bulk_create = async_to_streamed_response_wrapper(
- suppressions.bulk_create,
- )
diff --git a/src/ark/resources/tenants.py b/src/ark/resources/tenants/tenants.py
similarity index 74%
rename from src/ark/resources/tenants.py
rename to src/ark/resources/tenants/tenants.py
index e7b13a1..684cf1a 100644
--- a/src/ark/resources/tenants.py
+++ b/src/ark/resources/tenants/tenants.py
@@ -7,29 +7,101 @@
import httpx
-from ..types import tenant_list_params, tenant_create_params, tenant_update_params
-from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from .._utils import maybe_transform, async_maybe_transform
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
+from .usage import (
+ UsageResource,
+ AsyncUsageResource,
+ UsageResourceWithRawResponse,
+ AsyncUsageResourceWithRawResponse,
+ UsageResourceWithStreamingResponse,
+ AsyncUsageResourceWithStreamingResponse,
+)
+from ...types import tenant_list_params, tenant_create_params, tenant_update_params
+from .domains import (
+ DomainsResource,
+ AsyncDomainsResource,
+ DomainsResourceWithRawResponse,
+ AsyncDomainsResourceWithRawResponse,
+ DomainsResourceWithStreamingResponse,
+ AsyncDomainsResourceWithStreamingResponse,
+)
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from .tracking import (
+ TrackingResource,
+ AsyncTrackingResource,
+ TrackingResourceWithRawResponse,
+ AsyncTrackingResourceWithRawResponse,
+ TrackingResourceWithStreamingResponse,
+ AsyncTrackingResourceWithStreamingResponse,
+)
+from .webhooks import (
+ WebhooksResource,
+ AsyncWebhooksResource,
+ WebhooksResourceWithRawResponse,
+ AsyncWebhooksResourceWithRawResponse,
+ WebhooksResourceWithStreamingResponse,
+ AsyncWebhooksResourceWithStreamingResponse,
+)
+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 SyncPageNumberPagination, AsyncPageNumberPagination
-from .._base_client import AsyncPaginator, make_request_options
-from ..types.tenant import Tenant
-from ..types.tenant_create_response import TenantCreateResponse
-from ..types.tenant_delete_response import TenantDeleteResponse
-from ..types.tenant_update_response import TenantUpdateResponse
-from ..types.tenant_retrieve_response import TenantRetrieveResponse
+from .credentials import (
+ CredentialsResource,
+ AsyncCredentialsResource,
+ CredentialsResourceWithRawResponse,
+ AsyncCredentialsResourceWithRawResponse,
+ CredentialsResourceWithStreamingResponse,
+ AsyncCredentialsResourceWithStreamingResponse,
+)
+from ...pagination import SyncPageNumberPagination, AsyncPageNumberPagination
+from .suppressions import (
+ SuppressionsResource,
+ AsyncSuppressionsResource,
+ SuppressionsResourceWithRawResponse,
+ AsyncSuppressionsResourceWithRawResponse,
+ SuppressionsResourceWithStreamingResponse,
+ AsyncSuppressionsResourceWithStreamingResponse,
+)
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.tenant import Tenant
+from ...types.tenant_create_response import TenantCreateResponse
+from ...types.tenant_delete_response import TenantDeleteResponse
+from ...types.tenant_update_response import TenantUpdateResponse
+from ...types.tenant_retrieve_response import TenantRetrieveResponse
__all__ = ["TenantsResource", "AsyncTenantsResource"]
class TenantsResource(SyncAPIResource):
+ @cached_property
+ def credentials(self) -> CredentialsResource:
+ return CredentialsResource(self._client)
+
+ @cached_property
+ def domains(self) -> DomainsResource:
+ return DomainsResource(self._client)
+
+ @cached_property
+ def suppressions(self) -> SuppressionsResource:
+ return SuppressionsResource(self._client)
+
+ @cached_property
+ def webhooks(self) -> WebhooksResource:
+ return WebhooksResource(self._client)
+
+ @cached_property
+ def tracking(self) -> TrackingResource:
+ return TrackingResource(self._client)
+
+ @cached_property
+ def usage(self) -> UsageResource:
+ return UsageResource(self._client)
+
@cached_property
def with_raw_response(self) -> TenantsResourceWithRawResponse:
"""
@@ -284,6 +356,30 @@ def delete(
class AsyncTenantsResource(AsyncAPIResource):
+ @cached_property
+ def credentials(self) -> AsyncCredentialsResource:
+ return AsyncCredentialsResource(self._client)
+
+ @cached_property
+ def domains(self) -> AsyncDomainsResource:
+ return AsyncDomainsResource(self._client)
+
+ @cached_property
+ def suppressions(self) -> AsyncSuppressionsResource:
+ return AsyncSuppressionsResource(self._client)
+
+ @cached_property
+ def webhooks(self) -> AsyncWebhooksResource:
+ return AsyncWebhooksResource(self._client)
+
+ @cached_property
+ def tracking(self) -> AsyncTrackingResource:
+ return AsyncTrackingResource(self._client)
+
+ @cached_property
+ def usage(self) -> AsyncUsageResource:
+ return AsyncUsageResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncTenantsResourceWithRawResponse:
"""
@@ -557,6 +653,30 @@ def __init__(self, tenants: TenantsResource) -> None:
tenants.delete,
)
+ @cached_property
+ def credentials(self) -> CredentialsResourceWithRawResponse:
+ return CredentialsResourceWithRawResponse(self._tenants.credentials)
+
+ @cached_property
+ def domains(self) -> DomainsResourceWithRawResponse:
+ return DomainsResourceWithRawResponse(self._tenants.domains)
+
+ @cached_property
+ def suppressions(self) -> SuppressionsResourceWithRawResponse:
+ return SuppressionsResourceWithRawResponse(self._tenants.suppressions)
+
+ @cached_property
+ def webhooks(self) -> WebhooksResourceWithRawResponse:
+ return WebhooksResourceWithRawResponse(self._tenants.webhooks)
+
+ @cached_property
+ def tracking(self) -> TrackingResourceWithRawResponse:
+ return TrackingResourceWithRawResponse(self._tenants.tracking)
+
+ @cached_property
+ def usage(self) -> UsageResourceWithRawResponse:
+ return UsageResourceWithRawResponse(self._tenants.usage)
+
class AsyncTenantsResourceWithRawResponse:
def __init__(self, tenants: AsyncTenantsResource) -> None:
@@ -578,6 +698,30 @@ def __init__(self, tenants: AsyncTenantsResource) -> None:
tenants.delete,
)
+ @cached_property
+ def credentials(self) -> AsyncCredentialsResourceWithRawResponse:
+ return AsyncCredentialsResourceWithRawResponse(self._tenants.credentials)
+
+ @cached_property
+ def domains(self) -> AsyncDomainsResourceWithRawResponse:
+ return AsyncDomainsResourceWithRawResponse(self._tenants.domains)
+
+ @cached_property
+ def suppressions(self) -> AsyncSuppressionsResourceWithRawResponse:
+ return AsyncSuppressionsResourceWithRawResponse(self._tenants.suppressions)
+
+ @cached_property
+ def webhooks(self) -> AsyncWebhooksResourceWithRawResponse:
+ return AsyncWebhooksResourceWithRawResponse(self._tenants.webhooks)
+
+ @cached_property
+ def tracking(self) -> AsyncTrackingResourceWithRawResponse:
+ return AsyncTrackingResourceWithRawResponse(self._tenants.tracking)
+
+ @cached_property
+ def usage(self) -> AsyncUsageResourceWithRawResponse:
+ return AsyncUsageResourceWithRawResponse(self._tenants.usage)
+
class TenantsResourceWithStreamingResponse:
def __init__(self, tenants: TenantsResource) -> None:
@@ -599,6 +743,30 @@ def __init__(self, tenants: TenantsResource) -> None:
tenants.delete,
)
+ @cached_property
+ def credentials(self) -> CredentialsResourceWithStreamingResponse:
+ return CredentialsResourceWithStreamingResponse(self._tenants.credentials)
+
+ @cached_property
+ def domains(self) -> DomainsResourceWithStreamingResponse:
+ return DomainsResourceWithStreamingResponse(self._tenants.domains)
+
+ @cached_property
+ def suppressions(self) -> SuppressionsResourceWithStreamingResponse:
+ return SuppressionsResourceWithStreamingResponse(self._tenants.suppressions)
+
+ @cached_property
+ def webhooks(self) -> WebhooksResourceWithStreamingResponse:
+ return WebhooksResourceWithStreamingResponse(self._tenants.webhooks)
+
+ @cached_property
+ def tracking(self) -> TrackingResourceWithStreamingResponse:
+ return TrackingResourceWithStreamingResponse(self._tenants.tracking)
+
+ @cached_property
+ def usage(self) -> UsageResourceWithStreamingResponse:
+ return UsageResourceWithStreamingResponse(self._tenants.usage)
+
class AsyncTenantsResourceWithStreamingResponse:
def __init__(self, tenants: AsyncTenantsResource) -> None:
@@ -619,3 +787,27 @@ def __init__(self, tenants: AsyncTenantsResource) -> None:
self.delete = async_to_streamed_response_wrapper(
tenants.delete,
)
+
+ @cached_property
+ def credentials(self) -> AsyncCredentialsResourceWithStreamingResponse:
+ return AsyncCredentialsResourceWithStreamingResponse(self._tenants.credentials)
+
+ @cached_property
+ def domains(self) -> AsyncDomainsResourceWithStreamingResponse:
+ return AsyncDomainsResourceWithStreamingResponse(self._tenants.domains)
+
+ @cached_property
+ def suppressions(self) -> AsyncSuppressionsResourceWithStreamingResponse:
+ return AsyncSuppressionsResourceWithStreamingResponse(self._tenants.suppressions)
+
+ @cached_property
+ def webhooks(self) -> AsyncWebhooksResourceWithStreamingResponse:
+ return AsyncWebhooksResourceWithStreamingResponse(self._tenants.webhooks)
+
+ @cached_property
+ def tracking(self) -> AsyncTrackingResourceWithStreamingResponse:
+ return AsyncTrackingResourceWithStreamingResponse(self._tenants.tracking)
+
+ @cached_property
+ def usage(self) -> AsyncUsageResourceWithStreamingResponse:
+ return AsyncUsageResourceWithStreamingResponse(self._tenants.usage)
diff --git a/src/ark/resources/tracking.py b/src/ark/resources/tenants/tracking.py
similarity index 84%
rename from src/ark/resources/tracking.py
rename to src/ark/resources/tenants/tracking.py
index ddcbdd5..995292f 100644
--- a/src/ark/resources/tracking.py
+++ b/src/ark/resources/tenants/tracking.py
@@ -6,24 +6,24 @@
import httpx
-from ..types import tracking_create_params, tracking_update_params
-from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from .._utils import maybe_transform, async_maybe_transform
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import 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.tracking_list_response import TrackingListResponse
-from ..types.tracking_create_response import TrackingCreateResponse
-from ..types.tracking_delete_response import TrackingDeleteResponse
-from ..types.tracking_update_response import TrackingUpdateResponse
-from ..types.tracking_verify_response import TrackingVerifyResponse
-from ..types.tracking_retrieve_response import TrackingRetrieveResponse
+from ..._base_client import make_request_options
+from ...types.tenants import tracking_create_params, tracking_update_params
+from ...types.tenants.tracking_list_response import TrackingListResponse
+from ...types.tenants.tracking_create_response import TrackingCreateResponse
+from ...types.tenants.tracking_delete_response import TrackingDeleteResponse
+from ...types.tenants.tracking_update_response import TrackingUpdateResponse
+from ...types.tenants.tracking_verify_response import TrackingVerifyResponse
+from ...types.tenants.tracking_retrieve_response import TrackingRetrieveResponse
__all__ = ["TrackingResource", "AsyncTrackingResource"]
@@ -50,6 +50,7 @@ def with_streaming_response(self) -> TrackingResourceWithStreamingResponse:
def create(
self,
+ tenant_id: str,
*,
domain_id: int,
name: str,
@@ -64,7 +65,7 @@ def create(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TrackingCreateResponse:
"""
- Create a new track domain for open/click tracking.
+ Create a new track domain for open/click tracking for a tenant.
After creation, you must configure a CNAME record pointing to the provided DNS
value before tracking will work.
@@ -88,8 +89,10 @@ def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._post(
- "/tracking",
+ f"/tenants/{tenant_id}/tracking",
body=maybe_transform(
{
"domain_id": domain_id,
@@ -110,6 +113,7 @@ def retrieve(
self,
tracking_id: str,
*,
+ tenant_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,
@@ -118,7 +122,7 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TrackingRetrieveResponse:
"""
- Get details of a specific track domain including DNS configuration
+ Get details of a specific track domain including DNS configuration.
Args:
extra_headers: Send extra headers
@@ -129,10 +133,12 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return self._get(
- f"/tracking/{tracking_id}",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -143,6 +149,7 @@ def update(
self,
tracking_id: str,
*,
+ tenant_id: str,
excluded_click_domains: Optional[str] | Omit = omit,
ssl_enabled: Optional[bool] | Omit = omit,
track_clicks: Optional[bool] | Omit = omit,
@@ -181,10 +188,12 @@ def update(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return self._patch(
- f"/tracking/{tracking_id}",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}",
body=maybe_transform(
{
"excluded_click_domains": excluded_click_domains,
@@ -202,6 +211,7 @@ def update(
def list(
self,
+ tenant_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.
@@ -210,13 +220,24 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TrackingListResponse:
- """List all track domains configured for your server.
+ """List all track domains configured for a tenant.
Track domains enable open and
- click tracking for your emails.
+ click tracking for emails.
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._get(
- "/tracking",
+ f"/tenants/{tenant_id}/tracking",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -227,6 +248,7 @@ def delete(
self,
tracking_id: str,
*,
+ tenant_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,
@@ -248,10 +270,12 @@ def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return self._delete(
- f"/tracking/{tracking_id}",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -262,6 +286,7 @@ def verify(
self,
tracking_id: str,
*,
+ tenant_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,
@@ -284,10 +309,12 @@ def verify(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return self._post(
- f"/tracking/{tracking_id}/verify",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}/verify",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -317,6 +344,7 @@ def with_streaming_response(self) -> AsyncTrackingResourceWithStreamingResponse:
async def create(
self,
+ tenant_id: str,
*,
domain_id: int,
name: str,
@@ -331,7 +359,7 @@ async def create(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TrackingCreateResponse:
"""
- Create a new track domain for open/click tracking.
+ Create a new track domain for open/click tracking for a tenant.
After creation, you must configure a CNAME record pointing to the provided DNS
value before tracking will work.
@@ -355,8 +383,10 @@ async def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._post(
- "/tracking",
+ f"/tenants/{tenant_id}/tracking",
body=await async_maybe_transform(
{
"domain_id": domain_id,
@@ -377,6 +407,7 @@ async def retrieve(
self,
tracking_id: str,
*,
+ tenant_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,
@@ -385,7 +416,7 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TrackingRetrieveResponse:
"""
- Get details of a specific track domain including DNS configuration
+ Get details of a specific track domain including DNS configuration.
Args:
extra_headers: Send extra headers
@@ -396,10 +427,12 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return await self._get(
- f"/tracking/{tracking_id}",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -410,6 +443,7 @@ async def update(
self,
tracking_id: str,
*,
+ tenant_id: str,
excluded_click_domains: Optional[str] | Omit = omit,
ssl_enabled: Optional[bool] | Omit = omit,
track_clicks: Optional[bool] | Omit = omit,
@@ -448,10 +482,12 @@ async def update(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return await self._patch(
- f"/tracking/{tracking_id}",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}",
body=await async_maybe_transform(
{
"excluded_click_domains": excluded_click_domains,
@@ -469,6 +505,7 @@ async def update(
async def list(
self,
+ tenant_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.
@@ -477,13 +514,24 @@ async def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TrackingListResponse:
- """List all track domains configured for your server.
+ """List all track domains configured for a tenant.
Track domains enable open and
- click tracking for your emails.
+ click tracking for emails.
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._get(
- "/tracking",
+ f"/tenants/{tenant_id}/tracking",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -494,6 +542,7 @@ async def delete(
self,
tracking_id: str,
*,
+ tenant_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,
@@ -515,10 +564,12 @@ async def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return await self._delete(
- f"/tracking/{tracking_id}",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -529,6 +580,7 @@ async def verify(
self,
tracking_id: str,
*,
+ tenant_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,
@@ -551,10 +603,12 @@ async def verify(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not tracking_id:
raise ValueError(f"Expected a non-empty value for `tracking_id` but received {tracking_id!r}")
return await self._post(
- f"/tracking/{tracking_id}/verify",
+ f"/tenants/{tenant_id}/tracking/{tracking_id}/verify",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/ark/resources/tenants/usage.py b/src/ark/resources/tenants/usage.py
new file mode 100644
index 0000000..ce82b19
--- /dev/null
+++ b/src/ark/resources/tenants/usage.py
@@ -0,0 +1,402 @@
+# 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 Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import 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.tenants import usage_retrieve_params, usage_retrieve_timeseries_params
+from ...types.tenants.usage_retrieve_response import UsageRetrieveResponse
+from ...types.tenants.usage_retrieve_timeseries_response import UsageRetrieveTimeseriesResponse
+
+__all__ = ["UsageResource", "AsyncUsageResource"]
+
+
+class UsageResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> UsageResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return UsageResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> UsageResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return UsageResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ tenant_id: str,
+ *,
+ period: str | Omit = omit,
+ timezone: 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,
+ ) -> UsageRetrieveResponse:
+ """
+ Returns email sending statistics for a specific tenant over a time period.
+
+ **Use cases:**
+
+ - Display usage dashboard to your customers
+ - Calculate per-tenant billing
+ - Monitor tenant health and delivery rates
+
+ **Period formats:**
+
+ - Shortcuts: `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+ - Month: `2024-01` (full month)
+ - Date range: `2024-01-01..2024-01-31`
+ - Single day: `2024-01-15`
+
+ **Response includes:**
+
+ - `emails` - Counts for sent, delivered, soft_failed, hard_failed, bounced, held
+ - `rates` - Delivery rate and bounce rate as decimals (0.95 = 95%)
+
+ Args:
+ period: Time period for usage data. Defaults to current month.
+
+ **Formats:**
+
+ - Shortcuts: `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+ - Month: `2024-01`
+ - Range: `2024-01-01..2024-01-31`
+ - Day: `2024-01-15`
+
+ timezone: Timezone for period calculations (IANA format). Defaults to 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._get(
+ f"/tenants/{tenant_id}/usage",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "period": period,
+ "timezone": timezone,
+ },
+ usage_retrieve_params.UsageRetrieveParams,
+ ),
+ ),
+ cast_to=UsageRetrieveResponse,
+ )
+
+ def retrieve_timeseries(
+ self,
+ tenant_id: str,
+ *,
+ granularity: Literal["hour", "day", "week", "month"] | Omit = omit,
+ period: str | Omit = omit,
+ timezone: 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,
+ ) -> UsageRetrieveTimeseriesResponse:
+ """
+ Returns time-bucketed email statistics for a specific tenant.
+
+ **Use cases:**
+
+ - Build usage charts and graphs
+ - Identify sending patterns
+ - Detect anomalies in delivery rates
+
+ **Granularity options:**
+
+ - `hour` - Hourly buckets (best for last 7 days)
+ - `day` - Daily buckets (best for last 30-90 days)
+ - `week` - Weekly buckets (best for last 6 months)
+ - `month` - Monthly buckets (best for year-over-year)
+
+ The response includes a data point for each time bucket with all email metrics.
+
+ Args:
+ granularity: Time bucket size for data points
+
+ period: Time period for timeseries data. Defaults to current month.
+
+ timezone: Timezone for period calculations (IANA format). Defaults to 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return self._get(
+ f"/tenants/{tenant_id}/usage/timeseries",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "granularity": granularity,
+ "period": period,
+ "timezone": timezone,
+ },
+ usage_retrieve_timeseries_params.UsageRetrieveTimeseriesParams,
+ ),
+ ),
+ cast_to=UsageRetrieveTimeseriesResponse,
+ )
+
+
+class AsyncUsageResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncUsageResourceWithRawResponse:
+ """
+ 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/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncUsageResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncUsageResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return AsyncUsageResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ tenant_id: str,
+ *,
+ period: str | Omit = omit,
+ timezone: 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,
+ ) -> UsageRetrieveResponse:
+ """
+ Returns email sending statistics for a specific tenant over a time period.
+
+ **Use cases:**
+
+ - Display usage dashboard to your customers
+ - Calculate per-tenant billing
+ - Monitor tenant health and delivery rates
+
+ **Period formats:**
+
+ - Shortcuts: `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+ - Month: `2024-01` (full month)
+ - Date range: `2024-01-01..2024-01-31`
+ - Single day: `2024-01-15`
+
+ **Response includes:**
+
+ - `emails` - Counts for sent, delivered, soft_failed, hard_failed, bounced, held
+ - `rates` - Delivery rate and bounce rate as decimals (0.95 = 95%)
+
+ Args:
+ period: Time period for usage data. Defaults to current month.
+
+ **Formats:**
+
+ - Shortcuts: `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+ - Month: `2024-01`
+ - Range: `2024-01-01..2024-01-31`
+ - Day: `2024-01-15`
+
+ timezone: Timezone for period calculations (IANA format). Defaults to 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return await self._get(
+ f"/tenants/{tenant_id}/usage",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "period": period,
+ "timezone": timezone,
+ },
+ usage_retrieve_params.UsageRetrieveParams,
+ ),
+ ),
+ cast_to=UsageRetrieveResponse,
+ )
+
+ async def retrieve_timeseries(
+ self,
+ tenant_id: str,
+ *,
+ granularity: Literal["hour", "day", "week", "month"] | Omit = omit,
+ period: str | Omit = omit,
+ timezone: 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,
+ ) -> UsageRetrieveTimeseriesResponse:
+ """
+ Returns time-bucketed email statistics for a specific tenant.
+
+ **Use cases:**
+
+ - Build usage charts and graphs
+ - Identify sending patterns
+ - Detect anomalies in delivery rates
+
+ **Granularity options:**
+
+ - `hour` - Hourly buckets (best for last 7 days)
+ - `day` - Daily buckets (best for last 30-90 days)
+ - `week` - Weekly buckets (best for last 6 months)
+ - `month` - Monthly buckets (best for year-over-year)
+
+ The response includes a data point for each time bucket with all email metrics.
+
+ Args:
+ granularity: Time bucket size for data points
+
+ period: Time period for timeseries data. Defaults to current month.
+
+ timezone: Timezone for period calculations (IANA format). Defaults to 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
+ return await self._get(
+ f"/tenants/{tenant_id}/usage/timeseries",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "granularity": granularity,
+ "period": period,
+ "timezone": timezone,
+ },
+ usage_retrieve_timeseries_params.UsageRetrieveTimeseriesParams,
+ ),
+ ),
+ cast_to=UsageRetrieveTimeseriesResponse,
+ )
+
+
+class UsageResourceWithRawResponse:
+ def __init__(self, usage: UsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = to_raw_response_wrapper(
+ usage.retrieve,
+ )
+ self.retrieve_timeseries = to_raw_response_wrapper(
+ usage.retrieve_timeseries,
+ )
+
+
+class AsyncUsageResourceWithRawResponse:
+ def __init__(self, usage: AsyncUsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = async_to_raw_response_wrapper(
+ usage.retrieve,
+ )
+ self.retrieve_timeseries = async_to_raw_response_wrapper(
+ usage.retrieve_timeseries,
+ )
+
+
+class UsageResourceWithStreamingResponse:
+ def __init__(self, usage: UsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = to_streamed_response_wrapper(
+ usage.retrieve,
+ )
+ self.retrieve_timeseries = to_streamed_response_wrapper(
+ usage.retrieve_timeseries,
+ )
+
+
+class AsyncUsageResourceWithStreamingResponse:
+ def __init__(self, usage: AsyncUsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ usage.retrieve,
+ )
+ self.retrieve_timeseries = async_to_streamed_response_wrapper(
+ usage.retrieve_timeseries,
+ )
diff --git a/src/ark/resources/webhooks.py b/src/ark/resources/tenants/webhooks.py
similarity index 87%
rename from src/ark/resources/webhooks.py
rename to src/ark/resources/tenants/webhooks.py
index 97b0202..ee3a683 100644
--- a/src/ark/resources/webhooks.py
+++ b/src/ark/resources/tenants/webhooks.py
@@ -7,27 +7,32 @@
import httpx
-from ..types import webhook_test_params, webhook_create_params, webhook_update_params, webhook_list_deliveries_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
-from .._response import (
+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
+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_list_response import WebhookListResponse
-from ..types.webhook_test_response import WebhookTestResponse
-from ..types.webhook_create_response import WebhookCreateResponse
-from ..types.webhook_delete_response import WebhookDeleteResponse
-from ..types.webhook_update_response import WebhookUpdateResponse
-from ..types.webhook_retrieve_response import WebhookRetrieveResponse
-from ..types.webhook_list_deliveries_response import WebhookListDeliveriesResponse
-from ..types.webhook_replay_delivery_response import WebhookReplayDeliveryResponse
-from ..types.webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse
+from ..._base_client import make_request_options
+from ...types.tenants import (
+ webhook_test_params,
+ webhook_create_params,
+ webhook_update_params,
+ webhook_list_deliveries_params,
+)
+from ...types.tenants.webhook_list_response import WebhookListResponse
+from ...types.tenants.webhook_test_response import WebhookTestResponse
+from ...types.tenants.webhook_create_response import WebhookCreateResponse
+from ...types.tenants.webhook_delete_response import WebhookDeleteResponse
+from ...types.tenants.webhook_update_response import WebhookUpdateResponse
+from ...types.tenants.webhook_retrieve_response import WebhookRetrieveResponse
+from ...types.tenants.webhook_list_deliveries_response import WebhookListDeliveriesResponse
+from ...types.tenants.webhook_replay_delivery_response import WebhookReplayDeliveryResponse
+from ...types.tenants.webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse
__all__ = ["WebhooksResource", "AsyncWebhooksResource"]
@@ -54,6 +59,7 @@ def with_streaming_response(self) -> WebhooksResourceWithStreamingResponse:
def create(
self,
+ tenant_id: str,
*,
name: str,
url: str,
@@ -82,7 +88,7 @@ def create(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> WebhookCreateResponse:
"""
- Create a webhook endpoint to receive email event notifications.
+ Create a webhook endpoint to receive email event notifications for a tenant.
**Available events:**
@@ -124,8 +130,10 @@ def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._post(
- "/webhooks",
+ f"/tenants/{tenant_id}/webhooks",
body=maybe_transform(
{
"name": name,
@@ -146,6 +154,7 @@ def retrieve(
self,
webhook_id: str,
*,
+ tenant_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,
@@ -165,10 +174,12 @@ def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return self._get(
- f"/webhooks/{webhook_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -179,6 +190,7 @@ def update(
self,
webhook_id: str,
*,
+ tenant_id: str,
all_events: Optional[bool] | Omit = omit,
enabled: Optional[bool] | Omit = omit,
events: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -203,10 +215,12 @@ def update(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return self._patch(
- f"/webhooks/{webhook_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}",
body=maybe_transform(
{
"all_events": all_events,
@@ -225,6 +239,7 @@ def update(
def list(
self,
+ tenant_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.
@@ -233,9 +248,22 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> WebhookListResponse:
- """Get all configured webhook endpoints"""
+ """
+ Get all configured webhook endpoints for a tenant.
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return self._get(
- "/webhooks",
+ f"/tenants/{tenant_id}/webhooks",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -246,6 +274,7 @@ def delete(
self,
webhook_id: str,
*,
+ tenant_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,
@@ -265,10 +294,12 @@ def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return self._delete(
- f"/webhooks/{webhook_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -279,6 +310,7 @@ def list_deliveries(
self,
webhook_id: str,
*,
+ tenant_id: str,
after: int | Omit = omit,
before: int | Omit = omit,
event: Literal[
@@ -341,10 +373,12 @@ def list_deliveries(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return self._get(
- f"/webhooks/{webhook_id}/deliveries",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries",
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -369,6 +403,7 @@ def replay_delivery(
self,
delivery_id: str,
*,
+ tenant_id: str,
webhook_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.
@@ -404,12 +439,14 @@ def replay_delivery(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
if not delivery_id:
raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
return self._post(
- f"/webhooks/{webhook_id}/deliveries/{delivery_id}/replay",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/replay",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -420,6 +457,7 @@ def retrieve_delivery(
self,
delivery_id: str,
*,
+ tenant_id: str,
webhook_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.
@@ -449,12 +487,14 @@ def retrieve_delivery(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
if not delivery_id:
raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
return self._get(
- f"/webhooks/{webhook_id}/deliveries/{delivery_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries/{delivery_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -465,6 +505,7 @@ def test(
self,
webhook_id: str,
*,
+ tenant_id: str,
event: Literal[
"MessageSent",
"MessageDelayed",
@@ -507,10 +548,12 @@ def test(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return self._post(
- f"/webhooks/{webhook_id}/test",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/test",
body=maybe_transform({"event": event}, webhook_test_params.WebhookTestParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -541,6 +584,7 @@ def with_streaming_response(self) -> AsyncWebhooksResourceWithStreamingResponse:
async def create(
self,
+ tenant_id: str,
*,
name: str,
url: str,
@@ -569,7 +613,7 @@ async def create(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> WebhookCreateResponse:
"""
- Create a webhook endpoint to receive email event notifications.
+ Create a webhook endpoint to receive email event notifications for a tenant.
**Available events:**
@@ -611,8 +655,10 @@ async def create(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._post(
- "/webhooks",
+ f"/tenants/{tenant_id}/webhooks",
body=await async_maybe_transform(
{
"name": name,
@@ -633,6 +679,7 @@ async def retrieve(
self,
webhook_id: str,
*,
+ tenant_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,
@@ -652,10 +699,12 @@ async def retrieve(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return await self._get(
- f"/webhooks/{webhook_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -666,6 +715,7 @@ async def update(
self,
webhook_id: str,
*,
+ tenant_id: str,
all_events: Optional[bool] | Omit = omit,
enabled: Optional[bool] | Omit = omit,
events: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -690,10 +740,12 @@ async def update(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return await self._patch(
- f"/webhooks/{webhook_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}",
body=await async_maybe_transform(
{
"all_events": all_events,
@@ -712,6 +764,7 @@ async def update(
async def list(
self,
+ tenant_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.
@@ -720,9 +773,22 @@ async def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> WebhookListResponse:
- """Get all configured webhook endpoints"""
+ """
+ Get all configured webhook endpoints for a tenant.
+
+ 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 tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
return await self._get(
- "/webhooks",
+ f"/tenants/{tenant_id}/webhooks",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -733,6 +799,7 @@ async def delete(
self,
webhook_id: str,
*,
+ tenant_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,
@@ -752,10 +819,12 @@ async def delete(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return await self._delete(
- f"/webhooks/{webhook_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -766,6 +835,7 @@ async def list_deliveries(
self,
webhook_id: str,
*,
+ tenant_id: str,
after: int | Omit = omit,
before: int | Omit = omit,
event: Literal[
@@ -828,10 +898,12 @@ async def list_deliveries(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return await self._get(
- f"/webhooks/{webhook_id}/deliveries",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries",
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -856,6 +928,7 @@ async def replay_delivery(
self,
delivery_id: str,
*,
+ tenant_id: str,
webhook_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.
@@ -891,12 +964,14 @@ async def replay_delivery(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
if not delivery_id:
raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
return await self._post(
- f"/webhooks/{webhook_id}/deliveries/{delivery_id}/replay",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/replay",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -907,6 +982,7 @@ async def retrieve_delivery(
self,
delivery_id: str,
*,
+ tenant_id: str,
webhook_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.
@@ -936,12 +1012,14 @@ async def retrieve_delivery(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
if not delivery_id:
raise ValueError(f"Expected a non-empty value for `delivery_id` but received {delivery_id!r}")
return await self._get(
- f"/webhooks/{webhook_id}/deliveries/{delivery_id}",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/deliveries/{delivery_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -952,6 +1030,7 @@ async def test(
self,
webhook_id: str,
*,
+ tenant_id: str,
event: Literal[
"MessageSent",
"MessageDelayed",
@@ -994,10 +1073,12 @@ async def test(
timeout: Override the client-level default timeout for this request, in seconds
"""
+ if not tenant_id:
+ raise ValueError(f"Expected a non-empty value for `tenant_id` but received {tenant_id!r}")
if not webhook_id:
raise ValueError(f"Expected a non-empty value for `webhook_id` but received {webhook_id!r}")
return await self._post(
- f"/webhooks/{webhook_id}/test",
+ f"/tenants/{tenant_id}/webhooks/{webhook_id}/test",
body=await async_maybe_transform({"event": event}, webhook_test_params.WebhookTestParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
diff --git a/src/ark/resources/usage.py b/src/ark/resources/usage.py
index cb9004f..9a38220 100644
--- a/src/ark/resources/usage.py
+++ b/src/ark/resources/usage.py
@@ -2,9 +2,13 @@
from __future__ import annotations
+from typing_extensions import Literal
+
import httpx
-from .._types import Body, Query, Headers, NotGiven, not_given
+from ..types import usage_export_params, usage_retrieve_params, usage_list_tenants_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -13,8 +17,11 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from .._base_client import make_request_options
-from ..types.usage_retrieve_response import UsageRetrieveResponse
+from ..pagination import SyncPageNumberPagination, AsyncPageNumberPagination
+from .._base_client import AsyncPaginator, make_request_options
+from ..types.org_usage_summary import OrgUsageSummary
+from ..types.tenant_usage_item import TenantUsageItem
+from ..types.usage_export_response import UsageExportResponse
__all__ = ["UsageResource", "AsyncUsageResource"]
@@ -42,41 +49,271 @@ def with_streaming_response(self) -> UsageResourceWithStreamingResponse:
def retrieve(
self,
*,
+ period: str | Omit = omit,
+ timezone: 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,
- ) -> UsageRetrieveResponse:
- """
- Returns current usage and limit information for your account.
+ ) -> OrgUsageSummary:
+ """Returns aggregated email sending statistics for your entire organization.
+
+ For
+ per-tenant breakdown, use `GET /usage/tenants`.
- This endpoint is designed for:
+ **Use cases:**
- - **AI agents/MCP servers:** Check constraints before planning batch operations
- - **Monitoring dashboards:** Display current usage status
- - **Rate limit awareness:** Know remaining capacity before making requests
+ - Platform dashboards showing org-wide metrics
+ - Quick health check on overall sending
+ - Monitoring total volume and delivery rates
**Response includes:**
- - `rateLimit` - API request rate limit (requests per second)
- - `sendLimit` - Email sending limit (emails per hour)
- - `billing` - Credit balance and auto-recharge configuration
+ - `emails` - Aggregated email counts across all tenants
+ - `rates` - Overall delivery and bounce rates
+ - `tenants` - Tenant count summary (total, active, with activity)
+
+ **Related endpoints:**
+
+ - `GET /usage/tenants` - Paginated usage per tenant
+ - `GET /usage/export` - Export usage data for billing
+ - `GET /tenants/{tenantId}/usage` - Single tenant usage details
+ - `GET /limits` - Rate limits and send limits
+
+ Args:
+ period: Time period for usage data.
+
+ **Shortcuts:** `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+
+ timezone: Timezone for period calculations (IANA format)
+
+ extra_headers: Send extra headers
- **Notes:**
+ extra_query: Add additional query parameters to the request
- - This request counts against your rate limit
- - `sendLimit` may be null if Postal is temporarily unavailable
- - `billing` is null if billing is not configured
- - Send limit resets at the top of each hour
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
"""
return self._get(
"/usage",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "period": period,
+ "timezone": timezone,
+ },
+ usage_retrieve_params.UsageRetrieveParams,
+ ),
),
- cast_to=UsageRetrieveResponse,
+ cast_to=OrgUsageSummary,
+ )
+
+ def export(
+ self,
+ *,
+ format: Literal["csv", "jsonl"] | Omit = omit,
+ min_sent: int | Omit = omit,
+ period: str | Omit = omit,
+ status: Literal["active", "suspended", "archived"] | Omit = omit,
+ timezone: 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,
+ ) -> UsageExportResponse:
+ """Export email usage data for all tenants in CSV or JSON Lines format.
+
+ Designed
+ for billing system integration, data warehousing, and analytics.
+
+ **Jobs to be done:**
+
+ - Import usage data into billing systems (Stripe, Chargebee, etc.)
+ - Load into data warehouses (Snowflake, BigQuery, etc.)
+ - Process in spreadsheets (Excel, Google Sheets)
+ - Feed into BI tools (Looker, Metabase, etc.)
+
+ **Export formats:**
+
+ - `csv` - UTF-8 with BOM for Excel compatibility (default)
+ - `jsonl` - JSON Lines (one JSON object per line, streamable)
+
+ **CSV columns:** `tenant_id`, `tenant_name`, `external_id`, `status`, `sent`,
+ `delivered`, `soft_failed`, `hard_failed`, `bounced`, `held`, `delivery_rate`,
+ `bounce_rate`, `period_start`, `period_end`
+
+ **Response headers:**
+
+ - `Content-Disposition` - Filename for download
+ - `Content-Type` - `text/csv` or `application/x-ndjson`
+
+ Args:
+ format: Export format
+
+ min_sent: Only include tenants with at least this many emails sent
+
+ period: Time period for export.
+
+ **Shortcuts:** `this_month`, `last_month`, `last_30_days`, etc.
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+
+ status: Filter by tenant status
+
+ timezone: Timezone for period calculations (IANA format)
+
+ 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(
+ "/usage/export",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "format": format,
+ "min_sent": min_sent,
+ "period": period,
+ "status": status,
+ "timezone": timezone,
+ },
+ usage_export_params.UsageExportParams,
+ ),
+ ),
+ cast_to=UsageExportResponse,
+ )
+
+ def list_tenants(
+ self,
+ *,
+ min_sent: int | Omit = omit,
+ page: int | Omit = omit,
+ period: str | Omit = omit,
+ per_page: int | Omit = omit,
+ sort: Literal[
+ "sent",
+ "-sent",
+ "delivered",
+ "-delivered",
+ "bounce_rate",
+ "-bounce_rate",
+ "delivery_rate",
+ "-delivery_rate",
+ "tenant_name",
+ "-tenant_name",
+ ]
+ | Omit = omit,
+ status: Literal["active", "suspended", "archived"] | Omit = omit,
+ timezone: 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,
+ ) -> SyncPageNumberPagination[TenantUsageItem]:
+ """Returns email usage statistics for all tenants in your organization.
+
+ Results are
+ paginated with page-based navigation.
+
+ **Jobs to be done:**
+
+ - Generate monthly billing invoices per tenant
+ - Build admin dashboards showing all customer usage
+ - Identify high-volume or problematic tenants
+ - Track usage against plan limits
+
+ **Sorting options:**
+
+ - `sent`, `-sent` - Sort by emails sent (ascending/descending)
+ - `delivered`, `-delivered` - Sort by emails delivered
+ - `bounce_rate`, `-bounce_rate` - Sort by bounce rate
+ - `tenant_name`, `-tenant_name` - Sort alphabetically by tenant name
+
+ **Filtering:**
+
+ - `status` - Filter by tenant status (active, suspended, archived)
+ - `minSent` - Only include tenants with at least N emails sent
+
+ **Auto-pagination:** SDKs support iterating over all pages automatically.
+
+ Args:
+ min_sent: Only include tenants with at least this many emails sent
+
+ page: Page number (1-indexed)
+
+ period: Time period for usage data. Defaults to current month.
+
+ **Shortcuts:** `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+
+ per_page: Results per page (max 100)
+
+ sort: Sort order for results. Prefix with `-` for descending order.
+
+ status: Filter by tenant status
+
+ timezone: Timezone for period calculations (IANA format). Defaults to 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
+ """
+ return self._get_api_list(
+ "/usage/tenants",
+ page=SyncPageNumberPagination[TenantUsageItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "min_sent": min_sent,
+ "page": page,
+ "period": period,
+ "per_page": per_page,
+ "sort": sort,
+ "status": status,
+ "timezone": timezone,
+ },
+ usage_list_tenants_params.UsageListTenantsParams,
+ ),
+ ),
+ model=TenantUsageItem,
)
@@ -103,41 +340,271 @@ def with_streaming_response(self) -> AsyncUsageResourceWithStreamingResponse:
async def retrieve(
self,
*,
+ period: str | Omit = omit,
+ timezone: 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,
- ) -> UsageRetrieveResponse:
- """
- Returns current usage and limit information for your account.
+ ) -> OrgUsageSummary:
+ """Returns aggregated email sending statistics for your entire organization.
+
+ For
+ per-tenant breakdown, use `GET /usage/tenants`.
- This endpoint is designed for:
+ **Use cases:**
- - **AI agents/MCP servers:** Check constraints before planning batch operations
- - **Monitoring dashboards:** Display current usage status
- - **Rate limit awareness:** Know remaining capacity before making requests
+ - Platform dashboards showing org-wide metrics
+ - Quick health check on overall sending
+ - Monitoring total volume and delivery rates
**Response includes:**
- - `rateLimit` - API request rate limit (requests per second)
- - `sendLimit` - Email sending limit (emails per hour)
- - `billing` - Credit balance and auto-recharge configuration
+ - `emails` - Aggregated email counts across all tenants
+ - `rates` - Overall delivery and bounce rates
+ - `tenants` - Tenant count summary (total, active, with activity)
+
+ **Related endpoints:**
+
+ - `GET /usage/tenants` - Paginated usage per tenant
+ - `GET /usage/export` - Export usage data for billing
+ - `GET /tenants/{tenantId}/usage` - Single tenant usage details
+ - `GET /limits` - Rate limits and send limits
- **Notes:**
+ Args:
+ period: Time period for usage data.
- - This request counts against your rate limit
- - `sendLimit` may be null if Postal is temporarily unavailable
- - `billing` is null if billing is not configured
- - Send limit resets at the top of each hour
+ **Shortcuts:** `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+
+ timezone: Timezone for period calculations (IANA format)
+
+ 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(
"/usage",
options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "period": period,
+ "timezone": timezone,
+ },
+ usage_retrieve_params.UsageRetrieveParams,
+ ),
+ ),
+ cast_to=OrgUsageSummary,
+ )
+
+ async def export(
+ self,
+ *,
+ format: Literal["csv", "jsonl"] | Omit = omit,
+ min_sent: int | Omit = omit,
+ period: str | Omit = omit,
+ status: Literal["active", "suspended", "archived"] | Omit = omit,
+ timezone: 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,
+ ) -> UsageExportResponse:
+ """Export email usage data for all tenants in CSV or JSON Lines format.
+
+ Designed
+ for billing system integration, data warehousing, and analytics.
+
+ **Jobs to be done:**
+
+ - Import usage data into billing systems (Stripe, Chargebee, etc.)
+ - Load into data warehouses (Snowflake, BigQuery, etc.)
+ - Process in spreadsheets (Excel, Google Sheets)
+ - Feed into BI tools (Looker, Metabase, etc.)
+
+ **Export formats:**
+
+ - `csv` - UTF-8 with BOM for Excel compatibility (default)
+ - `jsonl` - JSON Lines (one JSON object per line, streamable)
+
+ **CSV columns:** `tenant_id`, `tenant_name`, `external_id`, `status`, `sent`,
+ `delivered`, `soft_failed`, `hard_failed`, `bounced`, `held`, `delivery_rate`,
+ `bounce_rate`, `period_start`, `period_end`
+
+ **Response headers:**
+
+ - `Content-Disposition` - Filename for download
+ - `Content-Type` - `text/csv` or `application/x-ndjson`
+
+ Args:
+ format: Export format
+
+ min_sent: Only include tenants with at least this many emails sent
+
+ period: Time period for export.
+
+ **Shortcuts:** `this_month`, `last_month`, `last_30_days`, etc.
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+
+ status: Filter by tenant status
+
+ timezone: Timezone for period calculations (IANA format)
+
+ 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(
+ "/usage/export",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "format": format,
+ "min_sent": min_sent,
+ "period": period,
+ "status": status,
+ "timezone": timezone,
+ },
+ usage_export_params.UsageExportParams,
+ ),
+ ),
+ cast_to=UsageExportResponse,
+ )
+
+ def list_tenants(
+ self,
+ *,
+ min_sent: int | Omit = omit,
+ page: int | Omit = omit,
+ period: str | Omit = omit,
+ per_page: int | Omit = omit,
+ sort: Literal[
+ "sent",
+ "-sent",
+ "delivered",
+ "-delivered",
+ "bounce_rate",
+ "-bounce_rate",
+ "delivery_rate",
+ "-delivery_rate",
+ "tenant_name",
+ "-tenant_name",
+ ]
+ | Omit = omit,
+ status: Literal["active", "suspended", "archived"] | Omit = omit,
+ timezone: 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[TenantUsageItem, AsyncPageNumberPagination[TenantUsageItem]]:
+ """Returns email usage statistics for all tenants in your organization.
+
+ Results are
+ paginated with page-based navigation.
+
+ **Jobs to be done:**
+
+ - Generate monthly billing invoices per tenant
+ - Build admin dashboards showing all customer usage
+ - Identify high-volume or problematic tenants
+ - Track usage against plan limits
+
+ **Sorting options:**
+
+ - `sent`, `-sent` - Sort by emails sent (ascending/descending)
+ - `delivered`, `-delivered` - Sort by emails delivered
+ - `bounce_rate`, `-bounce_rate` - Sort by bounce rate
+ - `tenant_name`, `-tenant_name` - Sort alphabetically by tenant name
+
+ **Filtering:**
+
+ - `status` - Filter by tenant status (active, suspended, archived)
+ - `minSent` - Only include tenants with at least N emails sent
+
+ **Auto-pagination:** SDKs support iterating over all pages automatically.
+
+ Args:
+ min_sent: Only include tenants with at least this many emails sent
+
+ page: Page number (1-indexed)
+
+ period: Time period for usage data. Defaults to current month.
+
+ **Shortcuts:** `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+
+ per_page: Results per page (max 100)
+
+ sort: Sort order for results. Prefix with `-` for descending order.
+
+ status: Filter by tenant status
+
+ timezone: Timezone for period calculations (IANA format). Defaults to 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
+ """
+ return self._get_api_list(
+ "/usage/tenants",
+ page=AsyncPageNumberPagination[TenantUsageItem],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "min_sent": min_sent,
+ "page": page,
+ "period": period,
+ "per_page": per_page,
+ "sort": sort,
+ "status": status,
+ "timezone": timezone,
+ },
+ usage_list_tenants_params.UsageListTenantsParams,
+ ),
),
- cast_to=UsageRetrieveResponse,
+ model=TenantUsageItem,
)
@@ -148,6 +615,12 @@ def __init__(self, usage: UsageResource) -> None:
self.retrieve = to_raw_response_wrapper(
usage.retrieve,
)
+ self.export = to_raw_response_wrapper(
+ usage.export,
+ )
+ self.list_tenants = to_raw_response_wrapper(
+ usage.list_tenants,
+ )
class AsyncUsageResourceWithRawResponse:
@@ -157,6 +630,12 @@ def __init__(self, usage: AsyncUsageResource) -> None:
self.retrieve = async_to_raw_response_wrapper(
usage.retrieve,
)
+ self.export = async_to_raw_response_wrapper(
+ usage.export,
+ )
+ self.list_tenants = async_to_raw_response_wrapper(
+ usage.list_tenants,
+ )
class UsageResourceWithStreamingResponse:
@@ -166,6 +645,12 @@ def __init__(self, usage: UsageResource) -> None:
self.retrieve = to_streamed_response_wrapper(
usage.retrieve,
)
+ self.export = to_streamed_response_wrapper(
+ usage.export,
+ )
+ self.list_tenants = to_streamed_response_wrapper(
+ usage.list_tenants,
+ )
class AsyncUsageResourceWithStreamingResponse:
@@ -175,3 +660,9 @@ def __init__(self, usage: AsyncUsageResource) -> None:
self.retrieve = async_to_streamed_response_wrapper(
usage.retrieve,
)
+ self.export = async_to_streamed_response_wrapper(
+ usage.export,
+ )
+ self.list_tenants = async_to_streamed_response_wrapper(
+ usage.list_tenants,
+ )
diff --git a/src/ark/types/__init__.py b/src/ark/types/__init__.py
index 23e9cb2..8d422a6 100644
--- a/src/ark/types/__init__.py
+++ b/src/ark/types/__init__.py
@@ -5,63 +5,36 @@
from .shared import APIMeta as APIMeta
from .tenant import Tenant as Tenant
from .log_entry import LogEntry as LogEntry
-from .dns_record import DNSRecord as DNSRecord
-from .track_domain import TrackDomain as TrackDomain
+from .email_rates import EmailRates as EmailRates
+from .limits_data import LimitsData as LimitsData
+from .email_counts import EmailCounts as EmailCounts
+from .usage_period import UsagePeriod as UsagePeriod
from .log_list_params import LogListParams as LogListParams
from .log_entry_detail import LogEntryDetail as LogEntryDetail
from .email_list_params import EmailListParams as EmailListParams
from .email_send_params import EmailSendParams as EmailSendParams
+from .org_usage_summary import OrgUsageSummary as OrgUsageSummary
+from .tenant_usage_item import TenantUsageItem as TenantUsageItem
from .tenant_list_params import TenantListParams as TenantListParams
from .email_list_response import EmailListResponse as EmailListResponse
from .email_send_response import EmailSendResponse as EmailSendResponse
-from .webhook_test_params import WebhookTestParams as WebhookTestParams
-from .domain_create_params import DomainCreateParams as DomainCreateParams
-from .domain_list_response import DomainListResponse as DomainListResponse
+from .usage_export_params import UsageExportParams as UsageExportParams
from .email_retry_response import EmailRetryResponse as EmailRetryResponse
from .tenant_create_params import TenantCreateParams as TenantCreateParams
from .tenant_update_params import TenantUpdateParams as TenantUpdateParams
from .email_retrieve_params import EmailRetrieveParams as EmailRetrieveParams
from .email_send_raw_params import EmailSendRawParams as EmailSendRawParams
from .log_retrieve_response import LogRetrieveResponse as LogRetrieveResponse
-from .webhook_create_params import WebhookCreateParams as WebhookCreateParams
-from .webhook_list_response import WebhookListResponse as WebhookListResponse
-from .webhook_test_response import WebhookTestResponse as WebhookTestResponse
-from .webhook_update_params import WebhookUpdateParams as WebhookUpdateParams
-from .domain_create_response import DomainCreateResponse as DomainCreateResponse
-from .domain_delete_response import DomainDeleteResponse as DomainDeleteResponse
-from .domain_verify_response import DomainVerifyResponse as DomainVerifyResponse
+from .usage_export_response import UsageExportResponse as UsageExportResponse
+from .usage_retrieve_params import UsageRetrieveParams as UsageRetrieveParams
from .tenant_create_response import TenantCreateResponse as TenantCreateResponse
from .tenant_delete_response import TenantDeleteResponse as TenantDeleteResponse
from .tenant_update_response import TenantUpdateResponse as TenantUpdateResponse
-from .tracking_create_params import TrackingCreateParams as TrackingCreateParams
-from .tracking_list_response import TrackingListResponse as TrackingListResponse
-from .tracking_update_params import TrackingUpdateParams as TrackingUpdateParams
from .email_retrieve_response import EmailRetrieveResponse as EmailRetrieveResponse
from .email_send_batch_params import EmailSendBatchParams as EmailSendBatchParams
from .email_send_raw_response import EmailSendRawResponse as EmailSendRawResponse
-from .suppression_list_params import SuppressionListParams as SuppressionListParams
-from .usage_retrieve_response import UsageRetrieveResponse as UsageRetrieveResponse
-from .webhook_create_response import WebhookCreateResponse as WebhookCreateResponse
-from .webhook_delete_response import WebhookDeleteResponse as WebhookDeleteResponse
-from .webhook_update_response import WebhookUpdateResponse as WebhookUpdateResponse
-from .domain_retrieve_response import DomainRetrieveResponse as DomainRetrieveResponse
+from .limit_retrieve_response import LimitRetrieveResponse as LimitRetrieveResponse
from .tenant_retrieve_response import TenantRetrieveResponse as TenantRetrieveResponse
-from .tracking_create_response import TrackingCreateResponse as TrackingCreateResponse
-from .tracking_delete_response import TrackingDeleteResponse as TrackingDeleteResponse
-from .tracking_update_response import TrackingUpdateResponse as TrackingUpdateResponse
-from .tracking_verify_response import TrackingVerifyResponse as TrackingVerifyResponse
from .email_send_batch_response import EmailSendBatchResponse as EmailSendBatchResponse
-from .suppression_create_params import SuppressionCreateParams as SuppressionCreateParams
-from .suppression_list_response import SuppressionListResponse as SuppressionListResponse
-from .webhook_retrieve_response import WebhookRetrieveResponse as WebhookRetrieveResponse
-from .tracking_retrieve_response import TrackingRetrieveResponse as TrackingRetrieveResponse
-from .suppression_create_response import SuppressionCreateResponse as SuppressionCreateResponse
-from .suppression_delete_response import SuppressionDeleteResponse as SuppressionDeleteResponse
-from .suppression_retrieve_response import SuppressionRetrieveResponse as SuppressionRetrieveResponse
-from .suppression_bulk_create_params import SuppressionBulkCreateParams as SuppressionBulkCreateParams
-from .webhook_list_deliveries_params import WebhookListDeliveriesParams as WebhookListDeliveriesParams
-from .suppression_bulk_create_response import SuppressionBulkCreateResponse as SuppressionBulkCreateResponse
-from .webhook_list_deliveries_response import WebhookListDeliveriesResponse as WebhookListDeliveriesResponse
-from .webhook_replay_delivery_response import WebhookReplayDeliveryResponse as WebhookReplayDeliveryResponse
+from .usage_list_tenants_params import UsageListTenantsParams as UsageListTenantsParams
from .email_retrieve_deliveries_response import EmailRetrieveDeliveriesResponse as EmailRetrieveDeliveriesResponse
-from .webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse as WebhookRetrieveDeliveryResponse
diff --git a/src/ark/types/email_counts.py b/src/ark/types/email_counts.py
new file mode 100644
index 0000000..ffd5cf6
--- /dev/null
+++ b/src/ark/types/email_counts.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["EmailCounts"]
+
+
+class EmailCounts(BaseModel):
+ """Email delivery counts"""
+
+ bounced: int
+ """Emails that bounced"""
+
+ delivered: int
+ """Emails successfully delivered"""
+
+ hard_failed: int
+ """Emails that hard-failed (permanent failures)"""
+
+ held: int
+ """Emails currently held for review"""
+
+ sent: int
+ """Total emails sent"""
+
+ soft_failed: int
+ """Emails that soft-failed (temporary failures, may be retried)"""
diff --git a/src/ark/types/email_rates.py b/src/ark/types/email_rates.py
new file mode 100644
index 0000000..79450da
--- /dev/null
+++ b/src/ark/types/email_rates.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["EmailRates"]
+
+
+class EmailRates(BaseModel):
+ """Email delivery rates (as decimals, e.g., 0.95 = 95%)"""
+
+ bounce_rate: float
+ """Percentage of sent emails that bounced (0-1)"""
+
+ delivery_rate: float
+ """Percentage of sent emails that were delivered (0-1)"""
diff --git a/src/ark/types/email_retrieve_deliveries_response.py b/src/ark/types/email_retrieve_deliveries_response.py
index a591b9d..a923d98 100644
--- a/src/ark/types/email_retrieve_deliveries_response.py
+++ b/src/ark/types/email_retrieve_deliveries_response.py
@@ -142,7 +142,7 @@ class Data(BaseModel):
can_retry_manually: bool = FieldInfo(alias="canRetryManually")
"""
- Whether the message can be manually retried via `POST /emails/{id}/retry`.
+ Whether the message can be manually retried via `POST /emails/{emailId}/retry`.
`true` when the raw message content is still available (not expired). Messages
older than the retention period cannot be retried.
"""
diff --git a/src/ark/types/limit_retrieve_response.py b/src/ark/types/limit_retrieve_response.py
new file mode 100644
index 0000000..06bd330
--- /dev/null
+++ b/src/ark/types/limit_retrieve_response.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .._models import BaseModel
+from .limits_data import LimitsData
+from .shared.api_meta import APIMeta
+
+__all__ = ["LimitRetrieveResponse"]
+
+
+class LimitRetrieveResponse(BaseModel):
+ """Account rate limits and send limits response"""
+
+ data: LimitsData
+ """Current usage and limit information"""
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/usage_retrieve_response.py b/src/ark/types/limits_data.py
similarity index 73%
rename from src/ark/types/usage_retrieve_response.py
rename to src/ark/types/limits_data.py
index 86daa16..015054c 100644
--- a/src/ark/types/usage_retrieve_response.py
+++ b/src/ark/types/limits_data.py
@@ -7,12 +7,11 @@
from pydantic import Field as FieldInfo
from .._models import BaseModel
-from .shared.api_meta import APIMeta
-__all__ = ["UsageRetrieveResponse", "Data", "DataBilling", "DataBillingAutoRecharge", "DataRateLimit", "DataSendLimit"]
+__all__ = ["LimitsData", "Billing", "BillingAutoRecharge", "RateLimit", "SendLimit"]
-class DataBillingAutoRecharge(BaseModel):
+class BillingAutoRecharge(BaseModel):
"""Auto-recharge configuration"""
amount: str
@@ -25,10 +24,10 @@ class DataBillingAutoRecharge(BaseModel):
"""Balance threshold that triggers recharge"""
-class DataBilling(BaseModel):
+class Billing(BaseModel):
"""Billing and credit information"""
- auto_recharge: DataBillingAutoRecharge = FieldInfo(alias="autoRecharge")
+ auto_recharge: BillingAutoRecharge = FieldInfo(alias="autoRecharge")
"""Auto-recharge configuration"""
credit_balance: str = FieldInfo(alias="creditBalance")
@@ -41,7 +40,7 @@ class DataBilling(BaseModel):
"""Whether a payment method is configured"""
-class DataRateLimit(BaseModel):
+class RateLimit(BaseModel):
"""API rate limit status"""
limit: int
@@ -57,7 +56,7 @@ class DataRateLimit(BaseModel):
"""Unix timestamp when the limit resets"""
-class DataSendLimit(BaseModel):
+class SendLimit(BaseModel):
"""Email send limit status (hourly cap)"""
approaching: bool
@@ -85,25 +84,14 @@ class DataSendLimit(BaseModel):
"""Emails sent in current period"""
-class Data(BaseModel):
+class LimitsData(BaseModel):
"""Current usage and limit information"""
- billing: Optional[DataBilling] = None
+ billing: Optional[Billing] = None
"""Billing and credit information"""
- rate_limit: DataRateLimit = FieldInfo(alias="rateLimit")
+ rate_limit: RateLimit = FieldInfo(alias="rateLimit")
"""API rate limit status"""
- send_limit: Optional[DataSendLimit] = FieldInfo(alias="sendLimit", default=None)
+ send_limit: Optional[SendLimit] = FieldInfo(alias="sendLimit", default=None)
"""Email send limit status (hourly cap)"""
-
-
-class UsageRetrieveResponse(BaseModel):
- """Account usage and limits response"""
-
- data: Data
- """Current usage and limit information"""
-
- meta: APIMeta
-
- success: Literal[True]
diff --git a/src/ark/types/org_usage_summary.py b/src/ark/types/org_usage_summary.py
new file mode 100644
index 0000000..ef35604
--- /dev/null
+++ b/src/ark/types/org_usage_summary.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .email_rates import EmailRates
+from .email_counts import EmailCounts
+from .usage_period import UsagePeriod
+from .shared.api_meta import APIMeta
+
+__all__ = ["OrgUsageSummary", "Data", "DataTenants"]
+
+
+class DataTenants(BaseModel):
+ active: int
+ """Number of active tenants"""
+
+ total: int
+ """Total number of tenants"""
+
+ with_activity: int = FieldInfo(alias="withActivity")
+ """Number of tenants with sending activity"""
+
+
+class Data(BaseModel):
+ emails: EmailCounts
+ """Email delivery counts"""
+
+ period: UsagePeriod
+ """Time period for usage data"""
+
+ rates: EmailRates
+ """Email delivery rates (as decimals, e.g., 0.95 = 95%)"""
+
+ tenants: DataTenants
+
+
+class OrgUsageSummary(BaseModel):
+ """Org-wide usage summary response"""
+
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/platform/__init__.py b/src/ark/types/platform/__init__.py
new file mode 100644
index 0000000..6e70e72
--- /dev/null
+++ b/src/ark/types/platform/__init__.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .webhook_test_params import WebhookTestParams as WebhookTestParams
+from .webhook_create_params import WebhookCreateParams as WebhookCreateParams
+from .webhook_list_response import WebhookListResponse as WebhookListResponse
+from .webhook_test_response import WebhookTestResponse as WebhookTestResponse
+from .webhook_update_params import WebhookUpdateParams as WebhookUpdateParams
+from .webhook_create_response import WebhookCreateResponse as WebhookCreateResponse
+from .webhook_delete_response import WebhookDeleteResponse as WebhookDeleteResponse
+from .webhook_update_response import WebhookUpdateResponse as WebhookUpdateResponse
+from .webhook_retrieve_response import WebhookRetrieveResponse as WebhookRetrieveResponse
+from .webhook_list_deliveries_params import WebhookListDeliveriesParams as WebhookListDeliveriesParams
+from .webhook_list_deliveries_response import WebhookListDeliveriesResponse as WebhookListDeliveriesResponse
+from .webhook_replay_delivery_response import WebhookReplayDeliveryResponse as WebhookReplayDeliveryResponse
+from .webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse as WebhookRetrieveDeliveryResponse
diff --git a/src/ark/types/platform/webhook_create_params.py b/src/ark/types/platform/webhook_create_params.py
new file mode 100644
index 0000000..574222f
--- /dev/null
+++ b/src/ark/types/platform/webhook_create_params.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["WebhookCreateParams"]
+
+
+class WebhookCreateParams(TypedDict, total=False):
+ name: Required[str]
+ """Display name for the webhook"""
+
+ url: Required[str]
+ """Webhook endpoint URL (must be HTTPS)"""
+
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ """Events to subscribe to. Empty array means all events."""
diff --git a/src/ark/types/platform/webhook_create_response.py b/src/ark/types/platform/webhook_create_response.py
new file mode 100644
index 0000000..37ca2ca
--- /dev/null
+++ b/src/ark/types/platform/webhook_create_response.py
@@ -0,0 +1,52 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookCreateResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """Platform webhook ID"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ enabled: bool
+ """Whether the webhook is active"""
+
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ """Subscribed events (empty = all events)"""
+
+ name: str
+ """Webhook name for identification"""
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ url: str
+ """Webhook endpoint URL"""
+
+
+class WebhookCreateResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/webhook_delete_response.py b/src/ark/types/platform/webhook_delete_response.py
similarity index 81%
rename from src/ark/types/webhook_delete_response.py
rename to src/ark/types/platform/webhook_delete_response.py
index 63e9ff8..aee22ba 100644
--- a/src/ark/types/webhook_delete_response.py
+++ b/src/ark/types/platform/webhook_delete_response.py
@@ -2,8 +2,8 @@
from typing_extensions import Literal
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookDeleteResponse", "Data"]
diff --git a/src/ark/types/platform/webhook_list_deliveries_params.py b/src/ark/types/platform/webhook_list_deliveries_params.py
new file mode 100644
index 0000000..3d00ef0
--- /dev/null
+++ b/src/ark/types/platform/webhook_list_deliveries_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_extensions import Literal, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["WebhookListDeliveriesParams"]
+
+
+class WebhookListDeliveriesParams(TypedDict, total=False):
+ after: int
+ """Only deliveries after this Unix timestamp"""
+
+ before: int
+ """Only deliveries before this Unix timestamp"""
+
+ event: Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ """Filter by event type"""
+
+ page: int
+ """Page number (default 1)"""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Items per page (default 30, max 100)"""
+
+ success: bool
+ """Filter by delivery success"""
+
+ tenant_id: Annotated[str, PropertyInfo(alias="tenantId")]
+ """Filter by tenant ID"""
+
+ webhook_id: Annotated[str, PropertyInfo(alias="webhookId")]
+ """Filter by platform webhook ID"""
diff --git a/src/ark/types/platform/webhook_list_deliveries_response.py b/src/ark/types/platform/webhook_list_deliveries_response.py
new file mode 100644
index 0000000..25160c7
--- /dev/null
+++ b/src/ark/types/platform/webhook_list_deliveries_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__ = ["WebhookListDeliveriesResponse"]
+
+
+class WebhookListDeliveriesResponse(BaseModel):
+ """Summary of a platform webhook delivery attempt"""
+
+ id: str
+ """Unique delivery ID"""
+
+ attempt: int
+ """Attempt number (1 for first attempt, higher for retries)"""
+
+ event: Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ """Event type"""
+
+ status_code: Optional[int] = FieldInfo(alias="statusCode", default=None)
+ """HTTP status code returned by your endpoint (null if connection failed)"""
+
+ success: bool
+ """Whether delivery was successful (2xx response)"""
+
+ tenant_id: str = FieldInfo(alias="tenantId")
+ """Tenant that triggered the event"""
+
+ timestamp: datetime
+ """When the delivery was attempted"""
+
+ url: str
+ """Endpoint URL the delivery was sent to"""
+
+ webhook_id: str = FieldInfo(alias="webhookId")
+ """Platform webhook ID"""
+
+ will_retry: bool = FieldInfo(alias="willRetry")
+ """Whether this delivery will be retried"""
diff --git a/src/ark/types/platform/webhook_list_response.py b/src/ark/types/platform/webhook_list_response.py
new file mode 100644
index 0000000..3afef57
--- /dev/null
+++ b/src/ark/types/platform/webhook_list_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookListResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """Platform webhook ID"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ enabled: bool
+
+ events: List[str]
+
+ name: str
+
+ url: str
+
+
+class WebhookListResponse(BaseModel):
+ data: List[Data]
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/platform/webhook_replay_delivery_response.py b/src/ark/types/platform/webhook_replay_delivery_response.py
new file mode 100644
index 0000000..311bef0
--- /dev/null
+++ b/src/ark/types/platform/webhook_replay_delivery_response.py
@@ -0,0 +1,42 @@
+# 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
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookReplayDeliveryResponse", "Data"]
+
+
+class Data(BaseModel):
+ duration: int
+ """Request duration in milliseconds"""
+
+ new_delivery_id: str = FieldInfo(alias="newDeliveryId")
+ """ID of the new delivery created by the replay"""
+
+ original_delivery_id: str = FieldInfo(alias="originalDeliveryId")
+ """ID of the original delivery that was replayed"""
+
+ status_code: Optional[int] = FieldInfo(alias="statusCode", default=None)
+ """HTTP status code from your endpoint"""
+
+ success: bool
+ """Whether the replay was successful"""
+
+ timestamp: datetime
+ """When the replay was executed"""
+
+
+class WebhookReplayDeliveryResponse(BaseModel):
+ """Result of replaying a platform webhook delivery"""
+
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/platform/webhook_retrieve_delivery_response.py b/src/ark/types/platform/webhook_retrieve_delivery_response.py
new file mode 100644
index 0000000..6da891a
--- /dev/null
+++ b/src/ark/types/platform/webhook_retrieve_delivery_response.py
@@ -0,0 +1,81 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookRetrieveDeliveryResponse", "Data", "DataRequest", "DataResponse"]
+
+
+class DataRequest(BaseModel):
+ """Request details"""
+
+ headers: Optional[Dict[str, str]] = None
+ """Request headers including signature"""
+
+ payload: Optional[Dict[str, object]] = None
+ """The complete webhook payload that was sent"""
+
+
+class DataResponse(BaseModel):
+ """Response details"""
+
+ body: Optional[str] = None
+ """Response body (truncated if too large)"""
+
+ duration: Optional[int] = None
+ """Response time in milliseconds"""
+
+
+class Data(BaseModel):
+ id: str
+ """Unique delivery ID"""
+
+ attempt: int
+ """Attempt number"""
+
+ event: str
+ """Event type"""
+
+ request: DataRequest
+ """Request details"""
+
+ response: DataResponse
+ """Response details"""
+
+ status_code: Optional[int] = FieldInfo(alias="statusCode", default=None)
+ """HTTP status code from your endpoint"""
+
+ success: bool
+ """Whether delivery was successful"""
+
+ tenant_id: str = FieldInfo(alias="tenantId")
+ """Tenant that triggered the event"""
+
+ timestamp: datetime
+ """When delivery was attempted"""
+
+ url: str
+ """Endpoint URL"""
+
+ webhook_id: str = FieldInfo(alias="webhookId")
+ """Platform webhook ID"""
+
+ webhook_name: str = FieldInfo(alias="webhookName")
+ """Platform webhook name"""
+
+ will_retry: bool = FieldInfo(alias="willRetry")
+ """Whether this will be retried"""
+
+
+class WebhookRetrieveDeliveryResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/platform/webhook_retrieve_response.py b/src/ark/types/platform/webhook_retrieve_response.py
new file mode 100644
index 0000000..b589458
--- /dev/null
+++ b/src/ark/types/platform/webhook_retrieve_response.py
@@ -0,0 +1,52 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookRetrieveResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """Platform webhook ID"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ enabled: bool
+ """Whether the webhook is active"""
+
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ """Subscribed events (empty = all events)"""
+
+ name: str
+ """Webhook name for identification"""
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ url: str
+ """Webhook endpoint URL"""
+
+
+class WebhookRetrieveResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/webhook_test_params.py b/src/ark/types/platform/webhook_test_params.py
similarity index 100%
rename from src/ark/types/webhook_test_params.py
rename to src/ark/types/platform/webhook_test_params.py
diff --git a/src/ark/types/platform/webhook_test_response.py b/src/ark/types/platform/webhook_test_response.py
new file mode 100644
index 0000000..56036e1
--- /dev/null
+++ b/src/ark/types/platform/webhook_test_response.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookTestResponse", "Data"]
+
+
+class Data(BaseModel):
+ duration_ms: int = FieldInfo(alias="durationMs")
+ """Request duration in milliseconds"""
+
+ status_code: int = FieldInfo(alias="statusCode")
+ """HTTP status code from the webhook endpoint"""
+
+ success: bool
+ """Whether the webhook endpoint responded with a 2xx status"""
+
+ error: Optional[str] = None
+ """Error message if the request failed"""
+
+
+class WebhookTestResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/platform/webhook_update_params.py b/src/ark/types/platform/webhook_update_params.py
new file mode 100644
index 0000000..6da78ab
--- /dev/null
+++ b/src/ark/types/platform/webhook_update_params.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["WebhookUpdateParams"]
+
+
+class WebhookUpdateParams(TypedDict, total=False):
+ enabled: bool
+ """Enable or disable the webhook"""
+
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ """Events to subscribe to. Empty array means all events."""
+
+ name: str
+ """Display name for the webhook"""
+
+ url: str
+ """Webhook endpoint URL (must be HTTPS)"""
diff --git a/src/ark/types/platform/webhook_update_response.py b/src/ark/types/platform/webhook_update_response.py
new file mode 100644
index 0000000..d300235
--- /dev/null
+++ b/src/ark/types/platform/webhook_update_response.py
@@ -0,0 +1,52 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookUpdateResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """Platform webhook ID"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+
+ enabled: bool
+ """Whether the webhook is active"""
+
+ events: List[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ """Subscribed events (empty = all events)"""
+
+ name: str
+ """Webhook name for identification"""
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+
+ url: str
+ """Webhook endpoint URL"""
+
+
+class WebhookUpdateResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/suppression_bulk_create_params.py b/src/ark/types/suppression_bulk_create_params.py
deleted file mode 100644
index d0d0754..0000000
--- a/src/ark/types/suppression_bulk_create_params.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import Iterable, Optional
-from typing_extensions import Required, TypedDict
-
-__all__ = ["SuppressionBulkCreateParams", "Suppression"]
-
-
-class SuppressionBulkCreateParams(TypedDict, total=False):
- suppressions: Required[Iterable[Suppression]]
-
-
-class Suppression(TypedDict, total=False):
- address: Required[str]
-
- reason: Optional[str]
- """Reason for suppression (accepts null)"""
diff --git a/src/ark/types/suppression_bulk_create_response.py b/src/ark/types/suppression_bulk_create_response.py
deleted file mode 100644
index 4545f32..0000000
--- a/src/ark/types/suppression_bulk_create_response.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing_extensions import Literal
-
-from pydantic import Field as FieldInfo
-
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
-
-__all__ = ["SuppressionBulkCreateResponse", "Data"]
-
-
-class Data(BaseModel):
- added: int
- """Newly suppressed addresses"""
-
- failed: int
- """Invalid addresses skipped"""
-
- total_requested: int = FieldInfo(alias="totalRequested")
- """Total addresses in request"""
-
- updated: int
- """Already suppressed addresses (updated reason)"""
-
-
-class SuppressionBulkCreateResponse(BaseModel):
- data: Data
-
- meta: APIMeta
-
- success: Literal[True]
diff --git a/src/ark/types/tenant.py b/src/ark/types/tenant.py
index 5c1591a..1140756 100644
--- a/src/ark/types/tenant.py
+++ b/src/ark/types/tenant.py
@@ -4,6 +4,8 @@
from datetime import datetime
from typing_extensions import Literal
+from pydantic import Field as FieldInfo
+
from .._models import BaseModel
__all__ = ["Tenant"]
@@ -13,7 +15,7 @@ class Tenant(BaseModel):
id: str
"""Unique identifier for the tenant"""
- created_at: datetime
+ created_at: datetime = FieldInfo(alias="createdAt")
"""When the tenant was created"""
metadata: Dict[str, Union[str, float, bool, None]]
@@ -30,5 +32,5 @@ class Tenant(BaseModel):
- `archived` - Soft-deleted
"""
- updated_at: datetime
+ updated_at: datetime = FieldInfo(alias="updatedAt")
"""When the tenant was last updated"""
diff --git a/src/ark/types/tenant_usage_item.py b/src/ark/types/tenant_usage_item.py
new file mode 100644
index 0000000..d303755
--- /dev/null
+++ b/src/ark/types/tenant_usage_item.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .email_rates import EmailRates
+from .email_counts import EmailCounts
+
+__all__ = ["TenantUsageItem"]
+
+
+class TenantUsageItem(BaseModel):
+ """Usage record for a single tenant (camelCase for SDK)"""
+
+ emails: EmailCounts
+ """Email delivery counts"""
+
+ rates: EmailRates
+ """Email delivery rates (as decimals, e.g., 0.95 = 95%)"""
+
+ status: Literal["active", "suspended", "archived"]
+ """Current tenant status"""
+
+ tenant_id: str = FieldInfo(alias="tenantId")
+ """Unique tenant identifier"""
+
+ tenant_name: str = FieldInfo(alias="tenantName")
+ """Tenant display name"""
+
+ external_id: Optional[str] = FieldInfo(alias="externalId", default=None)
+ """Your external ID for this tenant"""
diff --git a/src/ark/types/tenants/__init__.py b/src/ark/types/tenants/__init__.py
new file mode 100644
index 0000000..c52a153
--- /dev/null
+++ b/src/ark/types/tenants/__init__.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .dns_record import DNSRecord as DNSRecord
+from .tenant_usage import TenantUsage as TenantUsage
+from .track_domain import TrackDomain as TrackDomain
+from .webhook_test_params import WebhookTestParams as WebhookTestParams
+from .domain_create_params import DomainCreateParams as DomainCreateParams
+from .domain_list_response import DomainListResponse as DomainListResponse
+from .usage_retrieve_params import UsageRetrieveParams as UsageRetrieveParams
+from .webhook_create_params import WebhookCreateParams as WebhookCreateParams
+from .webhook_list_response import WebhookListResponse as WebhookListResponse
+from .webhook_test_response import WebhookTestResponse as WebhookTestResponse
+from .webhook_update_params import WebhookUpdateParams as WebhookUpdateParams
+from .credential_list_params import CredentialListParams as CredentialListParams
+from .domain_create_response import DomainCreateResponse as DomainCreateResponse
+from .domain_delete_response import DomainDeleteResponse as DomainDeleteResponse
+from .domain_verify_response import DomainVerifyResponse as DomainVerifyResponse
+from .tracking_create_params import TrackingCreateParams as TrackingCreateParams
+from .tracking_list_response import TrackingListResponse as TrackingListResponse
+from .tracking_update_params import TrackingUpdateParams as TrackingUpdateParams
+from .suppression_list_params import SuppressionListParams as SuppressionListParams
+from .tenant_usage_timeseries import TenantUsageTimeseries as TenantUsageTimeseries
+from .usage_retrieve_response import UsageRetrieveResponse as UsageRetrieveResponse
+from .webhook_create_response import WebhookCreateResponse as WebhookCreateResponse
+from .webhook_delete_response import WebhookDeleteResponse as WebhookDeleteResponse
+from .webhook_update_response import WebhookUpdateResponse as WebhookUpdateResponse
+from .credential_create_params import CredentialCreateParams as CredentialCreateParams
+from .credential_list_response import CredentialListResponse as CredentialListResponse
+from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams
+from .domain_retrieve_response import DomainRetrieveResponse as DomainRetrieveResponse
+from .tracking_create_response import TrackingCreateResponse as TrackingCreateResponse
+from .tracking_delete_response import TrackingDeleteResponse as TrackingDeleteResponse
+from .tracking_update_response import TrackingUpdateResponse as TrackingUpdateResponse
+from .tracking_verify_response import TrackingVerifyResponse as TrackingVerifyResponse
+from .suppression_create_params import SuppressionCreateParams as SuppressionCreateParams
+from .suppression_list_response import SuppressionListResponse as SuppressionListResponse
+from .webhook_retrieve_response import WebhookRetrieveResponse as WebhookRetrieveResponse
+from .credential_create_response import CredentialCreateResponse as CredentialCreateResponse
+from .credential_delete_response import CredentialDeleteResponse as CredentialDeleteResponse
+from .credential_retrieve_params import CredentialRetrieveParams as CredentialRetrieveParams
+from .credential_update_response import CredentialUpdateResponse as CredentialUpdateResponse
+from .tracking_retrieve_response import TrackingRetrieveResponse as TrackingRetrieveResponse
+from .suppression_create_response import SuppressionCreateResponse as SuppressionCreateResponse
+from .suppression_delete_response import SuppressionDeleteResponse as SuppressionDeleteResponse
+from .credential_retrieve_response import CredentialRetrieveResponse as CredentialRetrieveResponse
+from .suppression_retrieve_response import SuppressionRetrieveResponse as SuppressionRetrieveResponse
+from .webhook_list_deliveries_params import WebhookListDeliveriesParams as WebhookListDeliveriesParams
+from .usage_retrieve_timeseries_params import UsageRetrieveTimeseriesParams as UsageRetrieveTimeseriesParams
+from .webhook_list_deliveries_response import WebhookListDeliveriesResponse as WebhookListDeliveriesResponse
+from .webhook_replay_delivery_response import WebhookReplayDeliveryResponse as WebhookReplayDeliveryResponse
+from .usage_retrieve_timeseries_response import UsageRetrieveTimeseriesResponse as UsageRetrieveTimeseriesResponse
+from .webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse as WebhookRetrieveDeliveryResponse
diff --git a/src/ark/types/tenants/credential_create_params.py b/src/ark/types/tenants/credential_create_params.py
new file mode 100644
index 0000000..3bbcc2d
--- /dev/null
+++ b/src/ark/types/tenants/credential_create_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 Literal, Required, TypedDict
+
+__all__ = ["CredentialCreateParams"]
+
+
+class CredentialCreateParams(TypedDict, total=False):
+ name: Required[str]
+ """Name for the credential.
+
+ Can only contain letters, numbers, hyphens, and underscores. Max 50 characters.
+ """
+
+ type: Required[Literal["smtp", "api"]]
+ """Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+ """
diff --git a/src/ark/types/tenants/credential_create_response.py b/src/ark/types/tenants/credential_create_response.py
new file mode 100644
index 0000000..685e534
--- /dev/null
+++ b/src/ark/types/tenants/credential_create_response.py
@@ -0,0 +1,63 @@
+# 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
+from ..shared.api_meta import APIMeta
+
+__all__ = ["CredentialCreateResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: int
+ """Unique identifier for the credential"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+ """When the credential was created"""
+
+ hold: bool
+ """
+ Whether the credential is on hold (disabled). When `true`, the credential cannot
+ be used to send emails.
+ """
+
+ key: str
+ """The credential key (secret).
+
+ **Store this securely** - it will not be shown again unless you use the reveal
+ parameter.
+ """
+
+ last_used_at: Optional[datetime] = FieldInfo(alias="lastUsedAt", default=None)
+ """When the credential was last used to send an email"""
+
+ name: str
+ """Name of the credential"""
+
+ type: Literal["smtp", "api"]
+ """Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+ """When the credential was last updated"""
+
+ smtp_username: Optional[str] = FieldInfo(alias="smtpUsername", default=None)
+ """SMTP username for authentication.
+
+ Only included for SMTP credentials. Format: `{tenantId}/{key}`
+ """
+
+
+class CredentialCreateResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/tenants/credential_delete_response.py b/src/ark/types/tenants/credential_delete_response.py
new file mode 100644
index 0000000..772b55e
--- /dev/null
+++ b/src/ark/types/tenants/credential_delete_response.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["CredentialDeleteResponse", "Data"]
+
+
+class Data(BaseModel):
+ deleted: Literal[True]
+
+
+class CredentialDeleteResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/tenants/credential_list_params.py b/src/ark/types/tenants/credential_list_params.py
new file mode 100644
index 0000000..29245af
--- /dev/null
+++ b/src/ark/types/tenants/credential_list_params.py
@@ -0,0 +1,20 @@
+# 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__ = ["CredentialListParams"]
+
+
+class CredentialListParams(TypedDict, total=False):
+ page: int
+ """Page number (1-indexed)"""
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Number of items per page (max 100)"""
+
+ type: Literal["smtp", "api"]
+ """Filter by credential type"""
diff --git a/src/ark/types/tenants/credential_list_response.py b/src/ark/types/tenants/credential_list_response.py
new file mode 100644
index 0000000..f69e705
--- /dev/null
+++ b/src/ark/types/tenants/credential_list_response.py
@@ -0,0 +1,41 @@
+# 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__ = ["CredentialListResponse"]
+
+
+class CredentialListResponse(BaseModel):
+ id: int
+ """Unique identifier for the credential"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+ """When the credential was created"""
+
+ hold: bool
+ """
+ Whether the credential is on hold (disabled). When `true`, the credential cannot
+ be used to send emails.
+ """
+
+ last_used_at: Optional[datetime] = FieldInfo(alias="lastUsedAt", default=None)
+ """When the credential was last used to send an email"""
+
+ name: str
+ """Name of the credential"""
+
+ type: Literal["smtp", "api"]
+ """Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+ """When the credential was last updated"""
diff --git a/src/ark/types/tenants/credential_retrieve_params.py b/src/ark/types/tenants/credential_retrieve_params.py
new file mode 100644
index 0000000..90e046f
--- /dev/null
+++ b/src/ark/types/tenants/credential_retrieve_params.py
@@ -0,0 +1,16 @@
+# 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__ = ["CredentialRetrieveParams"]
+
+
+class CredentialRetrieveParams(TypedDict, total=False):
+ tenant_id: Required[Annotated[str, PropertyInfo(alias="tenantId")]]
+
+ reveal: bool
+ """Set to `true` to include the credential key in the response"""
diff --git a/src/ark/types/tenants/credential_retrieve_response.py b/src/ark/types/tenants/credential_retrieve_response.py
new file mode 100644
index 0000000..a3633e8
--- /dev/null
+++ b/src/ark/types/tenants/credential_retrieve_response.py
@@ -0,0 +1,63 @@
+# 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
+from ..shared.api_meta import APIMeta
+
+__all__ = ["CredentialRetrieveResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: int
+ """Unique identifier for the credential"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+ """When the credential was created"""
+
+ hold: bool
+ """
+ Whether the credential is on hold (disabled). When `true`, the credential cannot
+ be used to send emails.
+ """
+
+ last_used_at: Optional[datetime] = FieldInfo(alias="lastUsedAt", default=None)
+ """When the credential was last used to send an email"""
+
+ name: str
+ """Name of the credential"""
+
+ type: Literal["smtp", "api"]
+ """Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+ """When the credential was last updated"""
+
+ key: Optional[str] = None
+ """The credential key (secret). Only included when:
+
+ - Creating a new credential (always returned)
+ - Retrieving with `reveal=true`
+ """
+
+ smtp_username: Optional[str] = FieldInfo(alias="smtpUsername", default=None)
+ """SMTP username for authentication.
+
+ Only included for SMTP credentials when the key is revealed.
+ """
+
+
+class CredentialRetrieveResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/tenants/credential_update_params.py b/src/ark/types/tenants/credential_update_params.py
new file mode 100644
index 0000000..f96a32c
--- /dev/null
+++ b/src/ark/types/tenants/credential_update_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__ = ["CredentialUpdateParams"]
+
+
+class CredentialUpdateParams(TypedDict, total=False):
+ tenant_id: Required[Annotated[str, PropertyInfo(alias="tenantId")]]
+
+ hold: bool
+ """
+ Set to `true` to disable the credential (put on hold). Set to `false` to enable
+ the credential (release from hold).
+ """
+
+ name: str
+ """New name for the credential"""
diff --git a/src/ark/types/tenants/credential_update_response.py b/src/ark/types/tenants/credential_update_response.py
new file mode 100644
index 0000000..9b19b2f
--- /dev/null
+++ b/src/ark/types/tenants/credential_update_response.py
@@ -0,0 +1,63 @@
+# 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
+from ..shared.api_meta import APIMeta
+
+__all__ = ["CredentialUpdateResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: int
+ """Unique identifier for the credential"""
+
+ created_at: datetime = FieldInfo(alias="createdAt")
+ """When the credential was created"""
+
+ hold: bool
+ """
+ Whether the credential is on hold (disabled). When `true`, the credential cannot
+ be used to send emails.
+ """
+
+ last_used_at: Optional[datetime] = FieldInfo(alias="lastUsedAt", default=None)
+ """When the credential was last used to send an email"""
+
+ name: str
+ """Name of the credential"""
+
+ type: Literal["smtp", "api"]
+ """Type of credential:
+
+ - `smtp` - For SMTP-based email sending
+ - `api` - For API-based email sending
+ """
+
+ updated_at: datetime = FieldInfo(alias="updatedAt")
+ """When the credential was last updated"""
+
+ key: Optional[str] = None
+ """The credential key (secret). Only included when:
+
+ - Creating a new credential (always returned)
+ - Retrieving with `reveal=true`
+ """
+
+ smtp_username: Optional[str] = FieldInfo(alias="smtpUsername", default=None)
+ """SMTP username for authentication.
+
+ Only included for SMTP credentials when the key is revealed.
+ """
+
+
+class CredentialUpdateResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/dns_record.py b/src/ark/types/tenants/dns_record.py
similarity index 98%
rename from src/ark/types/dns_record.py
rename to src/ark/types/tenants/dns_record.py
index 775e76e..50d47af 100644
--- a/src/ark/types/dns_record.py
+++ b/src/ark/types/tenants/dns_record.py
@@ -5,7 +5,7 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
__all__ = ["DNSRecord"]
diff --git a/src/ark/types/domain_create_params.py b/src/ark/types/tenants/domain_create_params.py
similarity index 100%
rename from src/ark/types/domain_create_params.py
rename to src/ark/types/tenants/domain_create_params.py
diff --git a/src/ark/types/domain_create_response.py b/src/ark/types/tenants/domain_create_response.py
similarity index 94%
rename from src/ark/types/domain_create_response.py
rename to src/ark/types/tenants/domain_create_response.py
index 56fdef4..39a6dcd 100644
--- a/src/ark/types/domain_create_response.py
+++ b/src/ark/types/tenants/domain_create_response.py
@@ -6,9 +6,9 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
from .dns_record import DNSRecord
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["DomainCreateResponse", "Data", "DataDNSRecords"]
@@ -124,6 +124,12 @@ class Data(BaseModel):
Domain must be verified before sending emails.
"""
+ tenant_id: Optional[str] = None
+ """ID of the tenant this domain belongs to"""
+
+ tenant_name: Optional[str] = None
+ """Name of the tenant this domain belongs to"""
+
verified_at: Optional[datetime] = FieldInfo(alias="verifiedAt", default=None)
"""Timestamp when the domain ownership was verified, or null if not yet verified"""
diff --git a/src/ark/types/domain_delete_response.py b/src/ark/types/tenants/domain_delete_response.py
similarity index 81%
rename from src/ark/types/domain_delete_response.py
rename to src/ark/types/tenants/domain_delete_response.py
index 71f6734..ded7cb7 100644
--- a/src/ark/types/domain_delete_response.py
+++ b/src/ark/types/tenants/domain_delete_response.py
@@ -2,8 +2,8 @@
from typing_extensions import Literal
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["DomainDeleteResponse", "Data"]
diff --git a/src/ark/types/domain_list_response.py b/src/ark/types/tenants/domain_list_response.py
similarity index 63%
rename from src/ark/types/domain_list_response.py
rename to src/ark/types/tenants/domain_list_response.py
index 50edc86..b7f328f 100644
--- a/src/ark/types/domain_list_response.py
+++ b/src/ark/types/tenants/domain_list_response.py
@@ -1,10 +1,10 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List
+from typing import List, Optional
from typing_extensions import Literal
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["DomainListResponse", "Data", "DataDomain"]
@@ -22,6 +22,14 @@ class DataDomain(BaseModel):
Domain must be verified before sending emails.
"""
+ tenant_id: Optional[str] = None
+ """ID of the tenant this domain belongs to (included when filtering by tenant_id)"""
+
+ tenant_name: Optional[str] = None
+ """
+ Name of the tenant this domain belongs to (included when filtering by tenant_id)
+ """
+
class Data(BaseModel):
domains: List[DataDomain]
diff --git a/src/ark/types/domain_retrieve_response.py b/src/ark/types/tenants/domain_retrieve_response.py
similarity index 94%
rename from src/ark/types/domain_retrieve_response.py
rename to src/ark/types/tenants/domain_retrieve_response.py
index bc9d550..6bb15d2 100644
--- a/src/ark/types/domain_retrieve_response.py
+++ b/src/ark/types/tenants/domain_retrieve_response.py
@@ -6,9 +6,9 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
from .dns_record import DNSRecord
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["DomainRetrieveResponse", "Data", "DataDNSRecords"]
@@ -124,6 +124,12 @@ class Data(BaseModel):
Domain must be verified before sending emails.
"""
+ tenant_id: Optional[str] = None
+ """ID of the tenant this domain belongs to"""
+
+ tenant_name: Optional[str] = None
+ """Name of the tenant this domain belongs to"""
+
verified_at: Optional[datetime] = FieldInfo(alias="verifiedAt", default=None)
"""Timestamp when the domain ownership was verified, or null if not yet verified"""
diff --git a/src/ark/types/domain_verify_response.py b/src/ark/types/tenants/domain_verify_response.py
similarity index 94%
rename from src/ark/types/domain_verify_response.py
rename to src/ark/types/tenants/domain_verify_response.py
index 8bc1ae9..cadeb40 100644
--- a/src/ark/types/domain_verify_response.py
+++ b/src/ark/types/tenants/domain_verify_response.py
@@ -6,9 +6,9 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
from .dns_record import DNSRecord
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["DomainVerifyResponse", "Data", "DataDNSRecords"]
@@ -124,6 +124,12 @@ class Data(BaseModel):
Domain must be verified before sending emails.
"""
+ tenant_id: Optional[str] = None
+ """ID of the tenant this domain belongs to"""
+
+ tenant_name: Optional[str] = None
+ """Name of the tenant this domain belongs to"""
+
verified_at: Optional[datetime] = FieldInfo(alias="verifiedAt", default=None)
"""Timestamp when the domain ownership was verified, or null if not yet verified"""
diff --git a/src/ark/types/suppression_create_params.py b/src/ark/types/tenants/suppression_create_params.py
similarity index 100%
rename from src/ark/types/suppression_create_params.py
rename to src/ark/types/tenants/suppression_create_params.py
diff --git a/src/ark/types/suppression_create_response.py b/src/ark/types/tenants/suppression_create_response.py
similarity index 89%
rename from src/ark/types/suppression_create_response.py
rename to src/ark/types/tenants/suppression_create_response.py
index 11b0897..021a7e8 100644
--- a/src/ark/types/suppression_create_response.py
+++ b/src/ark/types/tenants/suppression_create_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["SuppressionCreateResponse", "Data"]
diff --git a/src/ark/types/suppression_delete_response.py b/src/ark/types/tenants/suppression_delete_response.py
similarity index 82%
rename from src/ark/types/suppression_delete_response.py
rename to src/ark/types/tenants/suppression_delete_response.py
index 31e4ccc..a942543 100644
--- a/src/ark/types/suppression_delete_response.py
+++ b/src/ark/types/tenants/suppression_delete_response.py
@@ -2,8 +2,8 @@
from typing_extensions import Literal
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["SuppressionDeleteResponse", "Data"]
diff --git a/src/ark/types/suppression_list_params.py b/src/ark/types/tenants/suppression_list_params.py
similarity index 90%
rename from src/ark/types/suppression_list_params.py
rename to src/ark/types/tenants/suppression_list_params.py
index 71ee20d..57fa8bf 100644
--- a/src/ark/types/suppression_list_params.py
+++ b/src/ark/types/tenants/suppression_list_params.py
@@ -4,7 +4,7 @@
from typing_extensions import Annotated, TypedDict
-from .._utils import PropertyInfo
+from ..._utils import PropertyInfo
__all__ = ["SuppressionListParams"]
diff --git a/src/ark/types/suppression_list_response.py b/src/ark/types/tenants/suppression_list_response.py
similarity index 92%
rename from src/ark/types/suppression_list_response.py
rename to src/ark/types/tenants/suppression_list_response.py
index bf55aa5..7a67cee 100644
--- a/src/ark/types/suppression_list_response.py
+++ b/src/ark/types/tenants/suppression_list_response.py
@@ -5,7 +5,7 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
__all__ = ["SuppressionListResponse"]
diff --git a/src/ark/types/suppression_retrieve_response.py b/src/ark/types/tenants/suppression_retrieve_response.py
similarity index 91%
rename from src/ark/types/suppression_retrieve_response.py
rename to src/ark/types/tenants/suppression_retrieve_response.py
index f20757a..699da56 100644
--- a/src/ark/types/suppression_retrieve_response.py
+++ b/src/ark/types/tenants/suppression_retrieve_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["SuppressionRetrieveResponse", "Data"]
diff --git a/src/ark/types/tenants/tenant_usage.py b/src/ark/types/tenants/tenant_usage.py
new file mode 100644
index 0000000..98101fe
--- /dev/null
+++ b/src/ark/types/tenants/tenant_usage.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+from ..email_rates import EmailRates
+from ..email_counts import EmailCounts
+from ..usage_period import UsagePeriod
+
+__all__ = ["TenantUsage"]
+
+
+class TenantUsage(BaseModel):
+ """Tenant usage statistics"""
+
+ emails: EmailCounts
+ """Email delivery counts"""
+
+ period: UsagePeriod
+ """Time period for usage data"""
+
+ rates: EmailRates
+ """Email delivery rates (as decimals, e.g., 0.95 = 95%)"""
+
+ tenant_id: str
+ """Unique tenant identifier"""
+
+ tenant_name: str
+ """Tenant display name"""
+
+ external_id: Optional[str] = None
+ """Your external ID for this tenant (from metadata)"""
diff --git a/src/ark/types/tenants/tenant_usage_timeseries.py b/src/ark/types/tenants/tenant_usage_timeseries.py
new file mode 100644
index 0000000..cfc970d
--- /dev/null
+++ b/src/ark/types/tenants/tenant_usage_timeseries.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..usage_period import UsagePeriod
+
+__all__ = ["TenantUsageTimeseries", "Data"]
+
+
+class Data(BaseModel):
+ """Single timeseries data point"""
+
+ bounced: int
+ """Bounces in this bucket"""
+
+ delivered: int
+ """Emails delivered in this bucket"""
+
+ hard_failed: int
+ """Hard failures in this bucket"""
+
+ held: int
+ """Emails held in this bucket"""
+
+ sent: int
+ """Emails sent in this bucket"""
+
+ soft_failed: int
+ """Soft failures in this bucket"""
+
+ timestamp: datetime
+ """Start of time bucket"""
+
+
+class TenantUsageTimeseries(BaseModel):
+ """Timeseries usage statistics"""
+
+ data: List[Data]
+ """Array of time-bucketed data points"""
+
+ granularity: Literal["hour", "day", "week", "month"]
+ """Time bucket granularity"""
+
+ period: UsagePeriod
+ """Time period for usage data"""
+
+ tenant_id: str
+ """Unique tenant identifier"""
+
+ tenant_name: str
+ """Tenant display name"""
diff --git a/src/ark/types/track_domain.py b/src/ark/types/tenants/track_domain.py
similarity index 98%
rename from src/ark/types/track_domain.py
rename to src/ark/types/tenants/track_domain.py
index f2786ea..104d261 100644
--- a/src/ark/types/track_domain.py
+++ b/src/ark/types/tenants/track_domain.py
@@ -6,7 +6,7 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
__all__ = ["TrackDomain", "DNSRecord"]
diff --git a/src/ark/types/tracking_create_params.py b/src/ark/types/tenants/tracking_create_params.py
similarity index 96%
rename from src/ark/types/tracking_create_params.py
rename to src/ark/types/tenants/tracking_create_params.py
index d647b65..1ad766b 100644
--- a/src/ark/types/tracking_create_params.py
+++ b/src/ark/types/tenants/tracking_create_params.py
@@ -5,7 +5,7 @@
from typing import Optional
from typing_extensions import Required, Annotated, TypedDict
-from .._utils import PropertyInfo
+from ..._utils import PropertyInfo
__all__ = ["TrackingCreateParams"]
diff --git a/src/ark/types/tracking_create_response.py b/src/ark/types/tenants/tracking_create_response.py
similarity index 81%
rename from src/ark/types/tracking_create_response.py
rename to src/ark/types/tenants/tracking_create_response.py
index 93cbf43..879b574 100644
--- a/src/ark/types/tracking_create_response.py
+++ b/src/ark/types/tenants/tracking_create_response.py
@@ -2,9 +2,9 @@
from typing_extensions import Literal
-from .._models import BaseModel
+from ..._models import BaseModel
from .track_domain import TrackDomain
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["TrackingCreateResponse"]
diff --git a/src/ark/types/tracking_delete_response.py b/src/ark/types/tenants/tracking_delete_response.py
similarity index 81%
rename from src/ark/types/tracking_delete_response.py
rename to src/ark/types/tenants/tracking_delete_response.py
index bd440c9..3a1243e 100644
--- a/src/ark/types/tracking_delete_response.py
+++ b/src/ark/types/tenants/tracking_delete_response.py
@@ -2,8 +2,8 @@
from typing_extensions import Literal
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["TrackingDeleteResponse", "Data"]
diff --git a/src/ark/types/tracking_list_response.py b/src/ark/types/tenants/tracking_list_response.py
similarity index 86%
rename from src/ark/types/tracking_list_response.py
rename to src/ark/types/tenants/tracking_list_response.py
index ffd31a8..53bddb7 100644
--- a/src/ark/types/tracking_list_response.py
+++ b/src/ark/types/tenants/tracking_list_response.py
@@ -5,9 +5,9 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
+from ..._models import BaseModel
from .track_domain import TrackDomain
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["TrackingListResponse", "Data"]
diff --git a/src/ark/types/tracking_retrieve_response.py b/src/ark/types/tenants/tracking_retrieve_response.py
similarity index 81%
rename from src/ark/types/tracking_retrieve_response.py
rename to src/ark/types/tenants/tracking_retrieve_response.py
index cc974a2..7e39ca9 100644
--- a/src/ark/types/tracking_retrieve_response.py
+++ b/src/ark/types/tenants/tracking_retrieve_response.py
@@ -2,9 +2,9 @@
from typing_extensions import Literal
-from .._models import BaseModel
+from ..._models import BaseModel
from .track_domain import TrackDomain
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["TrackingRetrieveResponse"]
diff --git a/src/ark/types/tracking_update_params.py b/src/ark/types/tenants/tracking_update_params.py
similarity index 83%
rename from src/ark/types/tracking_update_params.py
rename to src/ark/types/tenants/tracking_update_params.py
index 3ad46a0..febe059 100644
--- a/src/ark/types/tracking_update_params.py
+++ b/src/ark/types/tenants/tracking_update_params.py
@@ -3,14 +3,16 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Annotated, TypedDict
+from typing_extensions import Required, Annotated, TypedDict
-from .._utils import PropertyInfo
+from ..._utils import PropertyInfo
__all__ = ["TrackingUpdateParams"]
class TrackingUpdateParams(TypedDict, total=False):
+ tenant_id: Required[Annotated[str, PropertyInfo(alias="tenantId")]]
+
excluded_click_domains: Annotated[Optional[str], PropertyInfo(alias="excludedClickDomains")]
"""Comma-separated list of domains to exclude from click tracking (accepts null)"""
diff --git a/src/ark/types/tracking_update_response.py b/src/ark/types/tenants/tracking_update_response.py
similarity index 81%
rename from src/ark/types/tracking_update_response.py
rename to src/ark/types/tenants/tracking_update_response.py
index a1761e5..c06efee 100644
--- a/src/ark/types/tracking_update_response.py
+++ b/src/ark/types/tenants/tracking_update_response.py
@@ -2,9 +2,9 @@
from typing_extensions import Literal
-from .._models import BaseModel
+from ..._models import BaseModel
from .track_domain import TrackDomain
-from .shared.api_meta import APIMeta
+from ..shared.api_meta import APIMeta
__all__ = ["TrackingUpdateResponse"]
diff --git a/src/ark/types/tracking_verify_response.py b/src/ark/types/tenants/tracking_verify_response.py
similarity index 94%
rename from src/ark/types/tracking_verify_response.py
rename to src/ark/types/tenants/tracking_verify_response.py
index 076afbf..d1f2df9 100644
--- a/src/ark/types/tracking_verify_response.py
+++ b/src/ark/types/tenants/tracking_verify_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["TrackingVerifyResponse", "Data", "DataDNSRecord"]
diff --git a/src/ark/types/tenants/usage_retrieve_params.py b/src/ark/types/tenants/usage_retrieve_params.py
new file mode 100644
index 0000000..48c3de3
--- /dev/null
+++ b/src/ark/types/tenants/usage_retrieve_params.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["UsageRetrieveParams"]
+
+
+class UsageRetrieveParams(TypedDict, total=False):
+ period: str
+ """Time period for usage data. Defaults to current month.
+
+ **Formats:**
+
+ - Shortcuts: `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+ - Month: `2024-01`
+ - Range: `2024-01-01..2024-01-31`
+ - Day: `2024-01-15`
+ """
+
+ timezone: str
+ """Timezone for period calculations (IANA format). Defaults to UTC."""
diff --git a/src/ark/types/tenants/usage_retrieve_response.py b/src/ark/types/tenants/usage_retrieve_response.py
new file mode 100644
index 0000000..d7abac0
--- /dev/null
+++ b/src/ark/types/tenants/usage_retrieve_response.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .tenant_usage import TenantUsage
+from ..shared.api_meta import APIMeta
+
+__all__ = ["UsageRetrieveResponse"]
+
+
+class UsageRetrieveResponse(BaseModel):
+ """Usage statistics for a single tenant"""
+
+ data: TenantUsage
+ """Tenant usage statistics"""
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/tenants/usage_retrieve_timeseries_params.py b/src/ark/types/tenants/usage_retrieve_timeseries_params.py
new file mode 100644
index 0000000..c6682b3
--- /dev/null
+++ b/src/ark/types/tenants/usage_retrieve_timeseries_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["UsageRetrieveTimeseriesParams"]
+
+
+class UsageRetrieveTimeseriesParams(TypedDict, total=False):
+ granularity: Literal["hour", "day", "week", "month"]
+ """Time bucket size for data points"""
+
+ period: str
+ """Time period for timeseries data. Defaults to current month."""
+
+ timezone: str
+ """Timezone for period calculations (IANA format). Defaults to UTC."""
diff --git a/src/ark/types/tenants/usage_retrieve_timeseries_response.py b/src/ark/types/tenants/usage_retrieve_timeseries_response.py
new file mode 100644
index 0000000..74962fb
--- /dev/null
+++ b/src/ark/types/tenants/usage_retrieve_timeseries_response.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+from .tenant_usage_timeseries import TenantUsageTimeseries
+
+__all__ = ["UsageRetrieveTimeseriesResponse"]
+
+
+class UsageRetrieveTimeseriesResponse(BaseModel):
+ """Timeseries usage data for a tenant"""
+
+ data: TenantUsageTimeseries
+ """Timeseries usage statistics"""
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/webhook_create_params.py b/src/ark/types/tenants/webhook_create_params.py
similarity index 97%
rename from src/ark/types/webhook_create_params.py
rename to src/ark/types/tenants/webhook_create_params.py
index 9899826..aa4767e 100644
--- a/src/ark/types/webhook_create_params.py
+++ b/src/ark/types/tenants/webhook_create_params.py
@@ -5,7 +5,7 @@
from typing import List, Optional
from typing_extensions import Literal, Required, Annotated, TypedDict
-from .._utils import PropertyInfo
+from ..._utils import PropertyInfo
__all__ = ["WebhookCreateParams"]
diff --git a/src/ark/types/webhook_create_response.py b/src/ark/types/tenants/webhook_create_response.py
similarity index 93%
rename from src/ark/types/webhook_create_response.py
rename to src/ark/types/tenants/webhook_create_response.py
index d78bb58..b30b19e 100644
--- a/src/ark/types/webhook_create_response.py
+++ b/src/ark/types/tenants/webhook_create_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookCreateResponse", "Data"]
diff --git a/src/ark/types/tenants/webhook_delete_response.py b/src/ark/types/tenants/webhook_delete_response.py
new file mode 100644
index 0000000..aee22ba
--- /dev/null
+++ b/src/ark/types/tenants/webhook_delete_response.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
+
+__all__ = ["WebhookDeleteResponse", "Data"]
+
+
+class Data(BaseModel):
+ message: str
+
+
+class WebhookDeleteResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/webhook_list_deliveries_params.py b/src/ark/types/tenants/webhook_list_deliveries_params.py
similarity index 83%
rename from src/ark/types/webhook_list_deliveries_params.py
rename to src/ark/types/tenants/webhook_list_deliveries_params.py
index c4048e4..c990a0c 100644
--- a/src/ark/types/webhook_list_deliveries_params.py
+++ b/src/ark/types/tenants/webhook_list_deliveries_params.py
@@ -2,14 +2,16 @@
from __future__ import annotations
-from typing_extensions import Literal, Annotated, TypedDict
+from typing_extensions import Literal, Required, Annotated, TypedDict
-from .._utils import PropertyInfo
+from ..._utils import PropertyInfo
__all__ = ["WebhookListDeliveriesParams"]
class WebhookListDeliveriesParams(TypedDict, total=False):
+ tenant_id: Required[Annotated[str, PropertyInfo(alias="tenantId")]]
+
after: int
"""Only deliveries after this Unix timestamp"""
diff --git a/src/ark/types/webhook_list_deliveries_response.py b/src/ark/types/tenants/webhook_list_deliveries_response.py
similarity index 96%
rename from src/ark/types/webhook_list_deliveries_response.py
rename to src/ark/types/tenants/webhook_list_deliveries_response.py
index 71a5fe2..ce0b50d 100644
--- a/src/ark/types/webhook_list_deliveries_response.py
+++ b/src/ark/types/tenants/webhook_list_deliveries_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookListDeliveriesResponse", "Data"]
diff --git a/src/ark/types/webhook_list_response.py b/src/ark/types/tenants/webhook_list_response.py
similarity index 87%
rename from src/ark/types/webhook_list_response.py
rename to src/ark/types/tenants/webhook_list_response.py
index 10acdd6..ccd413a 100644
--- a/src/ark/types/webhook_list_response.py
+++ b/src/ark/types/tenants/webhook_list_response.py
@@ -3,8 +3,8 @@
from typing import List
from typing_extensions import Literal
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookListResponse", "Data", "DataWebhook"]
diff --git a/src/ark/types/webhook_replay_delivery_response.py b/src/ark/types/tenants/webhook_replay_delivery_response.py
similarity index 93%
rename from src/ark/types/webhook_replay_delivery_response.py
rename to src/ark/types/tenants/webhook_replay_delivery_response.py
index f3961fd..58c9db6 100644
--- a/src/ark/types/webhook_replay_delivery_response.py
+++ b/src/ark/types/tenants/webhook_replay_delivery_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookReplayDeliveryResponse", "Data"]
diff --git a/src/ark/types/webhook_retrieve_delivery_response.py b/src/ark/types/tenants/webhook_retrieve_delivery_response.py
similarity index 97%
rename from src/ark/types/webhook_retrieve_delivery_response.py
rename to src/ark/types/tenants/webhook_retrieve_delivery_response.py
index efec5c4..f13d5f0 100644
--- a/src/ark/types/webhook_retrieve_delivery_response.py
+++ b/src/ark/types/tenants/webhook_retrieve_delivery_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookRetrieveDeliveryResponse", "Data", "DataRequest", "DataResponse"]
diff --git a/src/ark/types/webhook_retrieve_response.py b/src/ark/types/tenants/webhook_retrieve_response.py
similarity index 93%
rename from src/ark/types/webhook_retrieve_response.py
rename to src/ark/types/tenants/webhook_retrieve_response.py
index 6cb2f2e..d2a9d91 100644
--- a/src/ark/types/webhook_retrieve_response.py
+++ b/src/ark/types/tenants/webhook_retrieve_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookRetrieveResponse", "Data"]
diff --git a/src/ark/types/tenants/webhook_test_params.py b/src/ark/types/tenants/webhook_test_params.py
new file mode 100644
index 0000000..dc43485
--- /dev/null
+++ b/src/ark/types/tenants/webhook_test_params.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["WebhookTestParams"]
+
+
+class WebhookTestParams(TypedDict, total=False):
+ tenant_id: Required[Annotated[str, PropertyInfo(alias="tenantId")]]
+
+ event: Required[
+ Literal[
+ "MessageSent",
+ "MessageDelayed",
+ "MessageDeliveryFailed",
+ "MessageHeld",
+ "MessageBounced",
+ "MessageLinkClicked",
+ "MessageLoaded",
+ "DomainDNSError",
+ ]
+ ]
+ """Event type to simulate"""
diff --git a/src/ark/types/webhook_test_response.py b/src/ark/types/tenants/webhook_test_response.py
similarity index 92%
rename from src/ark/types/webhook_test_response.py
rename to src/ark/types/tenants/webhook_test_response.py
index a51041b..60da1c2 100644
--- a/src/ark/types/webhook_test_response.py
+++ b/src/ark/types/tenants/webhook_test_response.py
@@ -5,8 +5,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookTestResponse", "Data"]
diff --git a/src/ark/types/webhook_update_params.py b/src/ark/types/tenants/webhook_update_params.py
similarity index 67%
rename from src/ark/types/webhook_update_params.py
rename to src/ark/types/tenants/webhook_update_params.py
index 934b579..79587f2 100644
--- a/src/ark/types/webhook_update_params.py
+++ b/src/ark/types/tenants/webhook_update_params.py
@@ -3,15 +3,17 @@
from __future__ import annotations
from typing import Optional
-from typing_extensions import Annotated, TypedDict
+from typing_extensions import Required, Annotated, TypedDict
-from .._types import SequenceNotStr
-from .._utils import PropertyInfo
+from ..._types import SequenceNotStr
+from ..._utils import PropertyInfo
__all__ = ["WebhookUpdateParams"]
class WebhookUpdateParams(TypedDict, total=False):
+ tenant_id: Required[Annotated[str, PropertyInfo(alias="tenantId")]]
+
all_events: Annotated[Optional[bool], PropertyInfo(alias="allEvents")]
enabled: Optional[bool]
diff --git a/src/ark/types/webhook_update_response.py b/src/ark/types/tenants/webhook_update_response.py
similarity index 93%
rename from src/ark/types/webhook_update_response.py
rename to src/ark/types/tenants/webhook_update_response.py
index 4d9a0c6..d98f7d4 100644
--- a/src/ark/types/webhook_update_response.py
+++ b/src/ark/types/tenants/webhook_update_response.py
@@ -6,8 +6,8 @@
from pydantic import Field as FieldInfo
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
+from ..._models import BaseModel
+from ..shared.api_meta import APIMeta
__all__ = ["WebhookUpdateResponse", "Data"]
diff --git a/src/ark/types/usage_export_params.py b/src/ark/types/usage_export_params.py
new file mode 100644
index 0000000..2d02b6e
--- /dev/null
+++ b/src/ark/types/usage_export_params.py
@@ -0,0 +1,33 @@
+# 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__ = ["UsageExportParams"]
+
+
+class UsageExportParams(TypedDict, total=False):
+ format: Literal["csv", "jsonl"]
+ """Export format"""
+
+ min_sent: Annotated[int, PropertyInfo(alias="minSent")]
+ """Only include tenants with at least this many emails sent"""
+
+ period: str
+ """Time period for export.
+
+ **Shortcuts:** `this_month`, `last_month`, `last_30_days`, etc.
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+ """
+
+ status: Literal["active", "suspended", "archived"]
+ """Filter by tenant status"""
+
+ timezone: str
+ """Timezone for period calculations (IANA format)"""
diff --git a/src/ark/types/usage_export_response.py b/src/ark/types/usage_export_response.py
new file mode 100644
index 0000000..3680f66
--- /dev/null
+++ b/src/ark/types/usage_export_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 typing_extensions import Literal, TypeAlias
+
+from .._models import BaseModel
+
+__all__ = ["UsageExportResponse", "UsageExportResponseItem"]
+
+
+class UsageExportResponseItem(BaseModel):
+ """Single row in usage export (JSON format)"""
+
+ bounce_rate: float
+ """Bounce rate (0-1)"""
+
+ bounced: int
+ """Emails that bounced"""
+
+ delivered: int
+ """Emails successfully delivered"""
+
+ delivery_rate: float
+ """Delivery rate (0-1)"""
+
+ hard_failed: int
+ """Emails that hard-failed"""
+
+ held: int
+ """Emails currently held"""
+
+ sent: int
+ """Total emails sent"""
+
+ soft_failed: int
+ """Emails that soft-failed"""
+
+ status: Literal["active", "suspended", "archived"]
+ """Current tenant status"""
+
+ tenant_id: str
+ """Unique tenant identifier"""
+
+ tenant_name: str
+ """Tenant display name"""
+
+ external_id: Optional[str] = None
+ """Your external ID for this tenant"""
+
+
+UsageExportResponse: TypeAlias = List[UsageExportResponseItem]
diff --git a/src/ark/types/usage_list_tenants_params.py b/src/ark/types/usage_list_tenants_params.py
new file mode 100644
index 0000000..bf4d702
--- /dev/null
+++ b/src/ark/types/usage_list_tenants_params.py
@@ -0,0 +1,51 @@
+# 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__ = ["UsageListTenantsParams"]
+
+
+class UsageListTenantsParams(TypedDict, total=False):
+ min_sent: Annotated[int, PropertyInfo(alias="minSent")]
+ """Only include tenants with at least this many emails sent"""
+
+ page: int
+ """Page number (1-indexed)"""
+
+ period: str
+ """Time period for usage data. Defaults to current month.
+
+ **Shortcuts:** `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+ """
+
+ per_page: Annotated[int, PropertyInfo(alias="perPage")]
+ """Results per page (max 100)"""
+
+ sort: Literal[
+ "sent",
+ "-sent",
+ "delivered",
+ "-delivered",
+ "bounce_rate",
+ "-bounce_rate",
+ "delivery_rate",
+ "-delivery_rate",
+ "tenant_name",
+ "-tenant_name",
+ ]
+ """Sort order for results. Prefix with `-` for descending order."""
+
+ status: Literal["active", "suspended", "archived"]
+ """Filter by tenant status"""
+
+ timezone: str
+ """Timezone for period calculations (IANA format). Defaults to UTC."""
diff --git a/src/ark/types/usage_period.py b/src/ark/types/usage_period.py
new file mode 100644
index 0000000..4372295
--- /dev/null
+++ b/src/ark/types/usage_period.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from datetime import datetime
+
+from .._models import BaseModel
+
+__all__ = ["UsagePeriod"]
+
+
+class UsagePeriod(BaseModel):
+ """Time period for usage data"""
+
+ end: datetime
+ """Period end (inclusive)"""
+
+ start: datetime
+ """Period start (inclusive)"""
diff --git a/src/ark/types/usage_retrieve_params.py b/src/ark/types/usage_retrieve_params.py
new file mode 100644
index 0000000..89a75ba
--- /dev/null
+++ b/src/ark/types/usage_retrieve_params.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["UsageRetrieveParams"]
+
+
+class UsageRetrieveParams(TypedDict, total=False):
+ period: str
+ """Time period for usage data.
+
+ **Shortcuts:** `today`, `yesterday`, `this_week`, `last_week`, `this_month`,
+ `last_month`, `last_7_days`, `last_30_days`, `last_90_days`
+
+ **Month format:** `2024-01` (YYYY-MM)
+
+ **Custom range:** `2024-01-01..2024-01-15`
+ """
+
+ timezone: str
+ """Timezone for period calculations (IANA format)"""
diff --git a/tests/api_resources/platform/__init__.py b/tests/api_resources/platform/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/platform/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/platform/test_webhooks.py
similarity index 68%
rename from tests/api_resources/test_webhooks.py
rename to tests/api_resources/platform/test_webhooks.py
index e994815..b64b0a6 100644
--- a/tests/api_resources/test_webhooks.py
+++ b/tests/api_resources/platform/test_webhooks.py
@@ -8,7 +8,9 @@
import pytest
from ark import Ark, AsyncArk
-from ark.types import (
+from tests.utils import assert_matches_type
+from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
+from ark.types.platform import (
WebhookListResponse,
WebhookTestResponse,
WebhookCreateResponse,
@@ -19,7 +21,6 @@
WebhookReplayDeliveryResponse,
WebhookRetrieveDeliveryResponse,
)
-from tests.utils import assert_matches_type
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -29,28 +30,26 @@ class TestWebhooks:
@parametrize
def test_method_create(self, client: Ark) -> None:
- webhook = client.webhooks.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
+ webhook = client.platform.webhooks.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
)
assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
@parametrize
def test_method_create_with_all_params(self, client: Ark) -> None:
- webhook = client.webhooks.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
- all_events=True,
- enabled=True,
+ webhook = client.platform.webhooks.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
events=["MessageSent", "MessageDeliveryFailed", "MessageBounced"],
)
assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
@parametrize
def test_raw_response_create(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
+ response = client.platform.webhooks.with_raw_response.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
)
assert response.is_closed is True
@@ -60,9 +59,9 @@ def test_raw_response_create(self, client: Ark) -> None:
@parametrize
def test_streaming_response_create(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
+ with client.platform.webhooks.with_streaming_response.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -74,15 +73,15 @@ def test_streaming_response_create(self, client: Ark) -> None:
@parametrize
def test_method_retrieve(self, client: Ark) -> None:
- webhook = client.webhooks.retrieve(
- "webhookId",
+ webhook = client.platform.webhooks.retrieve(
+ "pwh_abc123def456",
)
assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.retrieve(
- "webhookId",
+ response = client.platform.webhooks.with_raw_response.retrieve(
+ "pwh_abc123def456",
)
assert response.is_closed is True
@@ -92,8 +91,8 @@ def test_raw_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.retrieve(
- "webhookId",
+ with client.platform.webhooks.with_streaming_response.retrieve(
+ "pwh_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -106,33 +105,32 @@ def test_streaming_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve(self, client: Ark) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.retrieve(
+ client.platform.webhooks.with_raw_response.retrieve(
"",
)
@parametrize
def test_method_update(self, client: Ark) -> None:
- webhook = client.webhooks.update(
- webhook_id="webhookId",
+ webhook = client.platform.webhooks.update(
+ webhook_id="pwh_abc123def456",
)
assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
@parametrize
def test_method_update_with_all_params(self, client: Ark) -> None:
- webhook = client.webhooks.update(
- webhook_id="webhookId",
- all_events=True,
+ webhook = client.platform.webhooks.update(
+ webhook_id="pwh_abc123def456",
enabled=True,
- events=["string"],
- name="name",
+ events=["MessageSent"],
+ name="x",
url="https://example.com",
)
assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
@parametrize
def test_raw_response_update(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.update(
- webhook_id="webhookId",
+ response = client.platform.webhooks.with_raw_response.update(
+ webhook_id="pwh_abc123def456",
)
assert response.is_closed is True
@@ -142,8 +140,8 @@ def test_raw_response_update(self, client: Ark) -> None:
@parametrize
def test_streaming_response_update(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.update(
- webhook_id="webhookId",
+ with client.platform.webhooks.with_streaming_response.update(
+ webhook_id="pwh_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -156,18 +154,18 @@ def test_streaming_response_update(self, client: Ark) -> None:
@parametrize
def test_path_params_update(self, client: Ark) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.update(
+ client.platform.webhooks.with_raw_response.update(
webhook_id="",
)
@parametrize
def test_method_list(self, client: Ark) -> None:
- webhook = client.webhooks.list()
+ webhook = client.platform.webhooks.list()
assert_matches_type(WebhookListResponse, webhook, path=["response"])
@parametrize
def test_raw_response_list(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.list()
+ response = client.platform.webhooks.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -176,7 +174,7 @@ def test_raw_response_list(self, client: Ark) -> None:
@parametrize
def test_streaming_response_list(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.list() as response:
+ with client.platform.webhooks.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -187,15 +185,15 @@ def test_streaming_response_list(self, client: Ark) -> None:
@parametrize
def test_method_delete(self, client: Ark) -> None:
- webhook = client.webhooks.delete(
- "webhookId",
+ webhook = client.platform.webhooks.delete(
+ "pwh_abc123def456",
)
assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
@parametrize
def test_raw_response_delete(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.delete(
- "webhookId",
+ response = client.platform.webhooks.with_raw_response.delete(
+ "pwh_abc123def456",
)
assert response.is_closed is True
@@ -205,8 +203,8 @@ def test_raw_response_delete(self, client: Ark) -> None:
@parametrize
def test_streaming_response_delete(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.delete(
- "webhookId",
+ with client.platform.webhooks.with_streaming_response.delete(
+ "pwh_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -219,74 +217,60 @@ def test_streaming_response_delete(self, client: Ark) -> None:
@parametrize
def test_path_params_delete(self, client: Ark) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.delete(
+ client.platform.webhooks.with_raw_response.delete(
"",
)
@parametrize
def test_method_list_deliveries(self, client: Ark) -> None:
- webhook = client.webhooks.list_deliveries(
- webhook_id="webhookId",
- )
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ webhook = client.platform.webhooks.list_deliveries()
+ assert_matches_type(SyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
@parametrize
def test_method_list_deliveries_with_all_params(self, client: Ark) -> None:
- webhook = client.webhooks.list_deliveries(
- webhook_id="webhookId",
+ webhook = client.platform.webhooks.list_deliveries(
after=0,
before=0,
event="MessageSent",
- page=1,
- per_page=1,
+ page=0,
+ per_page=100,
success=True,
+ tenant_id="tenantId",
+ webhook_id="webhookId",
)
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ assert_matches_type(SyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
@parametrize
def test_raw_response_list_deliveries(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.list_deliveries(
- webhook_id="webhookId",
- )
+ response = client.platform.webhooks.with_raw_response.list_deliveries()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
webhook = response.parse()
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ assert_matches_type(SyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
@parametrize
def test_streaming_response_list_deliveries(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.list_deliveries(
- webhook_id="webhookId",
- ) as response:
+ with client.platform.webhooks.with_streaming_response.list_deliveries() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
webhook = response.parse()
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ assert_matches_type(SyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
assert cast(Any, response.is_closed) is True
- @parametrize
- def test_path_params_list_deliveries(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.list_deliveries(
- webhook_id="",
- )
-
@parametrize
def test_method_replay_delivery(self, client: Ark) -> None:
- webhook = client.webhooks.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ webhook = client.platform.webhooks.replay_delivery(
+ "pwd_abc123def456",
)
assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
@parametrize
def test_raw_response_replay_delivery(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ response = client.platform.webhooks.with_raw_response.replay_delivery(
+ "pwd_abc123def456",
)
assert response.is_closed is True
@@ -296,9 +280,8 @@ def test_raw_response_replay_delivery(self, client: Ark) -> None:
@parametrize
def test_streaming_response_replay_delivery(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ with client.platform.webhooks.with_streaming_response.replay_delivery(
+ "pwd_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -310,31 +293,22 @@ def test_streaming_response_replay_delivery(self, client: Ark) -> None:
@parametrize
def test_path_params_replay_delivery(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="",
- )
-
with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
- client.webhooks.with_raw_response.replay_delivery(
- delivery_id="",
- webhook_id="webhookId",
+ client.platform.webhooks.with_raw_response.replay_delivery(
+ "",
)
@parametrize
def test_method_retrieve_delivery(self, client: Ark) -> None:
- webhook = client.webhooks.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ webhook = client.platform.webhooks.retrieve_delivery(
+ "pwd_abc123def456",
)
assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
@parametrize
def test_raw_response_retrieve_delivery(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ response = client.platform.webhooks.with_raw_response.retrieve_delivery(
+ "pwd_abc123def456",
)
assert response.is_closed is True
@@ -344,9 +318,8 @@ def test_raw_response_retrieve_delivery(self, client: Ark) -> None:
@parametrize
def test_streaming_response_retrieve_delivery(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ with client.platform.webhooks.with_streaming_response.retrieve_delivery(
+ "pwd_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -358,30 +331,23 @@ def test_streaming_response_retrieve_delivery(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve_delivery(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="",
- )
-
with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
- client.webhooks.with_raw_response.retrieve_delivery(
- delivery_id="",
- webhook_id="webhookId",
+ client.platform.webhooks.with_raw_response.retrieve_delivery(
+ "",
)
@parametrize
def test_method_test(self, client: Ark) -> None:
- webhook = client.webhooks.test(
- webhook_id="webhookId",
+ webhook = client.platform.webhooks.test(
+ webhook_id="pwh_abc123def456",
event="MessageSent",
)
assert_matches_type(WebhookTestResponse, webhook, path=["response"])
@parametrize
def test_raw_response_test(self, client: Ark) -> None:
- response = client.webhooks.with_raw_response.test(
- webhook_id="webhookId",
+ response = client.platform.webhooks.with_raw_response.test(
+ webhook_id="pwh_abc123def456",
event="MessageSent",
)
@@ -392,8 +358,8 @@ def test_raw_response_test(self, client: Ark) -> None:
@parametrize
def test_streaming_response_test(self, client: Ark) -> None:
- with client.webhooks.with_streaming_response.test(
- webhook_id="webhookId",
+ with client.platform.webhooks.with_streaming_response.test(
+ webhook_id="pwh_abc123def456",
event="MessageSent",
) as response:
assert not response.is_closed
@@ -407,7 +373,7 @@ def test_streaming_response_test(self, client: Ark) -> None:
@parametrize
def test_path_params_test(self, client: Ark) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- client.webhooks.with_raw_response.test(
+ client.platform.webhooks.with_raw_response.test(
webhook_id="",
event="MessageSent",
)
@@ -420,28 +386,26 @@ class TestAsyncWebhooks:
@parametrize
async def test_method_create(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
+ webhook = await async_client.platform.webhooks.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
)
assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
- all_events=True,
- enabled=True,
+ webhook = await async_client.platform.webhooks.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
events=["MessageSent", "MessageDeliveryFailed", "MessageBounced"],
)
assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
+ response = await async_client.platform.webhooks.with_raw_response.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
)
assert response.is_closed is True
@@ -451,9 +415,9 @@ async def test_raw_response_create(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.create(
- name="My App Webhook",
- url="https://myapp.com/webhooks/email",
+ async with async_client.platform.webhooks.with_streaming_response.create(
+ name="Central Event Processor",
+ url="https://myplatform.com/webhooks/email-events",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -465,15 +429,15 @@ async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
@parametrize
async def test_method_retrieve(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.retrieve(
- "webhookId",
+ webhook = await async_client.platform.webhooks.retrieve(
+ "pwh_abc123def456",
)
assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.retrieve(
- "webhookId",
+ response = await async_client.platform.webhooks.with_raw_response.retrieve(
+ "pwh_abc123def456",
)
assert response.is_closed is True
@@ -483,8 +447,8 @@ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.retrieve(
- "webhookId",
+ async with async_client.platform.webhooks.with_streaming_response.retrieve(
+ "pwh_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -497,33 +461,32 @@ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.retrieve(
+ await async_client.platform.webhooks.with_raw_response.retrieve(
"",
)
@parametrize
async def test_method_update(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.update(
- webhook_id="webhookId",
+ webhook = await async_client.platform.webhooks.update(
+ webhook_id="pwh_abc123def456",
)
assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
@parametrize
async def test_method_update_with_all_params(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.update(
- webhook_id="webhookId",
- all_events=True,
+ webhook = await async_client.platform.webhooks.update(
+ webhook_id="pwh_abc123def456",
enabled=True,
- events=["string"],
- name="name",
+ events=["MessageSent"],
+ name="x",
url="https://example.com",
)
assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_update(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.update(
- webhook_id="webhookId",
+ response = await async_client.platform.webhooks.with_raw_response.update(
+ webhook_id="pwh_abc123def456",
)
assert response.is_closed is True
@@ -533,8 +496,8 @@ async def test_raw_response_update(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_update(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.update(
- webhook_id="webhookId",
+ async with async_client.platform.webhooks.with_streaming_response.update(
+ webhook_id="pwh_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -547,18 +510,18 @@ async def test_streaming_response_update(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_update(self, async_client: AsyncArk) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.update(
+ await async_client.platform.webhooks.with_raw_response.update(
webhook_id="",
)
@parametrize
async def test_method_list(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.list()
+ webhook = await async_client.platform.webhooks.list()
assert_matches_type(WebhookListResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_list(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.list()
+ response = await async_client.platform.webhooks.with_raw_response.list()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -567,7 +530,7 @@ async def test_raw_response_list(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.list() as response:
+ async with async_client.platform.webhooks.with_streaming_response.list() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -578,15 +541,15 @@ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
@parametrize
async def test_method_delete(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.delete(
- "webhookId",
+ webhook = await async_client.platform.webhooks.delete(
+ "pwh_abc123def456",
)
assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.delete(
- "webhookId",
+ response = await async_client.platform.webhooks.with_raw_response.delete(
+ "pwh_abc123def456",
)
assert response.is_closed is True
@@ -596,8 +559,8 @@ async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.delete(
- "webhookId",
+ async with async_client.platform.webhooks.with_streaming_response.delete(
+ "pwh_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -610,74 +573,60 @@ async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_delete(self, async_client: AsyncArk) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.delete(
+ await async_client.platform.webhooks.with_raw_response.delete(
"",
)
@parametrize
async def test_method_list_deliveries(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.list_deliveries(
- webhook_id="webhookId",
- )
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ webhook = await async_client.platform.webhooks.list_deliveries()
+ assert_matches_type(AsyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
@parametrize
async def test_method_list_deliveries_with_all_params(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.list_deliveries(
- webhook_id="webhookId",
+ webhook = await async_client.platform.webhooks.list_deliveries(
after=0,
before=0,
event="MessageSent",
- page=1,
- per_page=1,
+ page=0,
+ per_page=100,
success=True,
+ tenant_id="tenantId",
+ webhook_id="webhookId",
)
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ assert_matches_type(AsyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
@parametrize
async def test_raw_response_list_deliveries(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.list_deliveries(
- webhook_id="webhookId",
- )
+ response = await async_client.platform.webhooks.with_raw_response.list_deliveries()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
webhook = await response.parse()
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ assert_matches_type(AsyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
@parametrize
async def test_streaming_response_list_deliveries(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.list_deliveries(
- webhook_id="webhookId",
- ) as response:
+ async with async_client.platform.webhooks.with_streaming_response.list_deliveries() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
webhook = await response.parse()
- assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+ assert_matches_type(AsyncPageNumberPagination[WebhookListDeliveriesResponse], webhook, path=["response"])
assert cast(Any, response.is_closed) is True
- @parametrize
- async def test_path_params_list_deliveries(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.list_deliveries(
- webhook_id="",
- )
-
@parametrize
async def test_method_replay_delivery(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ webhook = await async_client.platform.webhooks.replay_delivery(
+ "pwd_abc123def456",
)
assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_replay_delivery(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ response = await async_client.platform.webhooks.with_raw_response.replay_delivery(
+ "pwd_abc123def456",
)
assert response.is_closed is True
@@ -687,9 +636,8 @@ async def test_raw_response_replay_delivery(self, async_client: AsyncArk) -> Non
@parametrize
async def test_streaming_response_replay_delivery(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ async with async_client.platform.webhooks.with_streaming_response.replay_delivery(
+ "pwd_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -701,31 +649,22 @@ async def test_streaming_response_replay_delivery(self, async_client: AsyncArk)
@parametrize
async def test_path_params_replay_delivery(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.replay_delivery(
- delivery_id="deliveryId",
- webhook_id="",
- )
-
with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
- await async_client.webhooks.with_raw_response.replay_delivery(
- delivery_id="",
- webhook_id="webhookId",
+ await async_client.platform.webhooks.with_raw_response.replay_delivery(
+ "",
)
@parametrize
async def test_method_retrieve_delivery(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ webhook = await async_client.platform.webhooks.retrieve_delivery(
+ "pwd_abc123def456",
)
assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_retrieve_delivery(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ response = await async_client.platform.webhooks.with_raw_response.retrieve_delivery(
+ "pwd_abc123def456",
)
assert response.is_closed is True
@@ -735,9 +674,8 @@ async def test_raw_response_retrieve_delivery(self, async_client: AsyncArk) -> N
@parametrize
async def test_streaming_response_retrieve_delivery(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="webhookId",
+ async with async_client.platform.webhooks.with_streaming_response.retrieve_delivery(
+ "pwd_abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -749,30 +687,23 @@ async def test_streaming_response_retrieve_delivery(self, async_client: AsyncArk
@parametrize
async def test_path_params_retrieve_delivery(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.retrieve_delivery(
- delivery_id="deliveryId",
- webhook_id="",
- )
-
with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
- await async_client.webhooks.with_raw_response.retrieve_delivery(
- delivery_id="",
- webhook_id="webhookId",
+ await async_client.platform.webhooks.with_raw_response.retrieve_delivery(
+ "",
)
@parametrize
async def test_method_test(self, async_client: AsyncArk) -> None:
- webhook = await async_client.webhooks.test(
- webhook_id="webhookId",
+ webhook = await async_client.platform.webhooks.test(
+ webhook_id="pwh_abc123def456",
event="MessageSent",
)
assert_matches_type(WebhookTestResponse, webhook, path=["response"])
@parametrize
async def test_raw_response_test(self, async_client: AsyncArk) -> None:
- response = await async_client.webhooks.with_raw_response.test(
- webhook_id="webhookId",
+ response = await async_client.platform.webhooks.with_raw_response.test(
+ webhook_id="pwh_abc123def456",
event="MessageSent",
)
@@ -783,8 +714,8 @@ async def test_raw_response_test(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_test(self, async_client: AsyncArk) -> None:
- async with async_client.webhooks.with_streaming_response.test(
- webhook_id="webhookId",
+ async with async_client.platform.webhooks.with_streaming_response.test(
+ webhook_id="pwh_abc123def456",
event="MessageSent",
) as response:
assert not response.is_closed
@@ -798,7 +729,7 @@ async def test_streaming_response_test(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_test(self, async_client: AsyncArk) -> None:
with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
- await async_client.webhooks.with_raw_response.test(
+ await async_client.platform.webhooks.with_raw_response.test(
webhook_id="",
event="MessageSent",
)
diff --git a/tests/api_resources/tenants/__init__.py b/tests/api_resources/tenants/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/tenants/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/tenants/test_credentials.py b/tests/api_resources/tenants/test_credentials.py
new file mode 100644
index 0000000..b919a73
--- /dev/null
+++ b/tests/api_resources/tenants/test_credentials.py
@@ -0,0 +1,509 @@
+# 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 ark import Ark, AsyncArk
+from tests.utils import assert_matches_type
+from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
+from ark.types.tenants import (
+ CredentialListResponse,
+ CredentialCreateResponse,
+ CredentialDeleteResponse,
+ CredentialUpdateResponse,
+ CredentialRetrieveResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestCredentials:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Ark) -> None:
+ credential = client.tenants.credentials.create(
+ tenant_id="cm6abc123def456",
+ name="production-smtp",
+ type="smtp",
+ )
+ assert_matches_type(CredentialCreateResponse, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Ark) -> None:
+ response = client.tenants.credentials.with_raw_response.create(
+ tenant_id="cm6abc123def456",
+ name="production-smtp",
+ type="smtp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(CredentialCreateResponse, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Ark) -> None:
+ with client.tenants.credentials.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
+ name="production-smtp",
+ type="smtp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(CredentialCreateResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.credentials.with_raw_response.create(
+ tenant_id="",
+ name="production-smtp",
+ type="smtp",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: Ark) -> None:
+ credential = client.tenants.credentials.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ @parametrize
+ def test_method_retrieve_with_all_params(self, client: Ark) -> None:
+ credential = client.tenants.credentials.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ reveal=True,
+ )
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Ark) -> None:
+ response = client.tenants.credentials.with_raw_response.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Ark) -> None:
+ with client.tenants.credentials.with_streaming_response.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.credentials.with_raw_response.retrieve(
+ credential_id=123,
+ tenant_id="",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Ark) -> None:
+ credential = client.tenants.credentials.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Ark) -> None:
+ credential = client.tenants.credentials.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ hold=True,
+ name="production-smtp-v2",
+ )
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Ark) -> None:
+ response = client.tenants.credentials.with_raw_response.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Ark) -> None:
+ with client.tenants.credentials.with_streaming_response.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.credentials.with_raw_response.update(
+ credential_id=123,
+ tenant_id="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Ark) -> None:
+ credential = client.tenants.credentials.list(
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(SyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Ark) -> None:
+ credential = client.tenants.credentials.list(
+ tenant_id="cm6abc123def456",
+ page=1,
+ per_page=1,
+ type="smtp",
+ )
+ assert_matches_type(SyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Ark) -> None:
+ response = client.tenants.credentials.with_raw_response.list(
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(SyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Ark) -> None:
+ with client.tenants.credentials.with_streaming_response.list(
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(SyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.credentials.with_raw_response.list(
+ tenant_id="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Ark) -> None:
+ credential = client.tenants.credentials.delete(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(CredentialDeleteResponse, credential, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Ark) -> None:
+ response = client.tenants.credentials.with_raw_response.delete(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = response.parse()
+ assert_matches_type(CredentialDeleteResponse, credential, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Ark) -> None:
+ with client.tenants.credentials.with_streaming_response.delete(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = response.parse()
+ assert_matches_type(CredentialDeleteResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.credentials.with_raw_response.delete(
+ credential_id=123,
+ tenant_id="",
+ )
+
+
+class TestAsyncCredentials:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.create(
+ tenant_id="cm6abc123def456",
+ name="production-smtp",
+ type="smtp",
+ )
+ assert_matches_type(CredentialCreateResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.credentials.with_raw_response.create(
+ tenant_id="cm6abc123def456",
+ name="production-smtp",
+ type="smtp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = await response.parse()
+ assert_matches_type(CredentialCreateResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.credentials.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
+ name="production-smtp",
+ type="smtp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(CredentialCreateResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.credentials.with_raw_response.create(
+ tenant_id="",
+ name="production-smtp",
+ type="smtp",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_method_retrieve_with_all_params(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ reveal=True,
+ )
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.credentials.with_raw_response.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = await response.parse()
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.credentials.with_streaming_response.retrieve(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(CredentialRetrieveResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.credentials.with_raw_response.retrieve(
+ credential_id=123,
+ tenant_id="",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ hold=True,
+ name="production-smtp-v2",
+ )
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.credentials.with_raw_response.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = await response.parse()
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.credentials.with_streaming_response.update(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(CredentialUpdateResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.credentials.with_raw_response.update(
+ credential_id=123,
+ tenant_id="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.list(
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(AsyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.list(
+ tenant_id="cm6abc123def456",
+ page=1,
+ per_page=1,
+ type="smtp",
+ )
+ assert_matches_type(AsyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.credentials.with_raw_response.list(
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = await response.parse()
+ assert_matches_type(AsyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.credentials.with_streaming_response.list(
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(AsyncPageNumberPagination[CredentialListResponse], credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.credentials.with_raw_response.list(
+ tenant_id="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncArk) -> None:
+ credential = await async_client.tenants.credentials.delete(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(CredentialDeleteResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.credentials.with_raw_response.delete(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ credential = await response.parse()
+ assert_matches_type(CredentialDeleteResponse, credential, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.credentials.with_streaming_response.delete(
+ credential_id=123,
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ credential = await response.parse()
+ assert_matches_type(CredentialDeleteResponse, credential, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.credentials.with_raw_response.delete(
+ credential_id=123,
+ tenant_id="",
+ )
diff --git a/tests/api_resources/test_domains.py b/tests/api_resources/tenants/test_domains.py
similarity index 60%
rename from tests/api_resources/test_domains.py
rename to tests/api_resources/tenants/test_domains.py
index 41dcfff..b6d9162 100644
--- a/tests/api_resources/test_domains.py
+++ b/tests/api_resources/tenants/test_domains.py
@@ -8,14 +8,14 @@
import pytest
from ark import Ark, AsyncArk
-from ark.types import (
+from tests.utils import assert_matches_type
+from ark.types.tenants import (
DomainListResponse,
DomainCreateResponse,
DomainDeleteResponse,
DomainVerifyResponse,
DomainRetrieveResponse,
)
-from tests.utils import assert_matches_type
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -25,14 +25,16 @@ class TestDomains:
@parametrize
def test_method_create(self, client: Ark) -> None:
- domain = client.domains.create(
+ domain = client.tenants.domains.create(
+ tenant_id="cm6abc123def456",
name="notifications.myapp.com",
)
assert_matches_type(DomainCreateResponse, domain, path=["response"])
@parametrize
def test_raw_response_create(self, client: Ark) -> None:
- response = client.domains.with_raw_response.create(
+ response = client.tenants.domains.with_raw_response.create(
+ tenant_id="cm6abc123def456",
name="notifications.myapp.com",
)
@@ -43,7 +45,8 @@ def test_raw_response_create(self, client: Ark) -> None:
@parametrize
def test_streaming_response_create(self, client: Ark) -> None:
- with client.domains.with_streaming_response.create(
+ with client.tenants.domains.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
name="notifications.myapp.com",
) as response:
assert not response.is_closed
@@ -54,17 +57,27 @@ def test_streaming_response_create(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_path_params_create(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.domains.with_raw_response.create(
+ tenant_id="",
+ name="notifications.myapp.com",
+ )
+
@parametrize
def test_method_retrieve(self, client: Ark) -> None:
- domain = client.domains.retrieve(
- "domainId",
+ domain = client.tenants.domains.retrieve(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(DomainRetrieveResponse, domain, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Ark) -> None:
- response = client.domains.with_raw_response.retrieve(
- "domainId",
+ response = client.tenants.domains.with_raw_response.retrieve(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -74,8 +87,9 @@ def test_raw_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Ark) -> None:
- with client.domains.with_streaming_response.retrieve(
- "domainId",
+ with client.tenants.domains.with_streaming_response.retrieve(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -87,19 +101,30 @@ def test_streaming_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.domains.with_raw_response.retrieve(
+ domain_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
- client.domains.with_raw_response.retrieve(
- "",
+ client.tenants.domains.with_raw_response.retrieve(
+ domain_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
def test_method_list(self, client: Ark) -> None:
- domain = client.domains.list()
+ domain = client.tenants.domains.list(
+ "cm6abc123def456",
+ )
assert_matches_type(DomainListResponse, domain, path=["response"])
@parametrize
def test_raw_response_list(self, client: Ark) -> None:
- response = client.domains.with_raw_response.list()
+ response = client.tenants.domains.with_raw_response.list(
+ "cm6abc123def456",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -108,7 +133,9 @@ def test_raw_response_list(self, client: Ark) -> None:
@parametrize
def test_streaming_response_list(self, client: Ark) -> None:
- with client.domains.with_streaming_response.list() as response:
+ with client.tenants.domains.with_streaming_response.list(
+ "cm6abc123def456",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -117,17 +144,26 @@ def test_streaming_response_list(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_path_params_list(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.domains.with_raw_response.list(
+ "",
+ )
+
@parametrize
def test_method_delete(self, client: Ark) -> None:
- domain = client.domains.delete(
- "domainId",
+ domain = client.tenants.domains.delete(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(DomainDeleteResponse, domain, path=["response"])
@parametrize
def test_raw_response_delete(self, client: Ark) -> None:
- response = client.domains.with_raw_response.delete(
- "domainId",
+ response = client.tenants.domains.with_raw_response.delete(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -137,8 +173,9 @@ def test_raw_response_delete(self, client: Ark) -> None:
@parametrize
def test_streaming_response_delete(self, client: Ark) -> None:
- with client.domains.with_streaming_response.delete(
- "domainId",
+ with client.tenants.domains.with_streaming_response.delete(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -150,22 +187,31 @@ def test_streaming_response_delete(self, client: Ark) -> None:
@parametrize
def test_path_params_delete(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.domains.with_raw_response.delete(
+ domain_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
- client.domains.with_raw_response.delete(
- "",
+ client.tenants.domains.with_raw_response.delete(
+ domain_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
def test_method_verify(self, client: Ark) -> None:
- domain = client.domains.verify(
- "domainId",
+ domain = client.tenants.domains.verify(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(DomainVerifyResponse, domain, path=["response"])
@parametrize
def test_raw_response_verify(self, client: Ark) -> None:
- response = client.domains.with_raw_response.verify(
- "domainId",
+ response = client.tenants.domains.with_raw_response.verify(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -175,8 +221,9 @@ def test_raw_response_verify(self, client: Ark) -> None:
@parametrize
def test_streaming_response_verify(self, client: Ark) -> None:
- with client.domains.with_streaming_response.verify(
- "domainId",
+ with client.tenants.domains.with_streaming_response.verify(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -188,9 +235,16 @@ def test_streaming_response_verify(self, client: Ark) -> None:
@parametrize
def test_path_params_verify(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.domains.with_raw_response.verify(
+ domain_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
- client.domains.with_raw_response.verify(
- "",
+ client.tenants.domains.with_raw_response.verify(
+ domain_id="",
+ tenant_id="cm6abc123def456",
)
@@ -201,14 +255,16 @@ class TestAsyncDomains:
@parametrize
async def test_method_create(self, async_client: AsyncArk) -> None:
- domain = await async_client.domains.create(
+ domain = await async_client.tenants.domains.create(
+ tenant_id="cm6abc123def456",
name="notifications.myapp.com",
)
assert_matches_type(DomainCreateResponse, domain, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncArk) -> None:
- response = await async_client.domains.with_raw_response.create(
+ response = await async_client.tenants.domains.with_raw_response.create(
+ tenant_id="cm6abc123def456",
name="notifications.myapp.com",
)
@@ -219,7 +275,8 @@ async def test_raw_response_create(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
- async with async_client.domains.with_streaming_response.create(
+ async with async_client.tenants.domains.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
name="notifications.myapp.com",
) as response:
assert not response.is_closed
@@ -230,17 +287,27 @@ async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.domains.with_raw_response.create(
+ tenant_id="",
+ name="notifications.myapp.com",
+ )
+
@parametrize
async def test_method_retrieve(self, async_client: AsyncArk) -> None:
- domain = await async_client.domains.retrieve(
- "domainId",
+ domain = await async_client.tenants.domains.retrieve(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(DomainRetrieveResponse, domain, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
- response = await async_client.domains.with_raw_response.retrieve(
- "domainId",
+ response = await async_client.tenants.domains.with_raw_response.retrieve(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -250,8 +317,9 @@ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
- async with async_client.domains.with_streaming_response.retrieve(
- "domainId",
+ async with async_client.tenants.domains.with_streaming_response.retrieve(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -263,19 +331,30 @@ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.domains.with_raw_response.retrieve(
+ domain_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
- await async_client.domains.with_raw_response.retrieve(
- "",
+ await async_client.tenants.domains.with_raw_response.retrieve(
+ domain_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
async def test_method_list(self, async_client: AsyncArk) -> None:
- domain = await async_client.domains.list()
+ domain = await async_client.tenants.domains.list(
+ "cm6abc123def456",
+ )
assert_matches_type(DomainListResponse, domain, path=["response"])
@parametrize
async def test_raw_response_list(self, async_client: AsyncArk) -> None:
- response = await async_client.domains.with_raw_response.list()
+ response = await async_client.tenants.domains.with_raw_response.list(
+ "cm6abc123def456",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -284,7 +363,9 @@ async def test_raw_response_list(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
- async with async_client.domains.with_streaming_response.list() as response:
+ async with async_client.tenants.domains.with_streaming_response.list(
+ "cm6abc123def456",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -293,17 +374,26 @@ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.domains.with_raw_response.list(
+ "",
+ )
+
@parametrize
async def test_method_delete(self, async_client: AsyncArk) -> None:
- domain = await async_client.domains.delete(
- "domainId",
+ domain = await async_client.tenants.domains.delete(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(DomainDeleteResponse, domain, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
- response = await async_client.domains.with_raw_response.delete(
- "domainId",
+ response = await async_client.tenants.domains.with_raw_response.delete(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -313,8 +403,9 @@ async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
- async with async_client.domains.with_streaming_response.delete(
- "domainId",
+ async with async_client.tenants.domains.with_streaming_response.delete(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -326,22 +417,31 @@ async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_delete(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.domains.with_raw_response.delete(
+ domain_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
- await async_client.domains.with_raw_response.delete(
- "",
+ await async_client.tenants.domains.with_raw_response.delete(
+ domain_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
async def test_method_verify(self, async_client: AsyncArk) -> None:
- domain = await async_client.domains.verify(
- "domainId",
+ domain = await async_client.tenants.domains.verify(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(DomainVerifyResponse, domain, path=["response"])
@parametrize
async def test_raw_response_verify(self, async_client: AsyncArk) -> None:
- response = await async_client.domains.with_raw_response.verify(
- "domainId",
+ response = await async_client.tenants.domains.with_raw_response.verify(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -351,8 +451,9 @@ async def test_raw_response_verify(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_verify(self, async_client: AsyncArk) -> None:
- async with async_client.domains.with_streaming_response.verify(
- "domainId",
+ async with async_client.tenants.domains.with_streaming_response.verify(
+ domain_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -364,7 +465,14 @@ async def test_streaming_response_verify(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_verify(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.domains.with_raw_response.verify(
+ domain_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `domain_id` but received ''"):
- await async_client.domains.with_raw_response.verify(
- "",
+ await async_client.tenants.domains.with_raw_response.verify(
+ domain_id="",
+ tenant_id="cm6abc123def456",
)
diff --git a/tests/api_resources/test_suppressions.py b/tests/api_resources/tenants/test_suppressions.py
similarity index 62%
rename from tests/api_resources/test_suppressions.py
rename to tests/api_resources/tenants/test_suppressions.py
index d7c0622..879beca 100644
--- a/tests/api_resources/test_suppressions.py
+++ b/tests/api_resources/tenants/test_suppressions.py
@@ -8,15 +8,14 @@
import pytest
from ark import Ark, AsyncArk
-from ark.types import (
+from tests.utils import assert_matches_type
+from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
+from ark.types.tenants import (
SuppressionListResponse,
SuppressionCreateResponse,
SuppressionDeleteResponse,
SuppressionRetrieveResponse,
- SuppressionBulkCreateResponse,
)
-from tests.utils import assert_matches_type
-from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -26,14 +25,16 @@ class TestSuppressions:
@parametrize
def test_method_create(self, client: Ark) -> None:
- suppression = client.suppressions.create(
+ suppression = client.tenants.suppressions.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
)
assert_matches_type(SuppressionCreateResponse, suppression, path=["response"])
@parametrize
def test_method_create_with_all_params(self, client: Ark) -> None:
- suppression = client.suppressions.create(
+ suppression = client.tenants.suppressions.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
reason="user requested removal",
)
@@ -41,7 +42,8 @@ def test_method_create_with_all_params(self, client: Ark) -> None:
@parametrize
def test_raw_response_create(self, client: Ark) -> None:
- response = client.suppressions.with_raw_response.create(
+ response = client.tenants.suppressions.with_raw_response.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
)
@@ -52,7 +54,8 @@ def test_raw_response_create(self, client: Ark) -> None:
@parametrize
def test_streaming_response_create(self, client: Ark) -> None:
- with client.suppressions.with_streaming_response.create(
+ with client.tenants.suppressions.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
) as response:
assert not response.is_closed
@@ -63,17 +66,27 @@ def test_streaming_response_create(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_path_params_create(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.suppressions.with_raw_response.create(
+ tenant_id="",
+ address="user@example.com",
+ )
+
@parametrize
def test_method_retrieve(self, client: Ark) -> None:
- suppression = client.suppressions.retrieve(
- "dev@stainless.com",
+ suppression = client.tenants.suppressions.retrieve(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(SuppressionRetrieveResponse, suppression, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Ark) -> None:
- response = client.suppressions.with_raw_response.retrieve(
- "dev@stainless.com",
+ response = client.tenants.suppressions.with_raw_response.retrieve(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -83,8 +96,9 @@ def test_raw_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Ark) -> None:
- with client.suppressions.with_streaming_response.retrieve(
- "dev@stainless.com",
+ with client.tenants.suppressions.with_streaming_response.retrieve(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -96,19 +110,29 @@ def test_streaming_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.suppressions.with_raw_response.retrieve(
+ email="user@example.com",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `email` but received ''"):
- client.suppressions.with_raw_response.retrieve(
- "",
+ client.tenants.suppressions.with_raw_response.retrieve(
+ email="",
+ tenant_id="cm6abc123def456",
)
@parametrize
def test_method_list(self, client: Ark) -> None:
- suppression = client.suppressions.list()
+ suppression = client.tenants.suppressions.list(
+ tenant_id="cm6abc123def456",
+ )
assert_matches_type(SyncPageNumberPagination[SuppressionListResponse], suppression, path=["response"])
@parametrize
def test_method_list_with_all_params(self, client: Ark) -> None:
- suppression = client.suppressions.list(
+ suppression = client.tenants.suppressions.list(
+ tenant_id="cm6abc123def456",
page=0,
per_page=100,
)
@@ -116,7 +140,9 @@ def test_method_list_with_all_params(self, client: Ark) -> None:
@parametrize
def test_raw_response_list(self, client: Ark) -> None:
- response = client.suppressions.with_raw_response.list()
+ response = client.tenants.suppressions.with_raw_response.list(
+ tenant_id="cm6abc123def456",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -125,7 +151,9 @@ def test_raw_response_list(self, client: Ark) -> None:
@parametrize
def test_streaming_response_list(self, client: Ark) -> None:
- with client.suppressions.with_streaming_response.list() as response:
+ with client.tenants.suppressions.with_streaming_response.list(
+ tenant_id="cm6abc123def456",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -134,17 +162,26 @@ def test_streaming_response_list(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_path_params_list(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.suppressions.with_raw_response.list(
+ tenant_id="",
+ )
+
@parametrize
def test_method_delete(self, client: Ark) -> None:
- suppression = client.suppressions.delete(
- "dev@stainless.com",
+ suppression = client.tenants.suppressions.delete(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(SuppressionDeleteResponse, suppression, path=["response"])
@parametrize
def test_raw_response_delete(self, client: Ark) -> None:
- response = client.suppressions.with_raw_response.delete(
- "dev@stainless.com",
+ response = client.tenants.suppressions.with_raw_response.delete(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -154,8 +191,9 @@ def test_raw_response_delete(self, client: Ark) -> None:
@parametrize
def test_streaming_response_delete(self, client: Ark) -> None:
- with client.suppressions.with_streaming_response.delete(
- "dev@stainless.com",
+ with client.tenants.suppressions.with_streaming_response.delete(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -167,41 +205,17 @@ def test_streaming_response_delete(self, client: Ark) -> None:
@parametrize
def test_path_params_delete(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `email` but received ''"):
- client.suppressions.with_raw_response.delete(
- "",
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.suppressions.with_raw_response.delete(
+ email="user@example.com",
+ tenant_id="",
)
- @parametrize
- def test_method_bulk_create(self, client: Ark) -> None:
- suppression = client.suppressions.bulk_create(
- suppressions=[{"address": "dev@stainless.com"}],
- )
- assert_matches_type(SuppressionBulkCreateResponse, suppression, path=["response"])
-
- @parametrize
- def test_raw_response_bulk_create(self, client: Ark) -> None:
- response = client.suppressions.with_raw_response.bulk_create(
- suppressions=[{"address": "dev@stainless.com"}],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- suppression = response.parse()
- assert_matches_type(SuppressionBulkCreateResponse, suppression, path=["response"])
-
- @parametrize
- def test_streaming_response_bulk_create(self, client: Ark) -> None:
- with client.suppressions.with_streaming_response.bulk_create(
- suppressions=[{"address": "dev@stainless.com"}],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- suppression = response.parse()
- assert_matches_type(SuppressionBulkCreateResponse, suppression, path=["response"])
-
- assert cast(Any, response.is_closed) is True
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email` but received ''"):
+ client.tenants.suppressions.with_raw_response.delete(
+ email="",
+ tenant_id="cm6abc123def456",
+ )
class TestAsyncSuppressions:
@@ -211,14 +225,16 @@ class TestAsyncSuppressions:
@parametrize
async def test_method_create(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.create(
+ suppression = await async_client.tenants.suppressions.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
)
assert_matches_type(SuppressionCreateResponse, suppression, path=["response"])
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.create(
+ suppression = await async_client.tenants.suppressions.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
reason="user requested removal",
)
@@ -226,7 +242,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncArk) -> No
@parametrize
async def test_raw_response_create(self, async_client: AsyncArk) -> None:
- response = await async_client.suppressions.with_raw_response.create(
+ response = await async_client.tenants.suppressions.with_raw_response.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
)
@@ -237,7 +254,8 @@ async def test_raw_response_create(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
- async with async_client.suppressions.with_streaming_response.create(
+ async with async_client.tenants.suppressions.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
address="user@example.com",
) as response:
assert not response.is_closed
@@ -248,17 +266,27 @@ async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.suppressions.with_raw_response.create(
+ tenant_id="",
+ address="user@example.com",
+ )
+
@parametrize
async def test_method_retrieve(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.retrieve(
- "dev@stainless.com",
+ suppression = await async_client.tenants.suppressions.retrieve(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(SuppressionRetrieveResponse, suppression, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
- response = await async_client.suppressions.with_raw_response.retrieve(
- "dev@stainless.com",
+ response = await async_client.tenants.suppressions.with_raw_response.retrieve(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -268,8 +296,9 @@ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
- async with async_client.suppressions.with_streaming_response.retrieve(
- "dev@stainless.com",
+ async with async_client.tenants.suppressions.with_streaming_response.retrieve(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -281,19 +310,29 @@ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.suppressions.with_raw_response.retrieve(
+ email="user@example.com",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `email` but received ''"):
- await async_client.suppressions.with_raw_response.retrieve(
- "",
+ await async_client.tenants.suppressions.with_raw_response.retrieve(
+ email="",
+ tenant_id="cm6abc123def456",
)
@parametrize
async def test_method_list(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.list()
+ suppression = await async_client.tenants.suppressions.list(
+ tenant_id="cm6abc123def456",
+ )
assert_matches_type(AsyncPageNumberPagination[SuppressionListResponse], suppression, path=["response"])
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.list(
+ suppression = await async_client.tenants.suppressions.list(
+ tenant_id="cm6abc123def456",
page=0,
per_page=100,
)
@@ -301,7 +340,9 @@ async def test_method_list_with_all_params(self, async_client: AsyncArk) -> None
@parametrize
async def test_raw_response_list(self, async_client: AsyncArk) -> None:
- response = await async_client.suppressions.with_raw_response.list()
+ response = await async_client.tenants.suppressions.with_raw_response.list(
+ tenant_id="cm6abc123def456",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -310,7 +351,9 @@ async def test_raw_response_list(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
- async with async_client.suppressions.with_streaming_response.list() as response:
+ async with async_client.tenants.suppressions.with_streaming_response.list(
+ tenant_id="cm6abc123def456",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -319,17 +362,26 @@ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.suppressions.with_raw_response.list(
+ tenant_id="",
+ )
+
@parametrize
async def test_method_delete(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.delete(
- "dev@stainless.com",
+ suppression = await async_client.tenants.suppressions.delete(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(SuppressionDeleteResponse, suppression, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
- response = await async_client.suppressions.with_raw_response.delete(
- "dev@stainless.com",
+ response = await async_client.tenants.suppressions.with_raw_response.delete(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -339,8 +391,9 @@ async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
- async with async_client.suppressions.with_streaming_response.delete(
- "dev@stainless.com",
+ async with async_client.tenants.suppressions.with_streaming_response.delete(
+ email="user@example.com",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -352,38 +405,14 @@ async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_delete(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `email` but received ''"):
- await async_client.suppressions.with_raw_response.delete(
- "",
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.suppressions.with_raw_response.delete(
+ email="user@example.com",
+ tenant_id="",
)
- @parametrize
- async def test_method_bulk_create(self, async_client: AsyncArk) -> None:
- suppression = await async_client.suppressions.bulk_create(
- suppressions=[{"address": "dev@stainless.com"}],
- )
- assert_matches_type(SuppressionBulkCreateResponse, suppression, path=["response"])
-
- @parametrize
- async def test_raw_response_bulk_create(self, async_client: AsyncArk) -> None:
- response = await async_client.suppressions.with_raw_response.bulk_create(
- suppressions=[{"address": "dev@stainless.com"}],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- suppression = await response.parse()
- assert_matches_type(SuppressionBulkCreateResponse, suppression, path=["response"])
-
- @parametrize
- async def test_streaming_response_bulk_create(self, async_client: AsyncArk) -> None:
- async with async_client.suppressions.with_streaming_response.bulk_create(
- suppressions=[{"address": "dev@stainless.com"}],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- suppression = await response.parse()
- assert_matches_type(SuppressionBulkCreateResponse, suppression, path=["response"])
-
- assert cast(Any, response.is_closed) is True
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email` but received ''"):
+ await async_client.tenants.suppressions.with_raw_response.delete(
+ email="",
+ tenant_id="cm6abc123def456",
+ )
diff --git a/tests/api_resources/test_tracking.py b/tests/api_resources/tenants/test_tracking.py
similarity index 61%
rename from tests/api_resources/test_tracking.py
rename to tests/api_resources/tenants/test_tracking.py
index 7cd008b..58fb912 100644
--- a/tests/api_resources/test_tracking.py
+++ b/tests/api_resources/tenants/test_tracking.py
@@ -8,7 +8,8 @@
import pytest
from ark import Ark, AsyncArk
-from ark.types import (
+from tests.utils import assert_matches_type
+from ark.types.tenants import (
TrackingListResponse,
TrackingCreateResponse,
TrackingDeleteResponse,
@@ -16,7 +17,6 @@
TrackingVerifyResponse,
TrackingRetrieveResponse,
)
-from tests.utils import assert_matches_type
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -26,7 +26,8 @@ class TestTracking:
@parametrize
def test_method_create(self, client: Ark) -> None:
- tracking = client.tracking.create(
+ tracking = client.tenants.tracking.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
)
@@ -34,7 +35,8 @@ def test_method_create(self, client: Ark) -> None:
@parametrize
def test_method_create_with_all_params(self, client: Ark) -> None:
- tracking = client.tracking.create(
+ tracking = client.tenants.tracking.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
ssl_enabled=True,
@@ -45,7 +47,8 @@ def test_method_create_with_all_params(self, client: Ark) -> None:
@parametrize
def test_raw_response_create(self, client: Ark) -> None:
- response = client.tracking.with_raw_response.create(
+ response = client.tenants.tracking.with_raw_response.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
)
@@ -57,7 +60,8 @@ def test_raw_response_create(self, client: Ark) -> None:
@parametrize
def test_streaming_response_create(self, client: Ark) -> None:
- with client.tracking.with_streaming_response.create(
+ with client.tenants.tracking.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
) as response:
@@ -69,17 +73,28 @@ def test_streaming_response_create(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_path_params_create(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.tracking.with_raw_response.create(
+ tenant_id="",
+ domain_id=123,
+ name="track",
+ )
+
@parametrize
def test_method_retrieve(self, client: Ark) -> None:
- tracking = client.tracking.retrieve(
- "trackingId",
+ tracking = client.tenants.tracking.retrieve(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingRetrieveResponse, tracking, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Ark) -> None:
- response = client.tracking.with_raw_response.retrieve(
- "trackingId",
+ response = client.tenants.tracking.with_raw_response.retrieve(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -89,8 +104,9 @@ def test_raw_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Ark) -> None:
- with client.tracking.with_streaming_response.retrieve(
- "trackingId",
+ with client.tenants.tracking.with_streaming_response.retrieve(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -102,22 +118,31 @@ def test_streaming_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.tracking.with_raw_response.retrieve(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- client.tracking.with_raw_response.retrieve(
- "",
+ client.tenants.tracking.with_raw_response.retrieve(
+ tracking_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
def test_method_update(self, client: Ark) -> None:
- tracking = client.tracking.update(
- tracking_id="trackingId",
+ tracking = client.tenants.tracking.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingUpdateResponse, tracking, path=["response"])
@parametrize
def test_method_update_with_all_params(self, client: Ark) -> None:
- tracking = client.tracking.update(
- tracking_id="trackingId",
+ tracking = client.tenants.tracking.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
excluded_click_domains="example.com,mysite.org",
ssl_enabled=True,
track_clicks=True,
@@ -127,8 +152,9 @@ def test_method_update_with_all_params(self, client: Ark) -> None:
@parametrize
def test_raw_response_update(self, client: Ark) -> None:
- response = client.tracking.with_raw_response.update(
- tracking_id="trackingId",
+ response = client.tenants.tracking.with_raw_response.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -138,8 +164,9 @@ def test_raw_response_update(self, client: Ark) -> None:
@parametrize
def test_streaming_response_update(self, client: Ark) -> None:
- with client.tracking.with_streaming_response.update(
- tracking_id="trackingId",
+ with client.tenants.tracking.with_streaming_response.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -151,19 +178,30 @@ def test_streaming_response_update(self, client: Ark) -> None:
@parametrize
def test_path_params_update(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.tracking.with_raw_response.update(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- client.tracking.with_raw_response.update(
+ client.tenants.tracking.with_raw_response.update(
tracking_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
def test_method_list(self, client: Ark) -> None:
- tracking = client.tracking.list()
+ tracking = client.tenants.tracking.list(
+ "cm6abc123def456",
+ )
assert_matches_type(TrackingListResponse, tracking, path=["response"])
@parametrize
def test_raw_response_list(self, client: Ark) -> None:
- response = client.tracking.with_raw_response.list()
+ response = client.tenants.tracking.with_raw_response.list(
+ "cm6abc123def456",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -172,7 +210,9 @@ def test_raw_response_list(self, client: Ark) -> None:
@parametrize
def test_streaming_response_list(self, client: Ark) -> None:
- with client.tracking.with_streaming_response.list() as response:
+ with client.tenants.tracking.with_streaming_response.list(
+ "cm6abc123def456",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -181,17 +221,26 @@ def test_streaming_response_list(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_path_params_list(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.tracking.with_raw_response.list(
+ "",
+ )
+
@parametrize
def test_method_delete(self, client: Ark) -> None:
- tracking = client.tracking.delete(
- "trackingId",
+ tracking = client.tenants.tracking.delete(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingDeleteResponse, tracking, path=["response"])
@parametrize
def test_raw_response_delete(self, client: Ark) -> None:
- response = client.tracking.with_raw_response.delete(
- "trackingId",
+ response = client.tenants.tracking.with_raw_response.delete(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -201,8 +250,9 @@ def test_raw_response_delete(self, client: Ark) -> None:
@parametrize
def test_streaming_response_delete(self, client: Ark) -> None:
- with client.tracking.with_streaming_response.delete(
- "trackingId",
+ with client.tenants.tracking.with_streaming_response.delete(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -214,22 +264,31 @@ def test_streaming_response_delete(self, client: Ark) -> None:
@parametrize
def test_path_params_delete(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.tracking.with_raw_response.delete(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- client.tracking.with_raw_response.delete(
- "",
+ client.tenants.tracking.with_raw_response.delete(
+ tracking_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
def test_method_verify(self, client: Ark) -> None:
- tracking = client.tracking.verify(
- "trackingId",
+ tracking = client.tenants.tracking.verify(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingVerifyResponse, tracking, path=["response"])
@parametrize
def test_raw_response_verify(self, client: Ark) -> None:
- response = client.tracking.with_raw_response.verify(
- "trackingId",
+ response = client.tenants.tracking.with_raw_response.verify(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -239,8 +298,9 @@ def test_raw_response_verify(self, client: Ark) -> None:
@parametrize
def test_streaming_response_verify(self, client: Ark) -> None:
- with client.tracking.with_streaming_response.verify(
- "trackingId",
+ with client.tenants.tracking.with_streaming_response.verify(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -252,9 +312,16 @@ def test_streaming_response_verify(self, client: Ark) -> None:
@parametrize
def test_path_params_verify(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.tracking.with_raw_response.verify(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- client.tracking.with_raw_response.verify(
- "",
+ client.tenants.tracking.with_raw_response.verify(
+ tracking_id="",
+ tenant_id="cm6abc123def456",
)
@@ -265,7 +332,8 @@ class TestAsyncTracking:
@parametrize
async def test_method_create(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.create(
+ tracking = await async_client.tenants.tracking.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
)
@@ -273,7 +341,8 @@ async def test_method_create(self, async_client: AsyncArk) -> None:
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.create(
+ tracking = await async_client.tenants.tracking.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
ssl_enabled=True,
@@ -284,7 +353,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncArk) -> No
@parametrize
async def test_raw_response_create(self, async_client: AsyncArk) -> None:
- response = await async_client.tracking.with_raw_response.create(
+ response = await async_client.tenants.tracking.with_raw_response.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
)
@@ -296,7 +366,8 @@ async def test_raw_response_create(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
- async with async_client.tracking.with_streaming_response.create(
+ async with async_client.tenants.tracking.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
domain_id=123,
name="track",
) as response:
@@ -308,17 +379,28 @@ async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.tracking.with_raw_response.create(
+ tenant_id="",
+ domain_id=123,
+ name="track",
+ )
+
@parametrize
async def test_method_retrieve(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.retrieve(
- "trackingId",
+ tracking = await async_client.tenants.tracking.retrieve(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingRetrieveResponse, tracking, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
- response = await async_client.tracking.with_raw_response.retrieve(
- "trackingId",
+ response = await async_client.tenants.tracking.with_raw_response.retrieve(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -328,8 +410,9 @@ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
- async with async_client.tracking.with_streaming_response.retrieve(
- "trackingId",
+ async with async_client.tenants.tracking.with_streaming_response.retrieve(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -341,22 +424,31 @@ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.tracking.with_raw_response.retrieve(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- await async_client.tracking.with_raw_response.retrieve(
- "",
+ await async_client.tenants.tracking.with_raw_response.retrieve(
+ tracking_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
async def test_method_update(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.update(
- tracking_id="trackingId",
+ tracking = await async_client.tenants.tracking.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingUpdateResponse, tracking, path=["response"])
@parametrize
async def test_method_update_with_all_params(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.update(
- tracking_id="trackingId",
+ tracking = await async_client.tenants.tracking.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
excluded_click_domains="example.com,mysite.org",
ssl_enabled=True,
track_clicks=True,
@@ -366,8 +458,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncArk) -> No
@parametrize
async def test_raw_response_update(self, async_client: AsyncArk) -> None:
- response = await async_client.tracking.with_raw_response.update(
- tracking_id="trackingId",
+ response = await async_client.tenants.tracking.with_raw_response.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -377,8 +470,9 @@ async def test_raw_response_update(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_update(self, async_client: AsyncArk) -> None:
- async with async_client.tracking.with_streaming_response.update(
- tracking_id="trackingId",
+ async with async_client.tenants.tracking.with_streaming_response.update(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -390,19 +484,30 @@ async def test_streaming_response_update(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_update(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.tracking.with_raw_response.update(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- await async_client.tracking.with_raw_response.update(
+ await async_client.tenants.tracking.with_raw_response.update(
tracking_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
async def test_method_list(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.list()
+ tracking = await async_client.tenants.tracking.list(
+ "cm6abc123def456",
+ )
assert_matches_type(TrackingListResponse, tracking, path=["response"])
@parametrize
async def test_raw_response_list(self, async_client: AsyncArk) -> None:
- response = await async_client.tracking.with_raw_response.list()
+ response = await async_client.tenants.tracking.with_raw_response.list(
+ "cm6abc123def456",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -411,7 +516,9 @@ async def test_raw_response_list(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
- async with async_client.tracking.with_streaming_response.list() as response:
+ async with async_client.tenants.tracking.with_streaming_response.list(
+ "cm6abc123def456",
+ ) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -420,17 +527,26 @@ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.tracking.with_raw_response.list(
+ "",
+ )
+
@parametrize
async def test_method_delete(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.delete(
- "trackingId",
+ tracking = await async_client.tenants.tracking.delete(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingDeleteResponse, tracking, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
- response = await async_client.tracking.with_raw_response.delete(
- "trackingId",
+ response = await async_client.tenants.tracking.with_raw_response.delete(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -440,8 +556,9 @@ async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
- async with async_client.tracking.with_streaming_response.delete(
- "trackingId",
+ async with async_client.tenants.tracking.with_streaming_response.delete(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -453,22 +570,31 @@ async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_delete(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.tracking.with_raw_response.delete(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- await async_client.tracking.with_raw_response.delete(
- "",
+ await async_client.tenants.tracking.with_raw_response.delete(
+ tracking_id="",
+ tenant_id="cm6abc123def456",
)
@parametrize
async def test_method_verify(self, async_client: AsyncArk) -> None:
- tracking = await async_client.tracking.verify(
- "trackingId",
+ tracking = await async_client.tenants.tracking.verify(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert_matches_type(TrackingVerifyResponse, tracking, path=["response"])
@parametrize
async def test_raw_response_verify(self, async_client: AsyncArk) -> None:
- response = await async_client.tracking.with_raw_response.verify(
- "trackingId",
+ response = await async_client.tenants.tracking.with_raw_response.verify(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
)
assert response.is_closed is True
@@ -478,8 +604,9 @@ async def test_raw_response_verify(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_verify(self, async_client: AsyncArk) -> None:
- async with async_client.tracking.with_streaming_response.verify(
- "trackingId",
+ async with async_client.tenants.tracking.with_streaming_response.verify(
+ tracking_id="123",
+ tenant_id="cm6abc123def456",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -491,7 +618,14 @@ async def test_streaming_response_verify(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_verify(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.tracking.with_raw_response.verify(
+ tracking_id="123",
+ tenant_id="",
+ )
+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `tracking_id` but received ''"):
- await async_client.tracking.with_raw_response.verify(
- "",
+ await async_client.tenants.tracking.with_raw_response.verify(
+ tracking_id="",
+ tenant_id="cm6abc123def456",
)
diff --git a/tests/api_resources/tenants/test_usage.py b/tests/api_resources/tenants/test_usage.py
new file mode 100644
index 0000000..edb2820
--- /dev/null
+++ b/tests/api_resources/tenants/test_usage.py
@@ -0,0 +1,217 @@
+# 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 ark import Ark, AsyncArk
+from tests.utils import assert_matches_type
+from ark.types.tenants import (
+ UsageRetrieveResponse,
+ UsageRetrieveTimeseriesResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestUsage:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Ark) -> None:
+ usage = client.tenants.usage.retrieve(
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ def test_method_retrieve_with_all_params(self, client: Ark) -> None:
+ usage = client.tenants.usage.retrieve(
+ tenant_id="cm6abc123def456",
+ period="period",
+ timezone="timezone",
+ )
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Ark) -> None:
+ response = client.tenants.usage.with_raw_response.retrieve(
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Ark) -> None:
+ with client.tenants.usage.with_streaming_response.retrieve(
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.usage.with_raw_response.retrieve(
+ tenant_id="",
+ )
+
+ @parametrize
+ def test_method_retrieve_timeseries(self, client: Ark) -> None:
+ usage = client.tenants.usage.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ @parametrize
+ def test_method_retrieve_timeseries_with_all_params(self, client: Ark) -> None:
+ usage = client.tenants.usage.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ granularity="hour",
+ period="period",
+ timezone="timezone",
+ )
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve_timeseries(self, client: Ark) -> None:
+ response = client.tenants.usage.with_raw_response.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = response.parse()
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve_timeseries(self, client: Ark) -> None:
+ with client.tenants.usage.with_streaming_response.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = response.parse()
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve_timeseries(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.usage.with_raw_response.retrieve_timeseries(
+ tenant_id="",
+ )
+
+
+class TestAsyncUsage:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncArk) -> None:
+ usage = await async_client.tenants.usage.retrieve(
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_method_retrieve_with_all_params(self, async_client: AsyncArk) -> None:
+ usage = await async_client.tenants.usage.retrieve(
+ tenant_id="cm6abc123def456",
+ period="period",
+ timezone="timezone",
+ )
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.usage.with_raw_response.retrieve(
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = await response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.usage.with_streaming_response.retrieve(
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = await response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.usage.with_raw_response.retrieve(
+ tenant_id="",
+ )
+
+ @parametrize
+ async def test_method_retrieve_timeseries(self, async_client: AsyncArk) -> None:
+ usage = await async_client.tenants.usage.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_method_retrieve_timeseries_with_all_params(self, async_client: AsyncArk) -> None:
+ usage = await async_client.tenants.usage.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ granularity="hour",
+ period="period",
+ timezone="timezone",
+ )
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve_timeseries(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.usage.with_raw_response.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = await response.parse()
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve_timeseries(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.usage.with_streaming_response.retrieve_timeseries(
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = await response.parse()
+ assert_matches_type(UsageRetrieveTimeseriesResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve_timeseries(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.usage.with_raw_response.retrieve_timeseries(
+ tenant_id="",
+ )
diff --git a/tests/api_resources/tenants/test_webhooks.py b/tests/api_resources/tenants/test_webhooks.py
new file mode 100644
index 0000000..8a5284f
--- /dev/null
+++ b/tests/api_resources/tenants/test_webhooks.py
@@ -0,0 +1,1010 @@
+# 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 ark import Ark, AsyncArk
+from tests.utils import assert_matches_type
+from ark.types.tenants import (
+ WebhookListResponse,
+ WebhookTestResponse,
+ WebhookCreateResponse,
+ WebhookDeleteResponse,
+ WebhookUpdateResponse,
+ WebhookRetrieveResponse,
+ WebhookListDeliveriesResponse,
+ WebhookReplayDeliveryResponse,
+ WebhookRetrieveDeliveryResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestWebhooks:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ )
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ all_events=True,
+ enabled=True,
+ events=["MessageSent", "MessageDeliveryFailed", "MessageBounced"],
+ )
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.create(
+ tenant_id="",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.retrieve(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.retrieve(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.retrieve(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.retrieve(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.retrieve(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ all_events=True,
+ enabled=True,
+ events=["string"],
+ name="name",
+ url="https://example.com",
+ )
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.update(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.update(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.list(
+ "cm6abc123def456",
+ )
+ assert_matches_type(WebhookListResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.list(
+ "cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookListResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.list(
+ "cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookListResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.list(
+ "",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.delete(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.delete(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.delete(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.delete(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.delete(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ def test_method_list_deliveries(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_method_list_deliveries_with_all_params(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ after=0,
+ before=0,
+ event="MessageSent",
+ page=1,
+ per_page=1,
+ success=True,
+ )
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_deliveries(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_deliveries(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list_deliveries(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.list_deliveries(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.list_deliveries(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ def test_method_replay_delivery(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+ assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_replay_delivery(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_replay_delivery(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_replay_delivery(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="",
+ webhook_id="123",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ @parametrize
+ def test_method_retrieve_delivery(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+ assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve_delivery(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve_delivery(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve_delivery(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="",
+ webhook_id="123",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ @parametrize
+ def test_method_test(self, client: Ark) -> None:
+ webhook = client.tenants.webhooks.test(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ )
+ assert_matches_type(WebhookTestResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_raw_response_test(self, client: Ark) -> None:
+ response = client.tenants.webhooks.with_raw_response.test(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = response.parse()
+ assert_matches_type(WebhookTestResponse, webhook, path=["response"])
+
+ @parametrize
+ def test_streaming_response_test(self, client: Ark) -> None:
+ with client.tenants.webhooks.with_streaming_response.test(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = response.parse()
+ assert_matches_type(WebhookTestResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_test(self, client: Ark) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.test(
+ webhook_id="123",
+ tenant_id="",
+ event="MessageSent",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ client.tenants.webhooks.with_raw_response.test(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ )
+
+
+class TestAsyncWebhooks:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ )
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ all_events=True,
+ enabled=True,
+ events=["MessageSent", "MessageDeliveryFailed", "MessageBounced"],
+ )
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.create(
+ tenant_id="cm6abc123def456",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookCreateResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.create(
+ tenant_id="",
+ name="My App Webhook",
+ url="https://myapp.com/webhooks/email",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.retrieve(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.retrieve(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.retrieve(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.retrieve(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.retrieve(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ all_events=True,
+ enabled=True,
+ events=["string"],
+ name="name",
+ url="https://example.com",
+ )
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.update(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookUpdateResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.update(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.update(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.list(
+ "cm6abc123def456",
+ )
+ assert_matches_type(WebhookListResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.list(
+ "cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookListResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.list(
+ "cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookListResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.list(
+ "",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.delete(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.delete(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.delete(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookDeleteResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.delete(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.delete(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ async def test_method_list_deliveries(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_method_list_deliveries_with_all_params(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ after=0,
+ before=0,
+ event="MessageSent",
+ page=1,
+ per_page=1,
+ success=True,
+ )
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_deliveries(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_deliveries(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.list_deliveries(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookListDeliveriesResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list_deliveries(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.list_deliveries(
+ webhook_id="123",
+ tenant_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.list_deliveries(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ )
+
+ @parametrize
+ async def test_method_replay_delivery(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+ assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_replay_delivery(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_replay_delivery(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookReplayDeliveryResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_replay_delivery(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="",
+ webhook_id="123",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.replay_delivery(
+ delivery_id="",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ @parametrize
+ async def test_method_retrieve_delivery(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+ assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve_delivery(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve_delivery(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookRetrieveDeliveryResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve_delivery(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="",
+ webhook_id="123",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="whr_abc123def456",
+ tenant_id="cm6abc123def456",
+ webhook_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `delivery_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.retrieve_delivery(
+ delivery_id="",
+ tenant_id="cm6abc123def456",
+ webhook_id="123",
+ )
+
+ @parametrize
+ async def test_method_test(self, async_client: AsyncArk) -> None:
+ webhook = await async_client.tenants.webhooks.test(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ )
+ assert_matches_type(WebhookTestResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_raw_response_test(self, async_client: AsyncArk) -> None:
+ response = await async_client.tenants.webhooks.with_raw_response.test(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ webhook = await response.parse()
+ assert_matches_type(WebhookTestResponse, webhook, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_test(self, async_client: AsyncArk) -> None:
+ async with async_client.tenants.webhooks.with_streaming_response.test(
+ webhook_id="123",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ webhook = await response.parse()
+ assert_matches_type(WebhookTestResponse, webhook, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_test(self, async_client: AsyncArk) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `tenant_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.test(
+ webhook_id="123",
+ tenant_id="",
+ event="MessageSent",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `webhook_id` but received ''"):
+ await async_client.tenants.webhooks.with_raw_response.test(
+ webhook_id="",
+ tenant_id="cm6abc123def456",
+ event="MessageSent",
+ )
diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py
index 7a54df7..0e37b72 100644
--- a/tests/api_resources/test_emails.py
+++ b/tests/api_resources/test_emails.py
@@ -29,14 +29,14 @@ class TestEmails:
@parametrize
def test_method_retrieve(self, client: Ark) -> None:
email = client.emails.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
)
assert_matches_type(EmailRetrieveResponse, email, path=["response"])
@parametrize
def test_method_retrieve_with_all_params(self, client: Ark) -> None:
email = client.emails.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
expand="full",
)
assert_matches_type(EmailRetrieveResponse, email, path=["response"])
@@ -44,7 +44,7 @@ def test_method_retrieve_with_all_params(self, client: Ark) -> None:
@parametrize
def test_raw_response_retrieve(self, client: Ark) -> None:
response = client.emails.with_raw_response.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
)
assert response.is_closed is True
@@ -55,7 +55,7 @@ def test_raw_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Ark) -> None:
with client.emails.with_streaming_response.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -67,9 +67,9 @@ def test_streaming_response_retrieve(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email_id` but received ''"):
client.emails.with_raw_response.retrieve(
- id="",
+ email_id="",
)
@parametrize
@@ -144,7 +144,7 @@ def test_streaming_response_retrieve_deliveries(self, client: Ark) -> None:
@parametrize
def test_path_params_retrieve_deliveries(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email_id` but received ''"):
client.emails.with_raw_response.retrieve_deliveries(
"",
)
@@ -182,7 +182,7 @@ def test_streaming_response_retry(self, client: Ark) -> None:
@parametrize
def test_path_params_retry(self, client: Ark) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email_id` but received ''"):
client.emails.with_raw_response.retry(
"",
)
@@ -403,14 +403,14 @@ class TestAsyncEmails:
@parametrize
async def test_method_retrieve(self, async_client: AsyncArk) -> None:
email = await async_client.emails.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
)
assert_matches_type(EmailRetrieveResponse, email, path=["response"])
@parametrize
async def test_method_retrieve_with_all_params(self, async_client: AsyncArk) -> None:
email = await async_client.emails.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
expand="full",
)
assert_matches_type(EmailRetrieveResponse, email, path=["response"])
@@ -418,7 +418,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncArk) ->
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
response = await async_client.emails.with_raw_response.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
)
assert response.is_closed is True
@@ -429,7 +429,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
async with async_client.emails.with_streaming_response.retrieve(
- id="aBc123XyZ",
+ email_id="aBc123XyZ",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -441,9 +441,9 @@ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None
@parametrize
async def test_path_params_retrieve(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email_id` but received ''"):
await async_client.emails.with_raw_response.retrieve(
- id="",
+ email_id="",
)
@parametrize
@@ -518,7 +518,7 @@ async def test_streaming_response_retrieve_deliveries(self, async_client: AsyncA
@parametrize
async def test_path_params_retrieve_deliveries(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email_id` but received ''"):
await async_client.emails.with_raw_response.retrieve_deliveries(
"",
)
@@ -556,7 +556,7 @@ async def test_streaming_response_retry(self, async_client: AsyncArk) -> None:
@parametrize
async def test_path_params_retry(self, async_client: AsyncArk) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `email_id` but received ''"):
await async_client.emails.with_raw_response.retry(
"",
)
diff --git a/tests/api_resources/test_limits.py b/tests/api_resources/test_limits.py
new file mode 100644
index 0000000..f303d24
--- /dev/null
+++ b/tests/api_resources/test_limits.py
@@ -0,0 +1,74 @@
+# 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 ark import Ark, AsyncArk
+from ark.types import LimitRetrieveResponse
+from tests.utils import assert_matches_type
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLimits:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Ark) -> None:
+ limit = client.limits.retrieve()
+ assert_matches_type(LimitRetrieveResponse, limit, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Ark) -> None:
+ response = client.limits.with_raw_response.retrieve()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ limit = response.parse()
+ assert_matches_type(LimitRetrieveResponse, limit, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Ark) -> None:
+ with client.limits.with_streaming_response.retrieve() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ limit = response.parse()
+ assert_matches_type(LimitRetrieveResponse, limit, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncLimits:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncArk) -> None:
+ limit = await async_client.limits.retrieve()
+ assert_matches_type(LimitRetrieveResponse, limit, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
+ response = await async_client.limits.with_raw_response.retrieve()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ limit = await response.parse()
+ assert_matches_type(LimitRetrieveResponse, limit, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
+ async with async_client.limits.with_streaming_response.retrieve() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ limit = await response.parse()
+ assert_matches_type(LimitRetrieveResponse, limit, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_tenants.py b/tests/api_resources/test_tenants.py
index 0f02dd2..0d4c436 100644
--- a/tests/api_resources/test_tenants.py
+++ b/tests/api_resources/test_tenants.py
@@ -37,7 +37,7 @@ def test_method_create_with_all_params(self, client: Ark) -> None:
name="Acme Corp",
metadata={
"plan": "pro",
- "internal_id": "cust_12345",
+ "internalId": "cust_12345",
"region": "us-west",
},
)
@@ -118,7 +118,7 @@ def test_method_update_with_all_params(self, client: Ark) -> None:
tenant_id="cm6abc123def456",
metadata={
"plan": "pro",
- "internal_id": "cust_12345",
+ "internalId": "cust_12345",
"region": "us-west",
},
name="Acme Corporation",
@@ -248,7 +248,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncArk) -> No
name="Acme Corp",
metadata={
"plan": "pro",
- "internal_id": "cust_12345",
+ "internalId": "cust_12345",
"region": "us-west",
},
)
@@ -329,7 +329,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncArk) -> No
tenant_id="cm6abc123def456",
metadata={
"plan": "pro",
- "internal_id": "cust_12345",
+ "internalId": "cust_12345",
"region": "us-west",
},
name="Acme Corporation",
diff --git a/tests/api_resources/test_usage.py b/tests/api_resources/test_usage.py
index 767da80..69d4699 100644
--- a/tests/api_resources/test_usage.py
+++ b/tests/api_resources/test_usage.py
@@ -8,8 +8,13 @@
import pytest
from ark import Ark, AsyncArk
-from ark.types import UsageRetrieveResponse
+from ark.types import (
+ OrgUsageSummary,
+ TenantUsageItem,
+ UsageExportResponse,
+)
from tests.utils import assert_matches_type
+from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -20,7 +25,15 @@ class TestUsage:
@parametrize
def test_method_retrieve(self, client: Ark) -> None:
usage = client.usage.retrieve()
- assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
+
+ @parametrize
+ def test_method_retrieve_with_all_params(self, client: Ark) -> None:
+ usage = client.usage.retrieve(
+ period="period",
+ timezone="timezone",
+ )
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Ark) -> None:
@@ -29,7 +42,7 @@ def test_raw_response_retrieve(self, client: Ark) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
usage = response.parse()
- assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
@parametrize
def test_streaming_response_retrieve(self, client: Ark) -> None:
@@ -38,7 +51,81 @@ def test_streaming_response_retrieve(self, client: Ark) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
usage = response.parse()
- assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_export(self, client: Ark) -> None:
+ usage = client.usage.export()
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ @parametrize
+ def test_method_export_with_all_params(self, client: Ark) -> None:
+ usage = client.usage.export(
+ format="csv",
+ min_sent=0,
+ period="period",
+ status="active",
+ timezone="timezone",
+ )
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ @parametrize
+ def test_raw_response_export(self, client: Ark) -> None:
+ response = client.usage.with_raw_response.export()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = response.parse()
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_export(self, client: Ark) -> None:
+ with client.usage.with_streaming_response.export() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = response.parse()
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_list_tenants(self, client: Ark) -> None:
+ usage = client.usage.list_tenants()
+ assert_matches_type(SyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
+
+ @parametrize
+ def test_method_list_tenants_with_all_params(self, client: Ark) -> None:
+ usage = client.usage.list_tenants(
+ min_sent=0,
+ page=1,
+ period="period",
+ per_page=1,
+ sort="sent",
+ status="active",
+ timezone="timezone",
+ )
+ assert_matches_type(SyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_tenants(self, client: Ark) -> None:
+ response = client.usage.with_raw_response.list_tenants()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = response.parse()
+ assert_matches_type(SyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_tenants(self, client: Ark) -> None:
+ with client.usage.with_streaming_response.list_tenants() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = response.parse()
+ assert_matches_type(SyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -51,7 +138,15 @@ class TestAsyncUsage:
@parametrize
async def test_method_retrieve(self, async_client: AsyncArk) -> None:
usage = await async_client.usage.retrieve()
- assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
+
+ @parametrize
+ async def test_method_retrieve_with_all_params(self, async_client: AsyncArk) -> None:
+ usage = await async_client.usage.retrieve(
+ period="period",
+ timezone="timezone",
+ )
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
@@ -60,7 +155,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
usage = await response.parse()
- assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
@@ -69,6 +164,80 @@ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
usage = await response.parse()
- assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+ assert_matches_type(OrgUsageSummary, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_export(self, async_client: AsyncArk) -> None:
+ usage = await async_client.usage.export()
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_method_export_with_all_params(self, async_client: AsyncArk) -> None:
+ usage = await async_client.usage.export(
+ format="csv",
+ min_sent=0,
+ period="period",
+ status="active",
+ timezone="timezone",
+ )
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_export(self, async_client: AsyncArk) -> None:
+ response = await async_client.usage.with_raw_response.export()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = await response.parse()
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_export(self, async_client: AsyncArk) -> None:
+ async with async_client.usage.with_streaming_response.export() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = await response.parse()
+ assert_matches_type(UsageExportResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_list_tenants(self, async_client: AsyncArk) -> None:
+ usage = await async_client.usage.list_tenants()
+ assert_matches_type(AsyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
+
+ @parametrize
+ async def test_method_list_tenants_with_all_params(self, async_client: AsyncArk) -> None:
+ usage = await async_client.usage.list_tenants(
+ min_sent=0,
+ page=1,
+ period="period",
+ per_page=1,
+ sort="sent",
+ status="active",
+ timezone="timezone",
+ )
+ assert_matches_type(AsyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_tenants(self, async_client: AsyncArk) -> None:
+ response = await async_client.usage.with_raw_response.list_tenants()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = await response.parse()
+ assert_matches_type(AsyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_tenants(self, async_client: AsyncArk) -> None:
+ async with async_client.usage.with_streaming_response.list_tenants() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = await response.parse()
+ assert_matches_type(AsyncPageNumberPagination[TenantUsageItem], usage, path=["response"])
assert cast(Any, response.is_closed) is True