From 56309b8f34212923562d01dbad53df307d1a6a97 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 30 Jan 2026 15:26:31 +0000
Subject: [PATCH 1/3] feat(api): api update
---
.stats.yml | 6 +-
api.md | 6 -
src/ark/resources/emails.py | 363 +-----------------
src/ark/resources/webhooks.py | 12 +
src/ark/types/__init__.py | 4 -
src/ark/types/email_list_response.py | 4 +-
.../email_retrieve_deliveries_response.py | 130 -------
src/ark/types/email_retrieve_params.py | 21 -
src/ark/types/email_retrieve_response.py | 184 ---------
src/ark/types/email_retry_response.py | 23 --
src/ark/types/email_send_batch_response.py | 4 +-
src/ark/types/email_send_raw_response.py | 2 +-
src/ark/types/email_send_response.py | 2 +-
src/ark/types/log_entry.py | 2 +-
src/ark/types/webhook_create_params.py | 2 +
src/ark/types/webhook_create_response.py | 2 +
.../types/webhook_list_deliveries_params.py | 2 +
.../types/webhook_list_deliveries_response.py | 2 +
.../webhook_retrieve_delivery_response.py | 2 +
src/ark/types/webhook_retrieve_response.py | 2 +
src/ark/types/webhook_test_params.py | 2 +
src/ark/types/webhook_update_response.py | 2 +
tests/api_resources/test_emails.py | 247 ------------
23 files changed, 41 insertions(+), 985 deletions(-)
delete mode 100644 src/ark/types/email_retrieve_deliveries_response.py
delete mode 100644 src/ark/types/email_retrieve_params.py
delete mode 100644 src/ark/types/email_retrieve_response.py
delete mode 100644 src/ark/types/email_retry_response.py
diff --git a/.stats.yml b/.stats.yml
index 69b13d9..631edab 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 35
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-39b91ffd46b6e41924f8465ffaaff6ba3c200a68daa513d4f1eb1e4b29aba78f.yml
-openapi_spec_hash: 542dd50007316698c83e8b0bdd5e40e2
+configured_endpoints: 32
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-65d92724dca2fcf00a43883ad10e0591b457891aa45ed965b0414b27388b5e16.yml
+openapi_spec_hash: 42824aaa51f95c0f485c91600969673f
config_hash: 77a3908ee910a8019f5831d3a3d53c18
diff --git a/api.md b/api.md
index 7baaad3..c296d0a 100644
--- a/api.md
+++ b/api.md
@@ -10,10 +10,7 @@ Types:
```python
from ark.types import (
- EmailRetrieveResponse,
EmailListResponse,
- EmailRetrieveDeliveriesResponse,
- EmailRetryResponse,
EmailSendResponse,
EmailSendBatchResponse,
EmailSendRawResponse,
@@ -22,10 +19,7 @@ from ark.types import (
Methods:
-- client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse
- client.emails.list(\*\*params) -> SyncPageNumberPagination[EmailListResponse]
-- client.emails.retrieve_deliveries(id) -> EmailRetrieveDeliveriesResponse
-- client.emails.retry(id) -> EmailRetryResponse
- client.emails.send(\*\*params) -> EmailSendResponse
- client.emails.send_batch(\*\*params) -> EmailSendBatchResponse
- client.emails.send_raw(\*\*params) -> EmailSendRawResponse
diff --git a/src/ark/resources/emails.py b/src/ark/resources/emails.py
index 4169d3a..d4eea62 100644
--- a/src/ark/resources/emails.py
+++ b/src/ark/resources/emails.py
@@ -7,13 +7,7 @@
import httpx
-from ..types import (
- email_list_params,
- email_send_params,
- email_retrieve_params,
- email_send_raw_params,
- email_send_batch_params,
-)
+from ..types import email_list_params, email_send_params, email_send_raw_params, email_send_batch_params
from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from .._utils import maybe_transform, strip_not_given, async_maybe_transform
from .._compat import cached_property
@@ -28,11 +22,8 @@
from .._base_client import AsyncPaginator, make_request_options
from ..types.email_list_response import EmailListResponse
from ..types.email_send_response import EmailSendResponse
-from ..types.email_retry_response import EmailRetryResponse
-from ..types.email_retrieve_response import EmailRetrieveResponse
from ..types.email_send_raw_response import EmailSendRawResponse
from ..types.email_send_batch_response import EmailSendBatchResponse
-from ..types.email_retrieve_deliveries_response import EmailRetrieveDeliveriesResponse
__all__ = ["EmailsResource", "AsyncEmailsResource"]
@@ -57,59 +48,6 @@ def with_streaming_response(self) -> EmailsResourceWithStreamingResponse:
"""
return EmailsResourceWithStreamingResponse(self)
- def retrieve(
- self,
- 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.
- # 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,
- ) -> EmailRetrieveResponse:
- """
- Retrieve detailed information about a specific email including delivery status,
- timestamps, and optionally the email content.
-
- Use the `expand` parameter to include additional data like the HTML/text body,
- headers, or delivery attempts.
-
- Args:
- expand:
- Comma-separated list of fields to include:
-
- - `full` - Include all expanded fields in a single request
- - `content` - HTML and plain text body
- - `headers` - Email headers
- - `deliveries` - Delivery attempt history
- - `activity` - Opens and clicks tracking data
- - `attachments` - File attachments with content (base64 encoded)
- - `raw` - Complete raw MIME message (base64 encoded)
-
- 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 id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._get(
- f"/emails/{id}",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=maybe_transform({"expand": expand}, email_retrieve_params.EmailRetrieveParams),
- ),
- cast_to=EmailRetrieveResponse,
- )
-
def list(
self,
*,
@@ -198,111 +136,6 @@ def list(
model=EmailListResponse,
)
- def retrieve_deliveries(
- self,
- 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,
- ) -> EmailRetrieveDeliveriesResponse:
- """
- Get the complete delivery history for an email, including SMTP response codes,
- timestamps, and current retry state.
-
- ## Response Fields
-
- ### Status
-
- The current status of the email:
-
- - `pending` - Awaiting first delivery attempt
- - `sent` - Successfully delivered to recipient server
- - `softfail` - Temporary failure, automatic retry scheduled
- - `hardfail` - Permanent failure, will not retry
- - `held` - Held for manual review
- - `bounced` - Bounced by recipient server
-
- ### Retry State
-
- When the email is in the delivery queue (`pending` or `softfail` status),
- `retryState` provides information about the retry schedule:
-
- - `attempt` - Current attempt number (0 = first attempt)
- - `maxAttempts` - Maximum attempts before hard-fail (typically 18)
- - `attemptsRemaining` - Attempts left before hard-fail
- - `nextRetryAt` - When the next retry is scheduled (Unix timestamp)
- - `processing` - Whether the email is currently being processed
- - `manual` - Whether this was triggered by a manual retry
-
- When the email has finished processing (`sent`, `hardfail`, `held`, `bounced`),
- `retryState` is `null`.
-
- ### 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
- expired due to retention policy).
-
- 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 id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._get(
- f"/emails/{id}/deliveries",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=EmailRetrieveDeliveriesResponse,
- )
-
- def retry(
- self,
- 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,
- ) -> EmailRetryResponse:
- """Retry delivery of a failed or soft-bounced email.
-
- Creates a new delivery
- attempt.
-
- Only works for emails that have failed or are in a retryable state.
-
- 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 id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return self._post(
- f"/emails/{id}/retry",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=EmailRetryResponse,
- )
-
def send(
self,
*,
@@ -573,59 +406,6 @@ def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse:
"""
return AsyncEmailsResourceWithStreamingResponse(self)
- async def retrieve(
- self,
- 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.
- # 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,
- ) -> EmailRetrieveResponse:
- """
- Retrieve detailed information about a specific email including delivery status,
- timestamps, and optionally the email content.
-
- Use the `expand` parameter to include additional data like the HTML/text body,
- headers, or delivery attempts.
-
- Args:
- expand:
- Comma-separated list of fields to include:
-
- - `full` - Include all expanded fields in a single request
- - `content` - HTML and plain text body
- - `headers` - Email headers
- - `deliveries` - Delivery attempt history
- - `activity` - Opens and clicks tracking data
- - `attachments` - File attachments with content (base64 encoded)
- - `raw` - Complete raw MIME message (base64 encoded)
-
- 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 id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._get(
- f"/emails/{id}",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=await async_maybe_transform({"expand": expand}, email_retrieve_params.EmailRetrieveParams),
- ),
- cast_to=EmailRetrieveResponse,
- )
-
def list(
self,
*,
@@ -714,111 +494,6 @@ def list(
model=EmailListResponse,
)
- async def retrieve_deliveries(
- self,
- 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,
- ) -> EmailRetrieveDeliveriesResponse:
- """
- Get the complete delivery history for an email, including SMTP response codes,
- timestamps, and current retry state.
-
- ## Response Fields
-
- ### Status
-
- The current status of the email:
-
- - `pending` - Awaiting first delivery attempt
- - `sent` - Successfully delivered to recipient server
- - `softfail` - Temporary failure, automatic retry scheduled
- - `hardfail` - Permanent failure, will not retry
- - `held` - Held for manual review
- - `bounced` - Bounced by recipient server
-
- ### Retry State
-
- When the email is in the delivery queue (`pending` or `softfail` status),
- `retryState` provides information about the retry schedule:
-
- - `attempt` - Current attempt number (0 = first attempt)
- - `maxAttempts` - Maximum attempts before hard-fail (typically 18)
- - `attemptsRemaining` - Attempts left before hard-fail
- - `nextRetryAt` - When the next retry is scheduled (Unix timestamp)
- - `processing` - Whether the email is currently being processed
- - `manual` - Whether this was triggered by a manual retry
-
- When the email has finished processing (`sent`, `hardfail`, `held`, `bounced`),
- `retryState` is `null`.
-
- ### 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
- expired due to retention policy).
-
- 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 id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._get(
- f"/emails/{id}/deliveries",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=EmailRetrieveDeliveriesResponse,
- )
-
- async def retry(
- self,
- 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,
- ) -> EmailRetryResponse:
- """Retry delivery of a failed or soft-bounced email.
-
- Creates a new delivery
- attempt.
-
- Only works for emails that have failed or are in a retryable state.
-
- 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 id:
- raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
- return await self._post(
- f"/emails/{id}/retry",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=EmailRetryResponse,
- )
-
async def send(
self,
*,
@@ -1073,18 +748,9 @@ class EmailsResourceWithRawResponse:
def __init__(self, emails: EmailsResource) -> None:
self._emails = emails
- self.retrieve = to_raw_response_wrapper(
- emails.retrieve,
- )
self.list = to_raw_response_wrapper(
emails.list,
)
- self.retrieve_deliveries = to_raw_response_wrapper(
- emails.retrieve_deliveries,
- )
- self.retry = to_raw_response_wrapper(
- emails.retry,
- )
self.send = to_raw_response_wrapper(
emails.send,
)
@@ -1100,18 +766,9 @@ class AsyncEmailsResourceWithRawResponse:
def __init__(self, emails: AsyncEmailsResource) -> None:
self._emails = emails
- self.retrieve = async_to_raw_response_wrapper(
- emails.retrieve,
- )
self.list = async_to_raw_response_wrapper(
emails.list,
)
- self.retrieve_deliveries = async_to_raw_response_wrapper(
- emails.retrieve_deliveries,
- )
- self.retry = async_to_raw_response_wrapper(
- emails.retry,
- )
self.send = async_to_raw_response_wrapper(
emails.send,
)
@@ -1127,18 +784,9 @@ class EmailsResourceWithStreamingResponse:
def __init__(self, emails: EmailsResource) -> None:
self._emails = emails
- self.retrieve = to_streamed_response_wrapper(
- emails.retrieve,
- )
self.list = to_streamed_response_wrapper(
emails.list,
)
- self.retrieve_deliveries = to_streamed_response_wrapper(
- emails.retrieve_deliveries,
- )
- self.retry = to_streamed_response_wrapper(
- emails.retry,
- )
self.send = to_streamed_response_wrapper(
emails.send,
)
@@ -1154,18 +802,9 @@ class AsyncEmailsResourceWithStreamingResponse:
def __init__(self, emails: AsyncEmailsResource) -> None:
self._emails = emails
- self.retrieve = async_to_streamed_response_wrapper(
- emails.retrieve,
- )
self.list = async_to_streamed_response_wrapper(
emails.list,
)
- self.retrieve_deliveries = async_to_streamed_response_wrapper(
- emails.retrieve_deliveries,
- )
- self.retry = async_to_streamed_response_wrapper(
- emails.retry,
- )
self.send = async_to_streamed_response_wrapper(
emails.send,
)
diff --git a/src/ark/resources/webhooks.py b/src/ark/resources/webhooks.py
index 97b0202..f59179e 100644
--- a/src/ark/resources/webhooks.py
+++ b/src/ark/resources/webhooks.py
@@ -70,6 +70,8 @@ def create(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
]
@@ -290,6 +292,8 @@ def list_deliveries(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
| Omit = omit,
page: int | Omit = omit,
@@ -474,6 +478,8 @@ def test(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
],
# 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.
@@ -557,6 +563,8 @@ async def create(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
]
@@ -777,6 +785,8 @@ async def list_deliveries(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
| Omit = omit,
page: int | Omit = omit,
@@ -961,6 +971,8 @@ async def test(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
],
# 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.
diff --git a/src/ark/types/__init__.py b/src/ark/types/__init__.py
index 6cd75fb..5c42442 100644
--- a/src/ark/types/__init__.py
+++ b/src/ark/types/__init__.py
@@ -15,8 +15,6 @@
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 .email_retry_response import EmailRetryResponse as EmailRetryResponse
-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
@@ -29,7 +27,6 @@
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
@@ -55,5 +52,4 @@
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 .email_retrieve_deliveries_response import EmailRetrieveDeliveriesResponse as EmailRetrieveDeliveriesResponse
from .webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse as WebhookRetrieveDeliveryResponse
diff --git a/src/ark/types/email_list_response.py b/src/ark/types/email_list_response.py
index dce7d39..f8a1d1c 100644
--- a/src/ark/types/email_list_response.py
+++ b/src/ark/types/email_list_response.py
@@ -13,7 +13,9 @@
class EmailListResponse(BaseModel):
id: str
- """Unique message identifier (token)"""
+ """Internal message ID"""
+
+ token: str
from_: str = FieldInfo(alias="from")
diff --git a/src/ark/types/email_retrieve_deliveries_response.py b/src/ark/types/email_retrieve_deliveries_response.py
deleted file mode 100644
index 6406e9e..0000000
--- a/src/ark/types/email_retrieve_deliveries_response.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import List, Optional
-from datetime import datetime
-from typing_extensions import Literal
-
-from pydantic import Field as FieldInfo
-
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
-
-__all__ = ["EmailRetrieveDeliveriesResponse", "Data", "DataDelivery", "DataRetryState"]
-
-
-class DataDelivery(BaseModel):
- id: str
- """Delivery attempt ID"""
-
- status: str
- """Delivery status (lowercase)"""
-
- timestamp: float
- """Unix timestamp"""
-
- timestamp_iso: datetime = FieldInfo(alias="timestampIso")
- """ISO 8601 timestamp"""
-
- code: Optional[int] = None
- """SMTP response code"""
-
- details: Optional[str] = None
- """Status details"""
-
- output: Optional[str] = None
- """SMTP server response from the receiving mail server"""
-
- sent_with_ssl: Optional[bool] = FieldInfo(alias="sentWithSsl", default=None)
- """Whether TLS was used"""
-
-
-class DataRetryState(BaseModel):
- """
- Information about the current retry state of a message that is queued for delivery.
- Only present when the message is in the delivery queue.
- """
-
- attempt: int
- """Current attempt number (0-indexed).
-
- The first delivery attempt is 0, the first retry is 1, and so on.
- """
-
- attempts_remaining: int = FieldInfo(alias="attemptsRemaining")
- """
- Number of attempts remaining before the message is hard-failed. Calculated as
- `maxAttempts - attempt`.
- """
-
- manual: bool
- """
- Whether this queue entry was created by a manual retry request. Manual retries
- bypass certain hold conditions like suppression lists.
- """
-
- max_attempts: int = FieldInfo(alias="maxAttempts")
- """
- Maximum number of delivery attempts before the message is hard-failed.
- Configured at the server level.
- """
-
- processing: bool
- """
- Whether the message is currently being processed by a delivery worker. When
- `true`, the message is actively being sent.
- """
-
- next_retry_at: Optional[float] = FieldInfo(alias="nextRetryAt", default=None)
- """
- Unix timestamp of when the next retry attempt is scheduled. `null` if the
- message is ready for immediate processing or currently being processed.
- """
-
- next_retry_at_iso: Optional[datetime] = FieldInfo(alias="nextRetryAtIso", default=None)
- """
- ISO 8601 formatted timestamp of the next retry attempt. `null` if the message is
- ready for immediate processing.
- """
-
-
-class Data(BaseModel):
- id: str
- """Message identifier (token)"""
-
- can_retry_manually: bool = FieldInfo(alias="canRetryManually")
- """
- Whether the message can be manually retried via `POST /emails/{id}/retry`.
- `true` when the raw message content is still available (not expired). Messages
- older than the retention period cannot be retried.
- """
-
- deliveries: List[DataDelivery]
- """
- Chronological list of delivery attempts for this message. Each attempt includes
- SMTP response codes and timestamps.
- """
-
- retry_state: Optional[DataRetryState] = FieldInfo(alias="retryState", default=None)
- """
- Information about the current retry state of a message that is queued for
- delivery. Only present when the message is in the delivery queue.
- """
-
- status: Literal["pending", "sent", "softfail", "hardfail", "held", "bounced"]
- """Current message status (lowercase). Possible values:
-
- - `pending` - Initial state, awaiting first delivery attempt
- - `sent` - Successfully delivered
- - `softfail` - Temporary failure, will retry automatically
- - `hardfail` - Permanent failure, will not retry
- - `held` - Held for manual review (suppression list, etc.)
- - `bounced` - Bounced by recipient server
- """
-
-
-class EmailRetrieveDeliveriesResponse(BaseModel):
- data: Data
-
- meta: APIMeta
-
- success: Literal[True]
diff --git a/src/ark/types/email_retrieve_params.py b/src/ark/types/email_retrieve_params.py
deleted file mode 100644
index 5d3fc43..0000000
--- a/src/ark/types/email_retrieve_params.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing_extensions import TypedDict
-
-__all__ = ["EmailRetrieveParams"]
-
-
-class EmailRetrieveParams(TypedDict, total=False):
- expand: str
- """Comma-separated list of fields to include:
-
- - `full` - Include all expanded fields in a single request
- - `content` - HTML and plain text body
- - `headers` - Email headers
- - `deliveries` - Delivery attempt history
- - `activity` - Opens and clicks tracking data
- - `attachments` - File attachments with content (base64 encoded)
- - `raw` - Complete raw MIME message (base64 encoded)
- """
diff --git a/src/ark/types/email_retrieve_response.py b/src/ark/types/email_retrieve_response.py
deleted file mode 100644
index f60801e..0000000
--- a/src/ark/types/email_retrieve_response.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, List, Optional
-from datetime import datetime
-from typing_extensions import Literal
-
-from pydantic import Field as FieldInfo
-
-from .._models import BaseModel
-from .shared.api_meta import APIMeta
-
-__all__ = [
- "EmailRetrieveResponse",
- "Data",
- "DataActivity",
- "DataActivityClick",
- "DataActivityOpen",
- "DataAttachment",
- "DataDelivery",
-]
-
-
-class DataActivityClick(BaseModel):
- ip_address: Optional[str] = FieldInfo(alias="ipAddress", default=None)
- """IP address of the clicker"""
-
- timestamp: Optional[float] = None
- """Unix timestamp of the click event"""
-
- timestamp_iso: Optional[datetime] = FieldInfo(alias="timestampIso", default=None)
- """ISO 8601 timestamp of the click event"""
-
- url: Optional[str] = None
- """URL that was clicked"""
-
- user_agent: Optional[str] = FieldInfo(alias="userAgent", default=None)
- """User agent of the email client"""
-
-
-class DataActivityOpen(BaseModel):
- ip_address: Optional[str] = FieldInfo(alias="ipAddress", default=None)
- """IP address of the opener"""
-
- timestamp: Optional[float] = None
- """Unix timestamp of the open event"""
-
- timestamp_iso: Optional[datetime] = FieldInfo(alias="timestampIso", default=None)
- """ISO 8601 timestamp of the open event"""
-
- user_agent: Optional[str] = FieldInfo(alias="userAgent", default=None)
- """User agent of the email client"""
-
-
-class DataActivity(BaseModel):
- """Opens and clicks tracking data (included if expand=activity)"""
-
- clicks: Optional[List[DataActivityClick]] = None
- """List of link click events"""
-
- opens: Optional[List[DataActivityOpen]] = None
- """List of email open events"""
-
-
-class DataAttachment(BaseModel):
- """An email attachment retrieved from a sent message"""
-
- content_type: str = FieldInfo(alias="contentType")
- """MIME type of the attachment"""
-
- data: str
- """Base64 encoded attachment content. Decode this to get the raw file bytes."""
-
- filename: str
- """Original filename of the attachment"""
-
- hash: str
- """SHA256 hash of the attachment content for verification"""
-
- size: int
- """Size of the attachment in bytes"""
-
-
-class DataDelivery(BaseModel):
- id: str
- """Delivery attempt ID"""
-
- status: str
- """Delivery status (lowercase)"""
-
- timestamp: float
- """Unix timestamp"""
-
- timestamp_iso: datetime = FieldInfo(alias="timestampIso")
- """ISO 8601 timestamp"""
-
- code: Optional[int] = None
- """SMTP response code"""
-
- details: Optional[str] = None
- """Status details"""
-
- output: Optional[str] = None
- """SMTP server response from the receiving mail server"""
-
- sent_with_ssl: Optional[bool] = FieldInfo(alias="sentWithSsl", default=None)
- """Whether TLS was used"""
-
-
-class Data(BaseModel):
- id: str
- """Unique message identifier (token)"""
-
- from_: str = FieldInfo(alias="from")
- """Sender address"""
-
- scope: Literal["outgoing", "incoming"]
- """Message direction"""
-
- status: Literal["pending", "sent", "softfail", "hardfail", "bounced", "held"]
- """Current delivery status:
-
- - `pending` - Email accepted, waiting to be processed
- - `sent` - Email transmitted to recipient's mail server
- - `softfail` - Temporary delivery failure, will retry
- - `hardfail` - Permanent delivery failure
- - `bounced` - Email bounced back
- - `held` - Held for manual review
- """
-
- subject: str
- """Email subject line"""
-
- timestamp: float
- """Unix timestamp when the email was sent"""
-
- timestamp_iso: datetime = FieldInfo(alias="timestampIso")
- """ISO 8601 formatted timestamp"""
-
- to: str
- """Recipient address"""
-
- activity: Optional[DataActivity] = None
- """Opens and clicks tracking data (included if expand=activity)"""
-
- attachments: Optional[List[DataAttachment]] = None
- """File attachments (included if expand=attachments)"""
-
- deliveries: Optional[List[DataDelivery]] = None
- """Delivery attempt history (included if expand=deliveries)"""
-
- headers: Optional[Dict[str, str]] = None
- """Email headers (included if expand=headers)"""
-
- html_body: Optional[str] = FieldInfo(alias="htmlBody", default=None)
- """HTML body content (included if expand=content)"""
-
- message_id: Optional[str] = FieldInfo(alias="messageId", default=None)
- """SMTP Message-ID header"""
-
- plain_body: Optional[str] = FieldInfo(alias="plainBody", default=None)
- """Plain text body (included if expand=content)"""
-
- raw_message: Optional[str] = FieldInfo(alias="rawMessage", default=None)
- """
- Complete raw MIME message, base64 encoded (included if expand=raw). Decode this
- to get the original RFC 2822 formatted email.
- """
-
- spam: Optional[bool] = None
- """Whether the message was flagged as spam"""
-
- spam_score: Optional[float] = FieldInfo(alias="spamScore", default=None)
- """Spam score (if applicable)"""
-
- tag: Optional[str] = None
- """Optional categorization tag"""
-
-
-class EmailRetrieveResponse(BaseModel):
- data: Data
-
- meta: APIMeta
-
- success: Literal[True]
diff --git a/src/ark/types/email_retry_response.py b/src/ark/types/email_retry_response.py
deleted file mode 100644
index f63cc99..0000000
--- a/src/ark/types/email_retry_response.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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__ = ["EmailRetryResponse", "Data"]
-
-
-class Data(BaseModel):
- id: str
- """Email identifier (token)"""
-
- message: str
-
-
-class EmailRetryResponse(BaseModel):
- data: Data
-
- meta: APIMeta
-
- success: Literal[True]
diff --git a/src/ark/types/email_send_batch_response.py b/src/ark/types/email_send_batch_response.py
index d32e3fc..272119c 100644
--- a/src/ark/types/email_send_batch_response.py
+++ b/src/ark/types/email_send_batch_response.py
@@ -11,7 +11,9 @@
class DataMessages(BaseModel):
id: str
- """Message identifier (token)"""
+ """Message ID"""
+
+ token: str
class Data(BaseModel):
diff --git a/src/ark/types/email_send_raw_response.py b/src/ark/types/email_send_raw_response.py
index 46e7c96..f1e8c03 100644
--- a/src/ark/types/email_send_raw_response.py
+++ b/src/ark/types/email_send_raw_response.py
@@ -13,7 +13,7 @@
class Data(BaseModel):
id: str
- """Unique message identifier (token)"""
+ """Unique message ID (format: msg*{id}*{token})"""
status: Literal["pending", "sent"]
"""Current delivery status"""
diff --git a/src/ark/types/email_send_response.py b/src/ark/types/email_send_response.py
index cb7d814..39e1d36 100644
--- a/src/ark/types/email_send_response.py
+++ b/src/ark/types/email_send_response.py
@@ -13,7 +13,7 @@
class Data(BaseModel):
id: str
- """Unique message identifier (token)"""
+ """Unique message ID (format: msg*{id}*{token})"""
status: Literal["pending", "sent"]
"""Current delivery status"""
diff --git a/src/ark/types/log_entry.py b/src/ark/types/log_entry.py
index 4dfdd6c..9fad7e7 100644
--- a/src/ark/types/log_entry.py
+++ b/src/ark/types/log_entry.py
@@ -57,7 +57,7 @@ class Email(BaseModel):
"""Email-specific data (for email endpoints)"""
id: Optional[str] = None
- """Email message identifier (token)"""
+ """Email message ID"""
recipient_count: Optional[int] = FieldInfo(alias="recipientCount", default=None)
"""Number of recipients"""
diff --git a/src/ark/types/webhook_create_params.py b/src/ark/types/webhook_create_params.py
index 9899826..8b7a63e 100644
--- a/src/ark/types/webhook_create_params.py
+++ b/src/ark/types/webhook_create_params.py
@@ -34,6 +34,8 @@ class WebhookCreateParams(TypedDict, total=False):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
]
diff --git a/src/ark/types/webhook_create_response.py b/src/ark/types/webhook_create_response.py
index d78bb58..07f75ec 100644
--- a/src/ark/types/webhook_create_response.py
+++ b/src/ark/types/webhook_create_response.py
@@ -34,6 +34,8 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
"""Subscribed events"""
diff --git a/src/ark/types/webhook_list_deliveries_params.py b/src/ark/types/webhook_list_deliveries_params.py
index c4048e4..cce2c61 100644
--- a/src/ark/types/webhook_list_deliveries_params.py
+++ b/src/ark/types/webhook_list_deliveries_params.py
@@ -25,6 +25,8 @@ class WebhookListDeliveriesParams(TypedDict, total=False):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
"""Filter by event type"""
diff --git a/src/ark/types/webhook_list_deliveries_response.py b/src/ark/types/webhook_list_deliveries_response.py
index 71a5fe2..cb5033d 100644
--- a/src/ark/types/webhook_list_deliveries_response.py
+++ b/src/ark/types/webhook_list_deliveries_response.py
@@ -30,6 +30,8 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
"""Event type that triggered this delivery"""
diff --git a/src/ark/types/webhook_retrieve_delivery_response.py b/src/ark/types/webhook_retrieve_delivery_response.py
index efec5c4..9a0c38b 100644
--- a/src/ark/types/webhook_retrieve_delivery_response.py
+++ b/src/ark/types/webhook_retrieve_delivery_response.py
@@ -50,6 +50,8 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
"""Event type that triggered this delivery"""
diff --git a/src/ark/types/webhook_retrieve_response.py b/src/ark/types/webhook_retrieve_response.py
index 6cb2f2e..db780ca 100644
--- a/src/ark/types/webhook_retrieve_response.py
+++ b/src/ark/types/webhook_retrieve_response.py
@@ -34,6 +34,8 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
"""Subscribed events"""
diff --git a/src/ark/types/webhook_test_params.py b/src/ark/types/webhook_test_params.py
index 3f91fca..5648540 100644
--- a/src/ark/types/webhook_test_params.py
+++ b/src/ark/types/webhook_test_params.py
@@ -18,6 +18,8 @@ class WebhookTestParams(TypedDict, total=False):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
"""Event type to simulate"""
diff --git a/src/ark/types/webhook_update_response.py b/src/ark/types/webhook_update_response.py
index 4d9a0c6..75c6d59 100644
--- a/src/ark/types/webhook_update_response.py
+++ b/src/ark/types/webhook_update_response.py
@@ -34,6 +34,8 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
+ "SendLimitApproaching",
+ "SendLimitExceeded",
]
]
"""Subscribed events"""
diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py
index 7a54df7..e416353 100644
--- a/tests/api_resources/test_emails.py
+++ b/tests/api_resources/test_emails.py
@@ -11,11 +11,8 @@
from ark.types import (
EmailListResponse,
EmailSendResponse,
- EmailRetryResponse,
EmailSendRawResponse,
- EmailRetrieveResponse,
EmailSendBatchResponse,
- EmailRetrieveDeliveriesResponse,
)
from tests.utils import assert_matches_type
from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
@@ -26,52 +23,6 @@
class TestEmails:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
- @parametrize
- def test_method_retrieve(self, client: Ark) -> None:
- email = client.emails.retrieve(
- 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",
- expand="full",
- )
- assert_matches_type(EmailRetrieveResponse, email, path=["response"])
-
- @parametrize
- def test_raw_response_retrieve(self, client: Ark) -> None:
- response = client.emails.with_raw_response.retrieve(
- id="aBc123XyZ",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- email = response.parse()
- assert_matches_type(EmailRetrieveResponse, email, path=["response"])
-
- @parametrize
- def test_streaming_response_retrieve(self, client: Ark) -> None:
- with client.emails.with_streaming_response.retrieve(
- id="aBc123XyZ",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- email = response.parse()
- assert_matches_type(EmailRetrieveResponse, email, 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 `id` but received ''"):
- client.emails.with_raw_response.retrieve(
- id="",
- )
-
@parametrize
def test_method_list(self, client: Ark) -> None:
email = client.emails.list()
@@ -111,82 +62,6 @@ def test_streaming_response_list(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
- @parametrize
- def test_method_retrieve_deliveries(self, client: Ark) -> None:
- email = client.emails.retrieve_deliveries(
- "aBc123XyZ",
- )
- assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
-
- @parametrize
- def test_raw_response_retrieve_deliveries(self, client: Ark) -> None:
- response = client.emails.with_raw_response.retrieve_deliveries(
- "aBc123XyZ",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- email = response.parse()
- assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
-
- @parametrize
- def test_streaming_response_retrieve_deliveries(self, client: Ark) -> None:
- with client.emails.with_streaming_response.retrieve_deliveries(
- "aBc123XyZ",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- email = response.parse()
- assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @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 ''"):
- client.emails.with_raw_response.retrieve_deliveries(
- "",
- )
-
- @parametrize
- def test_method_retry(self, client: Ark) -> None:
- email = client.emails.retry(
- "aBc123XyZ",
- )
- assert_matches_type(EmailRetryResponse, email, path=["response"])
-
- @parametrize
- def test_raw_response_retry(self, client: Ark) -> None:
- response = client.emails.with_raw_response.retry(
- "aBc123XyZ",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- email = response.parse()
- assert_matches_type(EmailRetryResponse, email, path=["response"])
-
- @parametrize
- def test_streaming_response_retry(self, client: Ark) -> None:
- with client.emails.with_streaming_response.retry(
- "aBc123XyZ",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- email = response.parse()
- assert_matches_type(EmailRetryResponse, email, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @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 ''"):
- client.emails.with_raw_response.retry(
- "",
- )
-
@parametrize
def test_method_send(self, client: Ark) -> None:
email = client.emails.send(
@@ -400,52 +275,6 @@ class TestAsyncEmails:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
- @parametrize
- async def test_method_retrieve(self, async_client: AsyncArk) -> None:
- email = await async_client.emails.retrieve(
- 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",
- expand="full",
- )
- assert_matches_type(EmailRetrieveResponse, email, path=["response"])
-
- @parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
- response = await async_client.emails.with_raw_response.retrieve(
- id="aBc123XyZ",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- email = await response.parse()
- assert_matches_type(EmailRetrieveResponse, email, path=["response"])
-
- @parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
- async with async_client.emails.with_streaming_response.retrieve(
- id="aBc123XyZ",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- email = await response.parse()
- assert_matches_type(EmailRetrieveResponse, email, 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 `id` but received ''"):
- await async_client.emails.with_raw_response.retrieve(
- id="",
- )
-
@parametrize
async def test_method_list(self, async_client: AsyncArk) -> None:
email = await async_client.emails.list()
@@ -485,82 +314,6 @@ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
- @parametrize
- async def test_method_retrieve_deliveries(self, async_client: AsyncArk) -> None:
- email = await async_client.emails.retrieve_deliveries(
- "aBc123XyZ",
- )
- assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
-
- @parametrize
- async def test_raw_response_retrieve_deliveries(self, async_client: AsyncArk) -> None:
- response = await async_client.emails.with_raw_response.retrieve_deliveries(
- "aBc123XyZ",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- email = await response.parse()
- assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
-
- @parametrize
- async def test_streaming_response_retrieve_deliveries(self, async_client: AsyncArk) -> None:
- async with async_client.emails.with_streaming_response.retrieve_deliveries(
- "aBc123XyZ",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- email = await response.parse()
- assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @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 ''"):
- await async_client.emails.with_raw_response.retrieve_deliveries(
- "",
- )
-
- @parametrize
- async def test_method_retry(self, async_client: AsyncArk) -> None:
- email = await async_client.emails.retry(
- "aBc123XyZ",
- )
- assert_matches_type(EmailRetryResponse, email, path=["response"])
-
- @parametrize
- async def test_raw_response_retry(self, async_client: AsyncArk) -> None:
- response = await async_client.emails.with_raw_response.retry(
- "aBc123XyZ",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- email = await response.parse()
- assert_matches_type(EmailRetryResponse, email, path=["response"])
-
- @parametrize
- async def test_streaming_response_retry(self, async_client: AsyncArk) -> None:
- async with async_client.emails.with_streaming_response.retry(
- "aBc123XyZ",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- email = await response.parse()
- assert_matches_type(EmailRetryResponse, email, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @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 ''"):
- await async_client.emails.with_raw_response.retry(
- "",
- )
-
@parametrize
async def test_method_send(self, async_client: AsyncArk) -> None:
email = await async_client.emails.send(
From a6fdf54cba7744137502a2e97f9105ef204f4d79 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 30 Jan 2026 15:29:37 +0000
Subject: [PATCH 2/3] feat(api): manual updates
---
.stats.yml | 6 +-
api.md | 6 +
src/ark/resources/emails.py | 363 +++++++++++++++++-
src/ark/resources/webhooks.py | 12 -
src/ark/types/__init__.py | 4 +
src/ark/types/email_list_response.py | 4 +-
.../email_retrieve_deliveries_response.py | 130 +++++++
src/ark/types/email_retrieve_params.py | 21 +
src/ark/types/email_retrieve_response.py | 184 +++++++++
src/ark/types/email_retry_response.py | 23 ++
src/ark/types/email_send_batch_response.py | 4 +-
src/ark/types/email_send_raw_response.py | 2 +-
src/ark/types/email_send_response.py | 2 +-
src/ark/types/log_entry.py | 2 +-
src/ark/types/webhook_create_params.py | 2 -
src/ark/types/webhook_create_response.py | 2 -
.../types/webhook_list_deliveries_params.py | 2 -
.../types/webhook_list_deliveries_response.py | 2 -
.../webhook_retrieve_delivery_response.py | 2 -
src/ark/types/webhook_retrieve_response.py | 2 -
src/ark/types/webhook_test_params.py | 2 -
src/ark/types/webhook_update_response.py | 2 -
tests/api_resources/test_emails.py | 247 ++++++++++++
23 files changed, 985 insertions(+), 41 deletions(-)
create mode 100644 src/ark/types/email_retrieve_deliveries_response.py
create mode 100644 src/ark/types/email_retrieve_params.py
create mode 100644 src/ark/types/email_retrieve_response.py
create mode 100644 src/ark/types/email_retry_response.py
diff --git a/.stats.yml b/.stats.yml
index 631edab..69b13d9 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 32
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-65d92724dca2fcf00a43883ad10e0591b457891aa45ed965b0414b27388b5e16.yml
-openapi_spec_hash: 42824aaa51f95c0f485c91600969673f
+configured_endpoints: 35
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-39b91ffd46b6e41924f8465ffaaff6ba3c200a68daa513d4f1eb1e4b29aba78f.yml
+openapi_spec_hash: 542dd50007316698c83e8b0bdd5e40e2
config_hash: 77a3908ee910a8019f5831d3a3d53c18
diff --git a/api.md b/api.md
index c296d0a..7baaad3 100644
--- a/api.md
+++ b/api.md
@@ -10,7 +10,10 @@ Types:
```python
from ark.types import (
+ EmailRetrieveResponse,
EmailListResponse,
+ EmailRetrieveDeliveriesResponse,
+ EmailRetryResponse,
EmailSendResponse,
EmailSendBatchResponse,
EmailSendRawResponse,
@@ -19,7 +22,10 @@ from ark.types import (
Methods:
+- client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse
- client.emails.list(\*\*params) -> SyncPageNumberPagination[EmailListResponse]
+- client.emails.retrieve_deliveries(id) -> EmailRetrieveDeliveriesResponse
+- client.emails.retry(id) -> EmailRetryResponse
- client.emails.send(\*\*params) -> EmailSendResponse
- client.emails.send_batch(\*\*params) -> EmailSendBatchResponse
- client.emails.send_raw(\*\*params) -> EmailSendRawResponse
diff --git a/src/ark/resources/emails.py b/src/ark/resources/emails.py
index d4eea62..4169d3a 100644
--- a/src/ark/resources/emails.py
+++ b/src/ark/resources/emails.py
@@ -7,7 +7,13 @@
import httpx
-from ..types import email_list_params, email_send_params, email_send_raw_params, email_send_batch_params
+from ..types import (
+ email_list_params,
+ email_send_params,
+ email_retrieve_params,
+ email_send_raw_params,
+ email_send_batch_params,
+)
from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from .._utils import maybe_transform, strip_not_given, async_maybe_transform
from .._compat import cached_property
@@ -22,8 +28,11 @@
from .._base_client import AsyncPaginator, make_request_options
from ..types.email_list_response import EmailListResponse
from ..types.email_send_response import EmailSendResponse
+from ..types.email_retry_response import EmailRetryResponse
+from ..types.email_retrieve_response import EmailRetrieveResponse
from ..types.email_send_raw_response import EmailSendRawResponse
from ..types.email_send_batch_response import EmailSendBatchResponse
+from ..types.email_retrieve_deliveries_response import EmailRetrieveDeliveriesResponse
__all__ = ["EmailsResource", "AsyncEmailsResource"]
@@ -48,6 +57,59 @@ def with_streaming_response(self) -> EmailsResourceWithStreamingResponse:
"""
return EmailsResourceWithStreamingResponse(self)
+ def retrieve(
+ self,
+ 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.
+ # 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,
+ ) -> EmailRetrieveResponse:
+ """
+ Retrieve detailed information about a specific email including delivery status,
+ timestamps, and optionally the email content.
+
+ Use the `expand` parameter to include additional data like the HTML/text body,
+ headers, or delivery attempts.
+
+ Args:
+ expand:
+ Comma-separated list of fields to include:
+
+ - `full` - Include all expanded fields in a single request
+ - `content` - HTML and plain text body
+ - `headers` - Email headers
+ - `deliveries` - Delivery attempt history
+ - `activity` - Opens and clicks tracking data
+ - `attachments` - File attachments with content (base64 encoded)
+ - `raw` - Complete raw MIME message (base64 encoded)
+
+ 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 id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/emails/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"expand": expand}, email_retrieve_params.EmailRetrieveParams),
+ ),
+ cast_to=EmailRetrieveResponse,
+ )
+
def list(
self,
*,
@@ -136,6 +198,111 @@ def list(
model=EmailListResponse,
)
+ def retrieve_deliveries(
+ self,
+ 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,
+ ) -> EmailRetrieveDeliveriesResponse:
+ """
+ Get the complete delivery history for an email, including SMTP response codes,
+ timestamps, and current retry state.
+
+ ## Response Fields
+
+ ### Status
+
+ The current status of the email:
+
+ - `pending` - Awaiting first delivery attempt
+ - `sent` - Successfully delivered to recipient server
+ - `softfail` - Temporary failure, automatic retry scheduled
+ - `hardfail` - Permanent failure, will not retry
+ - `held` - Held for manual review
+ - `bounced` - Bounced by recipient server
+
+ ### Retry State
+
+ When the email is in the delivery queue (`pending` or `softfail` status),
+ `retryState` provides information about the retry schedule:
+
+ - `attempt` - Current attempt number (0 = first attempt)
+ - `maxAttempts` - Maximum attempts before hard-fail (typically 18)
+ - `attemptsRemaining` - Attempts left before hard-fail
+ - `nextRetryAt` - When the next retry is scheduled (Unix timestamp)
+ - `processing` - Whether the email is currently being processed
+ - `manual` - Whether this was triggered by a manual retry
+
+ When the email has finished processing (`sent`, `hardfail`, `held`, `bounced`),
+ `retryState` is `null`.
+
+ ### 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
+ expired due to retention policy).
+
+ 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 id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/emails/{id}/deliveries",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailRetrieveDeliveriesResponse,
+ )
+
+ def retry(
+ self,
+ 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,
+ ) -> EmailRetryResponse:
+ """Retry delivery of a failed or soft-bounced email.
+
+ Creates a new delivery
+ attempt.
+
+ Only works for emails that have failed or are in a retryable state.
+
+ 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 id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/emails/{id}/retry",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailRetryResponse,
+ )
+
def send(
self,
*,
@@ -406,6 +573,59 @@ def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse:
"""
return AsyncEmailsResourceWithStreamingResponse(self)
+ async def retrieve(
+ self,
+ 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.
+ # 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,
+ ) -> EmailRetrieveResponse:
+ """
+ Retrieve detailed information about a specific email including delivery status,
+ timestamps, and optionally the email content.
+
+ Use the `expand` parameter to include additional data like the HTML/text body,
+ headers, or delivery attempts.
+
+ Args:
+ expand:
+ Comma-separated list of fields to include:
+
+ - `full` - Include all expanded fields in a single request
+ - `content` - HTML and plain text body
+ - `headers` - Email headers
+ - `deliveries` - Delivery attempt history
+ - `activity` - Opens and clicks tracking data
+ - `attachments` - File attachments with content (base64 encoded)
+ - `raw` - Complete raw MIME message (base64 encoded)
+
+ 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 id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/emails/{id}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"expand": expand}, email_retrieve_params.EmailRetrieveParams),
+ ),
+ cast_to=EmailRetrieveResponse,
+ )
+
def list(
self,
*,
@@ -494,6 +714,111 @@ def list(
model=EmailListResponse,
)
+ async def retrieve_deliveries(
+ self,
+ 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,
+ ) -> EmailRetrieveDeliveriesResponse:
+ """
+ Get the complete delivery history for an email, including SMTP response codes,
+ timestamps, and current retry state.
+
+ ## Response Fields
+
+ ### Status
+
+ The current status of the email:
+
+ - `pending` - Awaiting first delivery attempt
+ - `sent` - Successfully delivered to recipient server
+ - `softfail` - Temporary failure, automatic retry scheduled
+ - `hardfail` - Permanent failure, will not retry
+ - `held` - Held for manual review
+ - `bounced` - Bounced by recipient server
+
+ ### Retry State
+
+ When the email is in the delivery queue (`pending` or `softfail` status),
+ `retryState` provides information about the retry schedule:
+
+ - `attempt` - Current attempt number (0 = first attempt)
+ - `maxAttempts` - Maximum attempts before hard-fail (typically 18)
+ - `attemptsRemaining` - Attempts left before hard-fail
+ - `nextRetryAt` - When the next retry is scheduled (Unix timestamp)
+ - `processing` - Whether the email is currently being processed
+ - `manual` - Whether this was triggered by a manual retry
+
+ When the email has finished processing (`sent`, `hardfail`, `held`, `bounced`),
+ `retryState` is `null`.
+
+ ### 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
+ expired due to retention policy).
+
+ 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 id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/emails/{id}/deliveries",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailRetrieveDeliveriesResponse,
+ )
+
+ async def retry(
+ self,
+ 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,
+ ) -> EmailRetryResponse:
+ """Retry delivery of a failed or soft-bounced email.
+
+ Creates a new delivery
+ attempt.
+
+ Only works for emails that have failed or are in a retryable state.
+
+ 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 id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/emails/{id}/retry",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmailRetryResponse,
+ )
+
async def send(
self,
*,
@@ -748,9 +1073,18 @@ class EmailsResourceWithRawResponse:
def __init__(self, emails: EmailsResource) -> None:
self._emails = emails
+ self.retrieve = to_raw_response_wrapper(
+ emails.retrieve,
+ )
self.list = to_raw_response_wrapper(
emails.list,
)
+ self.retrieve_deliveries = to_raw_response_wrapper(
+ emails.retrieve_deliveries,
+ )
+ self.retry = to_raw_response_wrapper(
+ emails.retry,
+ )
self.send = to_raw_response_wrapper(
emails.send,
)
@@ -766,9 +1100,18 @@ class AsyncEmailsResourceWithRawResponse:
def __init__(self, emails: AsyncEmailsResource) -> None:
self._emails = emails
+ self.retrieve = async_to_raw_response_wrapper(
+ emails.retrieve,
+ )
self.list = async_to_raw_response_wrapper(
emails.list,
)
+ self.retrieve_deliveries = async_to_raw_response_wrapper(
+ emails.retrieve_deliveries,
+ )
+ self.retry = async_to_raw_response_wrapper(
+ emails.retry,
+ )
self.send = async_to_raw_response_wrapper(
emails.send,
)
@@ -784,9 +1127,18 @@ class EmailsResourceWithStreamingResponse:
def __init__(self, emails: EmailsResource) -> None:
self._emails = emails
+ self.retrieve = to_streamed_response_wrapper(
+ emails.retrieve,
+ )
self.list = to_streamed_response_wrapper(
emails.list,
)
+ self.retrieve_deliveries = to_streamed_response_wrapper(
+ emails.retrieve_deliveries,
+ )
+ self.retry = to_streamed_response_wrapper(
+ emails.retry,
+ )
self.send = to_streamed_response_wrapper(
emails.send,
)
@@ -802,9 +1154,18 @@ class AsyncEmailsResourceWithStreamingResponse:
def __init__(self, emails: AsyncEmailsResource) -> None:
self._emails = emails
+ self.retrieve = async_to_streamed_response_wrapper(
+ emails.retrieve,
+ )
self.list = async_to_streamed_response_wrapper(
emails.list,
)
+ self.retrieve_deliveries = async_to_streamed_response_wrapper(
+ emails.retrieve_deliveries,
+ )
+ self.retry = async_to_streamed_response_wrapper(
+ emails.retry,
+ )
self.send = async_to_streamed_response_wrapper(
emails.send,
)
diff --git a/src/ark/resources/webhooks.py b/src/ark/resources/webhooks.py
index f59179e..97b0202 100644
--- a/src/ark/resources/webhooks.py
+++ b/src/ark/resources/webhooks.py
@@ -70,8 +70,6 @@ def create(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
]
@@ -292,8 +290,6 @@ def list_deliveries(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
| Omit = omit,
page: int | Omit = omit,
@@ -478,8 +474,6 @@ def test(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
],
# 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.
@@ -563,8 +557,6 @@ async def create(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
]
@@ -785,8 +777,6 @@ async def list_deliveries(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
| Omit = omit,
page: int | Omit = omit,
@@ -971,8 +961,6 @@ async def test(
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
],
# 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.
diff --git a/src/ark/types/__init__.py b/src/ark/types/__init__.py
index 5c42442..6cd75fb 100644
--- a/src/ark/types/__init__.py
+++ b/src/ark/types/__init__.py
@@ -15,6 +15,8 @@
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 .email_retry_response import EmailRetryResponse as EmailRetryResponse
+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
@@ -27,6 +29,7 @@
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
@@ -52,4 +55,5 @@
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 .email_retrieve_deliveries_response import EmailRetrieveDeliveriesResponse as EmailRetrieveDeliveriesResponse
from .webhook_retrieve_delivery_response import WebhookRetrieveDeliveryResponse as WebhookRetrieveDeliveryResponse
diff --git a/src/ark/types/email_list_response.py b/src/ark/types/email_list_response.py
index f8a1d1c..dce7d39 100644
--- a/src/ark/types/email_list_response.py
+++ b/src/ark/types/email_list_response.py
@@ -13,9 +13,7 @@
class EmailListResponse(BaseModel):
id: str
- """Internal message ID"""
-
- token: str
+ """Unique message identifier (token)"""
from_: str = FieldInfo(alias="from")
diff --git a/src/ark/types/email_retrieve_deliveries_response.py b/src/ark/types/email_retrieve_deliveries_response.py
new file mode 100644
index 0000000..6406e9e
--- /dev/null
+++ b/src/ark/types/email_retrieve_deliveries_response.py
@@ -0,0 +1,130 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .shared.api_meta import APIMeta
+
+__all__ = ["EmailRetrieveDeliveriesResponse", "Data", "DataDelivery", "DataRetryState"]
+
+
+class DataDelivery(BaseModel):
+ id: str
+ """Delivery attempt ID"""
+
+ status: str
+ """Delivery status (lowercase)"""
+
+ timestamp: float
+ """Unix timestamp"""
+
+ timestamp_iso: datetime = FieldInfo(alias="timestampIso")
+ """ISO 8601 timestamp"""
+
+ code: Optional[int] = None
+ """SMTP response code"""
+
+ details: Optional[str] = None
+ """Status details"""
+
+ output: Optional[str] = None
+ """SMTP server response from the receiving mail server"""
+
+ sent_with_ssl: Optional[bool] = FieldInfo(alias="sentWithSsl", default=None)
+ """Whether TLS was used"""
+
+
+class DataRetryState(BaseModel):
+ """
+ Information about the current retry state of a message that is queued for delivery.
+ Only present when the message is in the delivery queue.
+ """
+
+ attempt: int
+ """Current attempt number (0-indexed).
+
+ The first delivery attempt is 0, the first retry is 1, and so on.
+ """
+
+ attempts_remaining: int = FieldInfo(alias="attemptsRemaining")
+ """
+ Number of attempts remaining before the message is hard-failed. Calculated as
+ `maxAttempts - attempt`.
+ """
+
+ manual: bool
+ """
+ Whether this queue entry was created by a manual retry request. Manual retries
+ bypass certain hold conditions like suppression lists.
+ """
+
+ max_attempts: int = FieldInfo(alias="maxAttempts")
+ """
+ Maximum number of delivery attempts before the message is hard-failed.
+ Configured at the server level.
+ """
+
+ processing: bool
+ """
+ Whether the message is currently being processed by a delivery worker. When
+ `true`, the message is actively being sent.
+ """
+
+ next_retry_at: Optional[float] = FieldInfo(alias="nextRetryAt", default=None)
+ """
+ Unix timestamp of when the next retry attempt is scheduled. `null` if the
+ message is ready for immediate processing or currently being processed.
+ """
+
+ next_retry_at_iso: Optional[datetime] = FieldInfo(alias="nextRetryAtIso", default=None)
+ """
+ ISO 8601 formatted timestamp of the next retry attempt. `null` if the message is
+ ready for immediate processing.
+ """
+
+
+class Data(BaseModel):
+ id: str
+ """Message identifier (token)"""
+
+ can_retry_manually: bool = FieldInfo(alias="canRetryManually")
+ """
+ Whether the message can be manually retried via `POST /emails/{id}/retry`.
+ `true` when the raw message content is still available (not expired). Messages
+ older than the retention period cannot be retried.
+ """
+
+ deliveries: List[DataDelivery]
+ """
+ Chronological list of delivery attempts for this message. Each attempt includes
+ SMTP response codes and timestamps.
+ """
+
+ retry_state: Optional[DataRetryState] = FieldInfo(alias="retryState", default=None)
+ """
+ Information about the current retry state of a message that is queued for
+ delivery. Only present when the message is in the delivery queue.
+ """
+
+ status: Literal["pending", "sent", "softfail", "hardfail", "held", "bounced"]
+ """Current message status (lowercase). Possible values:
+
+ - `pending` - Initial state, awaiting first delivery attempt
+ - `sent` - Successfully delivered
+ - `softfail` - Temporary failure, will retry automatically
+ - `hardfail` - Permanent failure, will not retry
+ - `held` - Held for manual review (suppression list, etc.)
+ - `bounced` - Bounced by recipient server
+ """
+
+
+class EmailRetrieveDeliveriesResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/email_retrieve_params.py b/src/ark/types/email_retrieve_params.py
new file mode 100644
index 0000000..5d3fc43
--- /dev/null
+++ b/src/ark/types/email_retrieve_params.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["EmailRetrieveParams"]
+
+
+class EmailRetrieveParams(TypedDict, total=False):
+ expand: str
+ """Comma-separated list of fields to include:
+
+ - `full` - Include all expanded fields in a single request
+ - `content` - HTML and plain text body
+ - `headers` - Email headers
+ - `deliveries` - Delivery attempt history
+ - `activity` - Opens and clicks tracking data
+ - `attachments` - File attachments with content (base64 encoded)
+ - `raw` - Complete raw MIME message (base64 encoded)
+ """
diff --git a/src/ark/types/email_retrieve_response.py b/src/ark/types/email_retrieve_response.py
new file mode 100644
index 0000000..f60801e
--- /dev/null
+++ b/src/ark/types/email_retrieve_response.py
@@ -0,0 +1,184 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .shared.api_meta import APIMeta
+
+__all__ = [
+ "EmailRetrieveResponse",
+ "Data",
+ "DataActivity",
+ "DataActivityClick",
+ "DataActivityOpen",
+ "DataAttachment",
+ "DataDelivery",
+]
+
+
+class DataActivityClick(BaseModel):
+ ip_address: Optional[str] = FieldInfo(alias="ipAddress", default=None)
+ """IP address of the clicker"""
+
+ timestamp: Optional[float] = None
+ """Unix timestamp of the click event"""
+
+ timestamp_iso: Optional[datetime] = FieldInfo(alias="timestampIso", default=None)
+ """ISO 8601 timestamp of the click event"""
+
+ url: Optional[str] = None
+ """URL that was clicked"""
+
+ user_agent: Optional[str] = FieldInfo(alias="userAgent", default=None)
+ """User agent of the email client"""
+
+
+class DataActivityOpen(BaseModel):
+ ip_address: Optional[str] = FieldInfo(alias="ipAddress", default=None)
+ """IP address of the opener"""
+
+ timestamp: Optional[float] = None
+ """Unix timestamp of the open event"""
+
+ timestamp_iso: Optional[datetime] = FieldInfo(alias="timestampIso", default=None)
+ """ISO 8601 timestamp of the open event"""
+
+ user_agent: Optional[str] = FieldInfo(alias="userAgent", default=None)
+ """User agent of the email client"""
+
+
+class DataActivity(BaseModel):
+ """Opens and clicks tracking data (included if expand=activity)"""
+
+ clicks: Optional[List[DataActivityClick]] = None
+ """List of link click events"""
+
+ opens: Optional[List[DataActivityOpen]] = None
+ """List of email open events"""
+
+
+class DataAttachment(BaseModel):
+ """An email attachment retrieved from a sent message"""
+
+ content_type: str = FieldInfo(alias="contentType")
+ """MIME type of the attachment"""
+
+ data: str
+ """Base64 encoded attachment content. Decode this to get the raw file bytes."""
+
+ filename: str
+ """Original filename of the attachment"""
+
+ hash: str
+ """SHA256 hash of the attachment content for verification"""
+
+ size: int
+ """Size of the attachment in bytes"""
+
+
+class DataDelivery(BaseModel):
+ id: str
+ """Delivery attempt ID"""
+
+ status: str
+ """Delivery status (lowercase)"""
+
+ timestamp: float
+ """Unix timestamp"""
+
+ timestamp_iso: datetime = FieldInfo(alias="timestampIso")
+ """ISO 8601 timestamp"""
+
+ code: Optional[int] = None
+ """SMTP response code"""
+
+ details: Optional[str] = None
+ """Status details"""
+
+ output: Optional[str] = None
+ """SMTP server response from the receiving mail server"""
+
+ sent_with_ssl: Optional[bool] = FieldInfo(alias="sentWithSsl", default=None)
+ """Whether TLS was used"""
+
+
+class Data(BaseModel):
+ id: str
+ """Unique message identifier (token)"""
+
+ from_: str = FieldInfo(alias="from")
+ """Sender address"""
+
+ scope: Literal["outgoing", "incoming"]
+ """Message direction"""
+
+ status: Literal["pending", "sent", "softfail", "hardfail", "bounced", "held"]
+ """Current delivery status:
+
+ - `pending` - Email accepted, waiting to be processed
+ - `sent` - Email transmitted to recipient's mail server
+ - `softfail` - Temporary delivery failure, will retry
+ - `hardfail` - Permanent delivery failure
+ - `bounced` - Email bounced back
+ - `held` - Held for manual review
+ """
+
+ subject: str
+ """Email subject line"""
+
+ timestamp: float
+ """Unix timestamp when the email was sent"""
+
+ timestamp_iso: datetime = FieldInfo(alias="timestampIso")
+ """ISO 8601 formatted timestamp"""
+
+ to: str
+ """Recipient address"""
+
+ activity: Optional[DataActivity] = None
+ """Opens and clicks tracking data (included if expand=activity)"""
+
+ attachments: Optional[List[DataAttachment]] = None
+ """File attachments (included if expand=attachments)"""
+
+ deliveries: Optional[List[DataDelivery]] = None
+ """Delivery attempt history (included if expand=deliveries)"""
+
+ headers: Optional[Dict[str, str]] = None
+ """Email headers (included if expand=headers)"""
+
+ html_body: Optional[str] = FieldInfo(alias="htmlBody", default=None)
+ """HTML body content (included if expand=content)"""
+
+ message_id: Optional[str] = FieldInfo(alias="messageId", default=None)
+ """SMTP Message-ID header"""
+
+ plain_body: Optional[str] = FieldInfo(alias="plainBody", default=None)
+ """Plain text body (included if expand=content)"""
+
+ raw_message: Optional[str] = FieldInfo(alias="rawMessage", default=None)
+ """
+ Complete raw MIME message, base64 encoded (included if expand=raw). Decode this
+ to get the original RFC 2822 formatted email.
+ """
+
+ spam: Optional[bool] = None
+ """Whether the message was flagged as spam"""
+
+ spam_score: Optional[float] = FieldInfo(alias="spamScore", default=None)
+ """Spam score (if applicable)"""
+
+ tag: Optional[str] = None
+ """Optional categorization tag"""
+
+
+class EmailRetrieveResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/email_retry_response.py b/src/ark/types/email_retry_response.py
new file mode 100644
index 0000000..f63cc99
--- /dev/null
+++ b/src/ark/types/email_retry_response.py
@@ -0,0 +1,23 @@
+# 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__ = ["EmailRetryResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """Email identifier (token)"""
+
+ message: str
+
+
+class EmailRetryResponse(BaseModel):
+ data: Data
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/email_send_batch_response.py b/src/ark/types/email_send_batch_response.py
index 272119c..d32e3fc 100644
--- a/src/ark/types/email_send_batch_response.py
+++ b/src/ark/types/email_send_batch_response.py
@@ -11,9 +11,7 @@
class DataMessages(BaseModel):
id: str
- """Message ID"""
-
- token: str
+ """Message identifier (token)"""
class Data(BaseModel):
diff --git a/src/ark/types/email_send_raw_response.py b/src/ark/types/email_send_raw_response.py
index f1e8c03..46e7c96 100644
--- a/src/ark/types/email_send_raw_response.py
+++ b/src/ark/types/email_send_raw_response.py
@@ -13,7 +13,7 @@
class Data(BaseModel):
id: str
- """Unique message ID (format: msg*{id}*{token})"""
+ """Unique message identifier (token)"""
status: Literal["pending", "sent"]
"""Current delivery status"""
diff --git a/src/ark/types/email_send_response.py b/src/ark/types/email_send_response.py
index 39e1d36..cb7d814 100644
--- a/src/ark/types/email_send_response.py
+++ b/src/ark/types/email_send_response.py
@@ -13,7 +13,7 @@
class Data(BaseModel):
id: str
- """Unique message ID (format: msg*{id}*{token})"""
+ """Unique message identifier (token)"""
status: Literal["pending", "sent"]
"""Current delivery status"""
diff --git a/src/ark/types/log_entry.py b/src/ark/types/log_entry.py
index 9fad7e7..4dfdd6c 100644
--- a/src/ark/types/log_entry.py
+++ b/src/ark/types/log_entry.py
@@ -57,7 +57,7 @@ class Email(BaseModel):
"""Email-specific data (for email endpoints)"""
id: Optional[str] = None
- """Email message ID"""
+ """Email message identifier (token)"""
recipient_count: Optional[int] = FieldInfo(alias="recipientCount", default=None)
"""Number of recipients"""
diff --git a/src/ark/types/webhook_create_params.py b/src/ark/types/webhook_create_params.py
index 8b7a63e..9899826 100644
--- a/src/ark/types/webhook_create_params.py
+++ b/src/ark/types/webhook_create_params.py
@@ -34,8 +34,6 @@ class WebhookCreateParams(TypedDict, total=False):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
]
diff --git a/src/ark/types/webhook_create_response.py b/src/ark/types/webhook_create_response.py
index 07f75ec..d78bb58 100644
--- a/src/ark/types/webhook_create_response.py
+++ b/src/ark/types/webhook_create_response.py
@@ -34,8 +34,6 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
"""Subscribed events"""
diff --git a/src/ark/types/webhook_list_deliveries_params.py b/src/ark/types/webhook_list_deliveries_params.py
index cce2c61..c4048e4 100644
--- a/src/ark/types/webhook_list_deliveries_params.py
+++ b/src/ark/types/webhook_list_deliveries_params.py
@@ -25,8 +25,6 @@ class WebhookListDeliveriesParams(TypedDict, total=False):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
"""Filter by event type"""
diff --git a/src/ark/types/webhook_list_deliveries_response.py b/src/ark/types/webhook_list_deliveries_response.py
index cb5033d..71a5fe2 100644
--- a/src/ark/types/webhook_list_deliveries_response.py
+++ b/src/ark/types/webhook_list_deliveries_response.py
@@ -30,8 +30,6 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
"""Event type that triggered this delivery"""
diff --git a/src/ark/types/webhook_retrieve_delivery_response.py b/src/ark/types/webhook_retrieve_delivery_response.py
index 9a0c38b..efec5c4 100644
--- a/src/ark/types/webhook_retrieve_delivery_response.py
+++ b/src/ark/types/webhook_retrieve_delivery_response.py
@@ -50,8 +50,6 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
"""Event type that triggered this delivery"""
diff --git a/src/ark/types/webhook_retrieve_response.py b/src/ark/types/webhook_retrieve_response.py
index db780ca..6cb2f2e 100644
--- a/src/ark/types/webhook_retrieve_response.py
+++ b/src/ark/types/webhook_retrieve_response.py
@@ -34,8 +34,6 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
"""Subscribed events"""
diff --git a/src/ark/types/webhook_test_params.py b/src/ark/types/webhook_test_params.py
index 5648540..3f91fca 100644
--- a/src/ark/types/webhook_test_params.py
+++ b/src/ark/types/webhook_test_params.py
@@ -18,8 +18,6 @@ class WebhookTestParams(TypedDict, total=False):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
"""Event type to simulate"""
diff --git a/src/ark/types/webhook_update_response.py b/src/ark/types/webhook_update_response.py
index 75c6d59..4d9a0c6 100644
--- a/src/ark/types/webhook_update_response.py
+++ b/src/ark/types/webhook_update_response.py
@@ -34,8 +34,6 @@ class Data(BaseModel):
"MessageLinkClicked",
"MessageLoaded",
"DomainDNSError",
- "SendLimitApproaching",
- "SendLimitExceeded",
]
]
"""Subscribed events"""
diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py
index e416353..7a54df7 100644
--- a/tests/api_resources/test_emails.py
+++ b/tests/api_resources/test_emails.py
@@ -11,8 +11,11 @@
from ark.types import (
EmailListResponse,
EmailSendResponse,
+ EmailRetryResponse,
EmailSendRawResponse,
+ EmailRetrieveResponse,
EmailSendBatchResponse,
+ EmailRetrieveDeliveriesResponse,
)
from tests.utils import assert_matches_type
from ark.pagination import SyncPageNumberPagination, AsyncPageNumberPagination
@@ -23,6 +26,52 @@
class TestEmails:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @parametrize
+ def test_method_retrieve(self, client: Ark) -> None:
+ email = client.emails.retrieve(
+ 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",
+ expand="full",
+ )
+ assert_matches_type(EmailRetrieveResponse, email, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Ark) -> None:
+ response = client.emails.with_raw_response.retrieve(
+ id="aBc123XyZ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email = response.parse()
+ assert_matches_type(EmailRetrieveResponse, email, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Ark) -> None:
+ with client.emails.with_streaming_response.retrieve(
+ id="aBc123XyZ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email = response.parse()
+ assert_matches_type(EmailRetrieveResponse, email, 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 `id` but received ''"):
+ client.emails.with_raw_response.retrieve(
+ id="",
+ )
+
@parametrize
def test_method_list(self, client: Ark) -> None:
email = client.emails.list()
@@ -62,6 +111,82 @@ def test_streaming_response_list(self, client: Ark) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ def test_method_retrieve_deliveries(self, client: Ark) -> None:
+ email = client.emails.retrieve_deliveries(
+ "aBc123XyZ",
+ )
+ assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve_deliveries(self, client: Ark) -> None:
+ response = client.emails.with_raw_response.retrieve_deliveries(
+ "aBc123XyZ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email = response.parse()
+ assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve_deliveries(self, client: Ark) -> None:
+ with client.emails.with_streaming_response.retrieve_deliveries(
+ "aBc123XyZ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email = response.parse()
+ assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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 ''"):
+ client.emails.with_raw_response.retrieve_deliveries(
+ "",
+ )
+
+ @parametrize
+ def test_method_retry(self, client: Ark) -> None:
+ email = client.emails.retry(
+ "aBc123XyZ",
+ )
+ assert_matches_type(EmailRetryResponse, email, path=["response"])
+
+ @parametrize
+ def test_raw_response_retry(self, client: Ark) -> None:
+ response = client.emails.with_raw_response.retry(
+ "aBc123XyZ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email = response.parse()
+ assert_matches_type(EmailRetryResponse, email, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retry(self, client: Ark) -> None:
+ with client.emails.with_streaming_response.retry(
+ "aBc123XyZ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email = response.parse()
+ assert_matches_type(EmailRetryResponse, email, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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 ''"):
+ client.emails.with_raw_response.retry(
+ "",
+ )
+
@parametrize
def test_method_send(self, client: Ark) -> None:
email = client.emails.send(
@@ -275,6 +400,52 @@ class TestAsyncEmails:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncArk) -> None:
+ email = await async_client.emails.retrieve(
+ 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",
+ expand="full",
+ )
+ assert_matches_type(EmailRetrieveResponse, email, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
+ response = await async_client.emails.with_raw_response.retrieve(
+ id="aBc123XyZ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email = await response.parse()
+ assert_matches_type(EmailRetrieveResponse, email, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
+ async with async_client.emails.with_streaming_response.retrieve(
+ id="aBc123XyZ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email = await response.parse()
+ assert_matches_type(EmailRetrieveResponse, email, 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 `id` but received ''"):
+ await async_client.emails.with_raw_response.retrieve(
+ id="",
+ )
+
@parametrize
async def test_method_list(self, async_client: AsyncArk) -> None:
email = await async_client.emails.list()
@@ -314,6 +485,82 @@ async def test_streaming_response_list(self, async_client: AsyncArk) -> None:
assert cast(Any, response.is_closed) is True
+ @parametrize
+ async def test_method_retrieve_deliveries(self, async_client: AsyncArk) -> None:
+ email = await async_client.emails.retrieve_deliveries(
+ "aBc123XyZ",
+ )
+ assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve_deliveries(self, async_client: AsyncArk) -> None:
+ response = await async_client.emails.with_raw_response.retrieve_deliveries(
+ "aBc123XyZ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email = await response.parse()
+ assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve_deliveries(self, async_client: AsyncArk) -> None:
+ async with async_client.emails.with_streaming_response.retrieve_deliveries(
+ "aBc123XyZ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email = await response.parse()
+ assert_matches_type(EmailRetrieveDeliveriesResponse, email, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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 ''"):
+ await async_client.emails.with_raw_response.retrieve_deliveries(
+ "",
+ )
+
+ @parametrize
+ async def test_method_retry(self, async_client: AsyncArk) -> None:
+ email = await async_client.emails.retry(
+ "aBc123XyZ",
+ )
+ assert_matches_type(EmailRetryResponse, email, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retry(self, async_client: AsyncArk) -> None:
+ response = await async_client.emails.with_raw_response.retry(
+ "aBc123XyZ",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ email = await response.parse()
+ assert_matches_type(EmailRetryResponse, email, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retry(self, async_client: AsyncArk) -> None:
+ async with async_client.emails.with_streaming_response.retry(
+ "aBc123XyZ",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ email = await response.parse()
+ assert_matches_type(EmailRetryResponse, email, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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 ''"):
+ await async_client.emails.with_raw_response.retry(
+ "",
+ )
+
@parametrize
async def test_method_send(self, async_client: AsyncArk) -> None:
email = await async_client.emails.send(
From 8540e7cd143d28f9935bc71d279f343297fe5b26 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 30 Jan 2026 15:29:54 +0000
Subject: [PATCH 3/3] release: 0.16.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 9 +++++++++
pyproject.toml | 2 +-
src/ark/_version.py | 2 +-
4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 8f3e0a4..b4e9013 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.15.0"
+ ".": "0.16.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33dca82..b2c54d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 0.16.0 (2026-01-30)
+
+Full Changelog: [v0.15.0...v0.16.0](https://github.com/ArkHQ-io/ark-python/compare/v0.15.0...v0.16.0)
+
+### Features
+
+* **api:** api update ([56309b8](https://github.com/ArkHQ-io/ark-python/commit/56309b8f34212923562d01dbad53df307d1a6a97))
+* **api:** manual updates ([a6fdf54](https://github.com/ArkHQ-io/ark-python/commit/a6fdf54cba7744137502a2e97f9105ef204f4d79))
+
## 0.15.0 (2026-01-30)
Full Changelog: [v0.14.0...v0.15.0](https://github.com/ArkHQ-io/ark-python/compare/v0.14.0...v0.15.0)
diff --git a/pyproject.toml b/pyproject.toml
index 6c28b5f..c95a3cf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "ark-email"
-version = "0.15.0"
+version = "0.16.0"
description = "The official Python library for the ark API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/ark/_version.py b/src/ark/_version.py
index 1750a65..1f10948 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.15.0" # x-release-please-version
+__version__ = "0.16.0" # x-release-please-version