From 88b329cfe0db4eab88d4098e4e5ca7e7acbc738f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 26 Jan 2026 18:26:11 +0000
Subject: [PATCH 1/9] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index ebb8e0e..5fbb89c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-b651c88f3a87cebd528784fef019612f41517d0912c7e5695bb4419d00b9409b.yml
-openapi_spec_hash: 33d0f5c2bb0349abf085404e2a3b05f7
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-7279acbed237faa46b87fb0f40ad49a18d5d022b5626e076a03474489a2c2155.yml
+openapi_spec_hash: e4d83bccb18efb73f36af33741e91d22
config_hash: fbd4e7a9ee50aad316893984a725519b
From 69296893c13ad8e4f05047f312009e88a8f02830 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 13:32:06 +0000
Subject: [PATCH 2/9] feat(api): domain list improvement
improve docs for domain endpoints
---
.stats.yml | 4 +--
src/ark/types/dns_record.py | 18 ++++++------
src/ark/types/domain_create_response.py | 35 +++++++++++++++++------
src/ark/types/domain_list_response.py | 13 +++++----
src/ark/types/domain_retrieve_response.py | 35 +++++++++++++++++------
src/ark/types/domain_verify_response.py | 35 +++++++++++++++++------
src/ark/types/track_domain.py | 5 +++-
7 files changed, 101 insertions(+), 44 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 5fbb89c..d68a48a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-7279acbed237faa46b87fb0f40ad49a18d5d022b5626e076a03474489a2c2155.yml
-openapi_spec_hash: e4d83bccb18efb73f36af33741e91d22
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-e8db3d876bf08353291a905e38e3f803a17ec731e99f2c36bc8af39e2e14e797.yml
+openapi_spec_hash: 23ce09c8163237311498b548212d8367
config_hash: fbd4e7a9ee50aad316893984a725519b
diff --git a/src/ark/types/dns_record.py b/src/ark/types/dns_record.py
index 01a9744..11e28f9 100644
--- a/src/ark/types/dns_record.py
+++ b/src/ark/types/dns_record.py
@@ -9,20 +9,22 @@
class DNSRecord(BaseModel):
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
name: str
- """DNS record name (hostname)"""
+ """The hostname where the record should be created (relative to your domain)"""
type: Literal["TXT", "CNAME", "MX"]
- """DNS record type"""
+ """The DNS record type to create"""
value: str
- """DNS record value"""
+ """The value to set for the DNS record"""
status: Optional[Literal["OK", "Missing", "Invalid"]] = None
- """DNS verification status:
+ """Current verification status of this DNS record:
- - `OK` - Record is correctly configured
- - `Missing` - Record not found in DNS
- - `Invalid` - Record exists but has wrong value
- - `null` - Not yet checked
+ - `OK` - Record is correctly configured and verified
+ - `Missing` - Record was not found in your DNS
+ - `Invalid` - Record exists but has an incorrect value
+ - `null` - Record has not been checked yet
"""
diff --git a/src/ark/types/domain_create_response.py b/src/ark/types/domain_create_response.py
index 68ad482..732dc21 100644
--- a/src/ark/types/domain_create_response.py
+++ b/src/ark/types/domain_create_response.py
@@ -14,31 +14,48 @@
class DataDNSRecords(BaseModel):
- dkim: DNSRecord
+ """DNS records that must be added to your domain's DNS settings.
- return_path: DNSRecord = FieldInfo(alias="returnPath")
+ Null if records are not yet generated.
+ """
- spf: DNSRecord
+ dkim: Optional[DNSRecord] = None
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
+ return_path: Optional[DNSRecord] = FieldInfo(alias="returnPath", default=None)
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
+ spf: Optional[DNSRecord] = None
+ """A DNS record that needs to be configured in your domain's DNS settings"""
class Data(BaseModel):
- id: str
- """Domain ID"""
+ id: int
+ """Unique domain identifier"""
created_at: datetime = FieldInfo(alias="createdAt")
+ """Timestamp when the domain was added"""
- dns_records: DataDNSRecords = FieldInfo(alias="dnsRecords")
+ dns_records: Optional[DataDNSRecords] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records that must be added to your domain's DNS settings.
+
+ Null if records are not yet generated.
+ """
name: str
- """Domain name"""
+ """The domain name used for sending emails"""
uuid: str
+ """UUID of the domain"""
verified: bool
- """Whether DNS is verified"""
+ """Whether all DNS records (SPF, DKIM, Return Path) are correctly configured.
+
+ Domain must be verified before sending emails.
+ """
verified_at: Optional[datetime] = FieldInfo(alias="verifiedAt", default=None)
- """When the domain was verified (null if not verified)"""
+ """Timestamp when the domain ownership was verified, or null if not yet verified"""
class DomainCreateResponse(BaseModel):
diff --git a/src/ark/types/domain_list_response.py b/src/ark/types/domain_list_response.py
index 25df99d..50edc86 100644
--- a/src/ark/types/domain_list_response.py
+++ b/src/ark/types/domain_list_response.py
@@ -3,8 +3,6 @@
from typing import List
from typing_extensions import Literal
-from pydantic import Field as FieldInfo
-
from .._models import BaseModel
from .shared.api_meta import APIMeta
@@ -12,14 +10,17 @@
class DataDomain(BaseModel):
- id: str
- """Domain ID"""
-
- dns_ok: bool = FieldInfo(alias="dnsOk")
+ id: int
+ """Unique domain identifier"""
name: str
+ """The domain name used for sending emails"""
verified: bool
+ """Whether all DNS records (SPF, DKIM, Return Path) are correctly configured.
+
+ Domain must be verified before sending emails.
+ """
class Data(BaseModel):
diff --git a/src/ark/types/domain_retrieve_response.py b/src/ark/types/domain_retrieve_response.py
index 0c25702..c0e6e7d 100644
--- a/src/ark/types/domain_retrieve_response.py
+++ b/src/ark/types/domain_retrieve_response.py
@@ -14,31 +14,48 @@
class DataDNSRecords(BaseModel):
- dkim: DNSRecord
+ """DNS records that must be added to your domain's DNS settings.
- return_path: DNSRecord = FieldInfo(alias="returnPath")
+ Null if records are not yet generated.
+ """
- spf: DNSRecord
+ dkim: Optional[DNSRecord] = None
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
+ return_path: Optional[DNSRecord] = FieldInfo(alias="returnPath", default=None)
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
+ spf: Optional[DNSRecord] = None
+ """A DNS record that needs to be configured in your domain's DNS settings"""
class Data(BaseModel):
- id: str
- """Domain ID"""
+ id: int
+ """Unique domain identifier"""
created_at: datetime = FieldInfo(alias="createdAt")
+ """Timestamp when the domain was added"""
- dns_records: DataDNSRecords = FieldInfo(alias="dnsRecords")
+ dns_records: Optional[DataDNSRecords] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records that must be added to your domain's DNS settings.
+
+ Null if records are not yet generated.
+ """
name: str
- """Domain name"""
+ """The domain name used for sending emails"""
uuid: str
+ """UUID of the domain"""
verified: bool
- """Whether DNS is verified"""
+ """Whether all DNS records (SPF, DKIM, Return Path) are correctly configured.
+
+ Domain must be verified before sending emails.
+ """
verified_at: Optional[datetime] = FieldInfo(alias="verifiedAt", default=None)
- """When the domain was verified (null if not verified)"""
+ """Timestamp when the domain ownership was verified, or null if not yet verified"""
class DomainRetrieveResponse(BaseModel):
diff --git a/src/ark/types/domain_verify_response.py b/src/ark/types/domain_verify_response.py
index 63afdd7..f472a2f 100644
--- a/src/ark/types/domain_verify_response.py
+++ b/src/ark/types/domain_verify_response.py
@@ -14,31 +14,48 @@
class DataDNSRecords(BaseModel):
- dkim: DNSRecord
+ """DNS records that must be added to your domain's DNS settings.
- return_path: DNSRecord = FieldInfo(alias="returnPath")
+ Null if records are not yet generated.
+ """
- spf: DNSRecord
+ dkim: Optional[DNSRecord] = None
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
+ return_path: Optional[DNSRecord] = FieldInfo(alias="returnPath", default=None)
+ """A DNS record that needs to be configured in your domain's DNS settings"""
+
+ spf: Optional[DNSRecord] = None
+ """A DNS record that needs to be configured in your domain's DNS settings"""
class Data(BaseModel):
- id: str
- """Domain ID"""
+ id: int
+ """Unique domain identifier"""
created_at: datetime = FieldInfo(alias="createdAt")
+ """Timestamp when the domain was added"""
- dns_records: DataDNSRecords = FieldInfo(alias="dnsRecords")
+ dns_records: Optional[DataDNSRecords] = FieldInfo(alias="dnsRecords", default=None)
+ """DNS records that must be added to your domain's DNS settings.
+
+ Null if records are not yet generated.
+ """
name: str
- """Domain name"""
+ """The domain name used for sending emails"""
uuid: str
+ """UUID of the domain"""
verified: bool
- """Whether DNS is verified"""
+ """Whether all DNS records (SPF, DKIM, Return Path) are correctly configured.
+
+ Domain must be verified before sending emails.
+ """
verified_at: Optional[datetime] = FieldInfo(alias="verifiedAt", default=None)
- """When the domain was verified (null if not verified)"""
+ """Timestamp when the domain ownership was verified, or null if not yet verified"""
class DomainVerifyResponse(BaseModel):
diff --git a/src/ark/types/track_domain.py b/src/ark/types/track_domain.py
index f630e82..f2786ea 100644
--- a/src/ark/types/track_domain.py
+++ b/src/ark/types/track_domain.py
@@ -32,7 +32,10 @@ class TrackDomain(BaseModel):
"""When the track domain was created"""
dns_ok: bool = FieldInfo(alias="dnsOk")
- """Whether DNS is correctly configured"""
+ """Whether the tracking CNAME record is correctly configured.
+
+ Must be true to use tracking features.
+ """
domain_id: str = FieldInfo(alias="domainId")
"""ID of the parent sending domain"""
From 48b94f59a27f5a6c4f15313c492c14d0d41d4d82 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 14:26:16 +0000
Subject: [PATCH 3/9] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index d68a48a..12540f0 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-e8db3d876bf08353291a905e38e3f803a17ec731e99f2c36bc8af39e2e14e797.yml
-openapi_spec_hash: 23ce09c8163237311498b548212d8367
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-eafcdb767b2180578a316ee7128571845220fdf6b867d5a48ce5d2e65c956194.yml
+openapi_spec_hash: 345689e75317d6d17d90c9ea21fa6c08
config_hash: fbd4e7a9ee50aad316893984a725519b
From 10f496a29924ecd9bcc93d0551ea8bd29662aa89 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 17:26:18 +0000
Subject: [PATCH 4/9] feat(api): api update
---
.stats.yml | 4 +-
src/ark/types/dns_record.py | 31 ++++++++-
src/ark/types/domain_create_response.py | 76 ++++++++++++++++++++++-
src/ark/types/domain_retrieve_response.py | 76 ++++++++++++++++++++++-
src/ark/types/domain_verify_response.py | 76 ++++++++++++++++++++++-
5 files changed, 250 insertions(+), 13 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 12540f0..1daf23b 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-eafcdb767b2180578a316ee7128571845220fdf6b867d5a48ce5d2e65c956194.yml
-openapi_spec_hash: 345689e75317d6d17d90c9ea21fa6c08
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-8670129913b62cd1d61750d00086e32d3dd0ab9a7a3f8e77bcd9d1703a66131a.yml
+openapi_spec_hash: 4d0cda60fbd3e18171bec9dc86e58036
config_hash: fbd4e7a9ee50aad316893984a725519b
diff --git a/src/ark/types/dns_record.py b/src/ark/types/dns_record.py
index 11e28f9..775e76e 100644
--- a/src/ark/types/dns_record.py
+++ b/src/ark/types/dns_record.py
@@ -3,16 +3,43 @@
from typing import Optional
from typing_extensions import Literal
+from pydantic import Field as FieldInfo
+
from .._models import BaseModel
__all__ = ["DNSRecord"]
class DNSRecord(BaseModel):
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider (which auto-appends the zone).
+ The `fullName` field contains the complete fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
+
+ full_name: str = FieldInfo(alias="fullName")
+ """
+ The complete fully-qualified domain name (FQDN). Use this as a reference to
+ verify the record is configured correctly.
+ """
name: str
- """The hostname where the record should be created (relative to your domain)"""
+ """
+ The relative hostname to enter in your DNS provider. Most DNS providers
+ auto-append the zone name, so you only need to enter this relative part.
+
+ - `"@"` means the apex/root of the zone (for root domains)
+ - `"mail"` for a subdomain like `mail.example.com`
+ - `"ark-xyz._domainkey.mail"` for DKIM on a subdomain
+ """
type: Literal["TXT", "CNAME", "MX"]
"""The DNS record type to create"""
diff --git a/src/ark/types/domain_create_response.py b/src/ark/types/domain_create_response.py
index 732dc21..56fdef4 100644
--- a/src/ark/types/domain_create_response.py
+++ b/src/ark/types/domain_create_response.py
@@ -17,16 +17,76 @@ class DataDNSRecords(BaseModel):
"""DNS records that must be added to your domain's DNS settings.
Null if records are not yet generated.
+
+ **Important:** The `name` field contains the relative hostname that you should enter in your DNS provider.
+ Most DNS providers auto-append the zone name, so you only need to enter the relative part.
+
+ For subdomains like `mail.example.com`, the zone is `example.com`, so:
+ - SPF `name` would be `mail` (not `@`)
+ - DKIM `name` would be `ark-xyz._domainkey.mail`
+ - Return Path `name` would be `psrp.mail`
"""
dkim: Optional[DNSRecord] = None
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
return_path: Optional[DNSRecord] = FieldInfo(alias="returnPath", default=None)
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
spf: Optional[DNSRecord] = None
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
+
+ zone: Optional[str] = None
+ """
+ The DNS zone (registrable domain) where records should be added. This is the
+ root domain that your DNS provider manages. For `mail.example.com`, the zone is
+ `example.com`. For `example.co.uk`, the zone is `example.co.uk`.
+ """
class Data(BaseModel):
@@ -40,6 +100,16 @@ class Data(BaseModel):
"""DNS records that must be added to your domain's DNS settings.
Null if records are not yet generated.
+
+ **Important:** The `name` field contains the relative hostname that you should
+ enter in your DNS provider. Most DNS providers auto-append the zone name, so you
+ only need to enter the relative part.
+
+ For subdomains like `mail.example.com`, the zone is `example.com`, so:
+
+ - SPF `name` would be `mail` (not `@`)
+ - DKIM `name` would be `ark-xyz._domainkey.mail`
+ - Return Path `name` would be `psrp.mail`
"""
name: str
diff --git a/src/ark/types/domain_retrieve_response.py b/src/ark/types/domain_retrieve_response.py
index c0e6e7d..bc9d550 100644
--- a/src/ark/types/domain_retrieve_response.py
+++ b/src/ark/types/domain_retrieve_response.py
@@ -17,16 +17,76 @@ class DataDNSRecords(BaseModel):
"""DNS records that must be added to your domain's DNS settings.
Null if records are not yet generated.
+
+ **Important:** The `name` field contains the relative hostname that you should enter in your DNS provider.
+ Most DNS providers auto-append the zone name, so you only need to enter the relative part.
+
+ For subdomains like `mail.example.com`, the zone is `example.com`, so:
+ - SPF `name` would be `mail` (not `@`)
+ - DKIM `name` would be `ark-xyz._domainkey.mail`
+ - Return Path `name` would be `psrp.mail`
"""
dkim: Optional[DNSRecord] = None
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
return_path: Optional[DNSRecord] = FieldInfo(alias="returnPath", default=None)
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
spf: Optional[DNSRecord] = None
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
+
+ zone: Optional[str] = None
+ """
+ The DNS zone (registrable domain) where records should be added. This is the
+ root domain that your DNS provider manages. For `mail.example.com`, the zone is
+ `example.com`. For `example.co.uk`, the zone is `example.co.uk`.
+ """
class Data(BaseModel):
@@ -40,6 +100,16 @@ class Data(BaseModel):
"""DNS records that must be added to your domain's DNS settings.
Null if records are not yet generated.
+
+ **Important:** The `name` field contains the relative hostname that you should
+ enter in your DNS provider. Most DNS providers auto-append the zone name, so you
+ only need to enter the relative part.
+
+ For subdomains like `mail.example.com`, the zone is `example.com`, so:
+
+ - SPF `name` would be `mail` (not `@`)
+ - DKIM `name` would be `ark-xyz._domainkey.mail`
+ - Return Path `name` would be `psrp.mail`
"""
name: str
diff --git a/src/ark/types/domain_verify_response.py b/src/ark/types/domain_verify_response.py
index f472a2f..8bc1ae9 100644
--- a/src/ark/types/domain_verify_response.py
+++ b/src/ark/types/domain_verify_response.py
@@ -17,16 +17,76 @@ class DataDNSRecords(BaseModel):
"""DNS records that must be added to your domain's DNS settings.
Null if records are not yet generated.
+
+ **Important:** The `name` field contains the relative hostname that you should enter in your DNS provider.
+ Most DNS providers auto-append the zone name, so you only need to enter the relative part.
+
+ For subdomains like `mail.example.com`, the zone is `example.com`, so:
+ - SPF `name` would be `mail` (not `@`)
+ - DKIM `name` would be `ark-xyz._domainkey.mail`
+ - Return Path `name` would be `psrp.mail`
"""
dkim: Optional[DNSRecord] = None
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
return_path: Optional[DNSRecord] = FieldInfo(alias="returnPath", default=None)
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
spf: Optional[DNSRecord] = None
- """A DNS record that needs to be configured in your domain's DNS settings"""
+ """A DNS record that needs to be configured in your domain's DNS settings.
+
+ The `name` field contains the relative hostname to enter in your DNS provider
+ (which auto-appends the zone). The `fullName` field contains the complete
+ fully-qualified domain name (FQDN) for reference.
+
+ **Example for subdomain `mail.example.com`:**
+
+ - `name`: `"mail"` (what you enter in DNS provider)
+ - `fullName`: `"mail.example.com"` (the complete hostname)
+
+ **Example for root domain `example.com`:**
+
+ - `name`: `"@"` (DNS shorthand for apex/root)
+ - `fullName`: `"example.com"`
+ """
+
+ zone: Optional[str] = None
+ """
+ The DNS zone (registrable domain) where records should be added. This is the
+ root domain that your DNS provider manages. For `mail.example.com`, the zone is
+ `example.com`. For `example.co.uk`, the zone is `example.co.uk`.
+ """
class Data(BaseModel):
@@ -40,6 +100,16 @@ class Data(BaseModel):
"""DNS records that must be added to your domain's DNS settings.
Null if records are not yet generated.
+
+ **Important:** The `name` field contains the relative hostname that you should
+ enter in your DNS provider. Most DNS providers auto-append the zone name, so you
+ only need to enter the relative part.
+
+ For subdomains like `mail.example.com`, the zone is `example.com`, so:
+
+ - SPF `name` would be `mail` (not `@`)
+ - DKIM `name` would be `ark-xyz._domainkey.mail`
+ - Return Path `name` would be `psrp.mail`
"""
name: str
From 5f358fbcbaca2a03fe984fb9a0d571204f8551f1 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 17:27:00 +0000
Subject: [PATCH 5/9] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 1daf23b..2f411f3 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-8670129913b62cd1d61750d00086e32d3dd0ab9a7a3f8e77bcd9d1703a66131a.yml
-openapi_spec_hash: 4d0cda60fbd3e18171bec9dc86e58036
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-d7be50edd67f0426e0433ccc8d48c1249f39b3f45b8e8dbabde95cb242448b6f.yml
+openapi_spec_hash: 188fc55ae984a1031bc672088e46562c
config_hash: fbd4e7a9ee50aad316893984a725519b
From c20d3f8007956a98182f12ceab74df8c44ea359d Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 18:26:17 +0000
Subject: [PATCH 6/9] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 2f411f3..1daf23b 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-d7be50edd67f0426e0433ccc8d48c1249f39b3f45b8e8dbabde95cb242448b6f.yml
-openapi_spec_hash: 188fc55ae984a1031bc672088e46562c
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-8670129913b62cd1d61750d00086e32d3dd0ab9a7a3f8e77bcd9d1703a66131a.yml
+openapi_spec_hash: 4d0cda60fbd3e18171bec9dc86e58036
config_hash: fbd4e7a9ee50aad316893984a725519b
From f55910772f06f551b98a1ddc7c7dda45fc2176f4 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 29 Jan 2026 06:54:25 +0000
Subject: [PATCH 7/9] fix(docs): fix mcp installation instructions for remote
servers
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 089c30b..bcf106d 100644
--- a/README.md
+++ b/README.md
@@ -13,8 +13,8 @@ It is generated with [Stainless](https://www.stainless.com/).
Use the Ark MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
-[](https://cursor.com/en-US/install-mcp?name=ark-email-mcp&config=eyJuYW1lIjoiYXJrLWVtYWlsLW1jcCIsInRyYW5zcG9ydCI6InNzZSIsInVybCI6Imh0dHBzOi8vYXJrLW1jcC5zdGxtY3AuY29tL3NzZSJ9)
-[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22ark-email-mcp%22%2C%22type%22%3A%22sse%22%2C%22url%22%3A%22https%3A%2F%2Fark-mcp.stlmcp.com%2Fsse%22%7D)
+[](https://cursor.com/en-US/install-mcp?name=ark-email-mcp&config=eyJuYW1lIjoiYXJrLWVtYWlsLW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2Fyay1tY3Auc3RsbWNwLmNvbSIsImhlYWRlcnMiOnsieC1hcmstYXBpLWtleSI6Ik15IEFQSSBLZXkifX0)
+[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22ark-email-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fark-mcp.stlmcp.com%22%2C%22headers%22%3A%7B%22x-ark-api-key%22%3A%22My%20API%20Key%22%7D%7D)
> Note: You may need to set environment variables in your MCP client.
From 4281980353ff7423dbc6627e37abefee952bc489 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 29 Jan 2026 13:54:16 +0000
Subject: [PATCH 8/9] feat(api): add usage and SendLimit Headers
---
.stats.yml | 8 +-
api.md | 12 ++
src/ark/_client.py | 39 +++-
src/ark/resources/__init__.py | 14 ++
src/ark/resources/usage.py | 177 ++++++++++++++++++
src/ark/resources/webhooks.py | 12 --
src/ark/types/__init__.py | 1 +
src/ark/types/usage_retrieve_response.py | 109 +++++++++++
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_usage.py | 74 ++++++++
17 files changed, 429 insertions(+), 33 deletions(-)
create mode 100644 src/ark/resources/usage.py
create mode 100644 src/ark/types/usage_retrieve_response.py
create mode 100644 tests/api_resources/test_usage.py
diff --git a/.stats.yml b/.stats.yml
index 1daf23b..0976618 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 34
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-8670129913b62cd1d61750d00086e32d3dd0ab9a7a3f8e77bcd9d1703a66131a.yml
-openapi_spec_hash: 4d0cda60fbd3e18171bec9dc86e58036
-config_hash: fbd4e7a9ee50aad316893984a725519b
+configured_endpoints: 35
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ark%2Fark-1949bcfc8775c97eca880428dc93e9f97aa91144bef82584027ede5089bb2e19.yml
+openapi_spec_hash: 0aa367455a067b701f18ef7892b6c7e9
+config_hash: 373e654f8034a40c42234eee9ebefbb9
diff --git a/api.md b/api.md
index 4299dc0..d511272 100644
--- a/api.md
+++ b/api.md
@@ -142,3 +142,15 @@ Methods:
- client.logs.retrieve(request_id) -> LogRetrieveResponse
- client.logs.list(\*\*params) -> SyncPageNumberPagination[LogEntry]
+
+# Usage
+
+Types:
+
+```python
+from ark.types import UsageRetrieveResponse
+```
+
+Methods:
+
+- client.usage.retrieve() -> UsageRetrieveResponse
diff --git a/src/ark/_client.py b/src/ark/_client.py
index 31bf629..4a2f2a8 100644
--- a/src/ark/_client.py
+++ b/src/ark/_client.py
@@ -31,8 +31,9 @@
)
if TYPE_CHECKING:
- from .resources import logs, emails, domains, tracking, webhooks, suppressions
+ from .resources import logs, usage, emails, domains, tracking, webhooks, suppressions
from .resources.logs import LogsResource, AsyncLogsResource
+ from .resources.usage import UsageResource, AsyncUsageResource
from .resources.emails import EmailsResource, AsyncEmailsResource
from .resources.domains import DomainsResource, AsyncDomainsResource
from .resources.tracking import TrackingResource, AsyncTrackingResource
@@ -133,6 +134,12 @@ def logs(self) -> LogsResource:
return LogsResource(self)
+ @cached_property
+ def usage(self) -> UsageResource:
+ from .resources.usage import UsageResource
+
+ return UsageResource(self)
+
@cached_property
def with_raw_response(self) -> ArkWithRawResponse:
return ArkWithRawResponse(self)
@@ -337,6 +344,12 @@ def logs(self) -> AsyncLogsResource:
return AsyncLogsResource(self)
+ @cached_property
+ def usage(self) -> AsyncUsageResource:
+ from .resources.usage import AsyncUsageResource
+
+ return AsyncUsageResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncArkWithRawResponse:
return AsyncArkWithRawResponse(self)
@@ -492,6 +505,12 @@ def logs(self) -> logs.LogsResourceWithRawResponse:
return LogsResourceWithRawResponse(self._client.logs)
+ @cached_property
+ def usage(self) -> usage.UsageResourceWithRawResponse:
+ from .resources.usage import UsageResourceWithRawResponse
+
+ return UsageResourceWithRawResponse(self._client.usage)
+
class AsyncArkWithRawResponse:
_client: AsyncArk
@@ -535,6 +554,12 @@ def logs(self) -> logs.AsyncLogsResourceWithRawResponse:
return AsyncLogsResourceWithRawResponse(self._client.logs)
+ @cached_property
+ def usage(self) -> usage.AsyncUsageResourceWithRawResponse:
+ from .resources.usage import AsyncUsageResourceWithRawResponse
+
+ return AsyncUsageResourceWithRawResponse(self._client.usage)
+
class ArkWithStreamedResponse:
_client: Ark
@@ -578,6 +603,12 @@ def logs(self) -> logs.LogsResourceWithStreamingResponse:
return LogsResourceWithStreamingResponse(self._client.logs)
+ @cached_property
+ def usage(self) -> usage.UsageResourceWithStreamingResponse:
+ from .resources.usage import UsageResourceWithStreamingResponse
+
+ return UsageResourceWithStreamingResponse(self._client.usage)
+
class AsyncArkWithStreamedResponse:
_client: AsyncArk
@@ -621,6 +652,12 @@ def logs(self) -> logs.AsyncLogsResourceWithStreamingResponse:
return AsyncLogsResourceWithStreamingResponse(self._client.logs)
+ @cached_property
+ def usage(self) -> usage.AsyncUsageResourceWithStreamingResponse:
+ from .resources.usage import AsyncUsageResourceWithStreamingResponse
+
+ return AsyncUsageResourceWithStreamingResponse(self._client.usage)
+
Client = Ark
diff --git a/src/ark/resources/__init__.py b/src/ark/resources/__init__.py
index 8bd1ac1..76b59c0 100644
--- a/src/ark/resources/__init__.py
+++ b/src/ark/resources/__init__.py
@@ -8,6 +8,14 @@
LogsResourceWithStreamingResponse,
AsyncLogsResourceWithStreamingResponse,
)
+from .usage import (
+ UsageResource,
+ AsyncUsageResource,
+ UsageResourceWithRawResponse,
+ AsyncUsageResourceWithRawResponse,
+ UsageResourceWithStreamingResponse,
+ AsyncUsageResourceWithStreamingResponse,
+)
from .emails import (
EmailsResource,
AsyncEmailsResource,
@@ -86,4 +94,10 @@
"AsyncLogsResourceWithRawResponse",
"LogsResourceWithStreamingResponse",
"AsyncLogsResourceWithStreamingResponse",
+ "UsageResource",
+ "AsyncUsageResource",
+ "UsageResourceWithRawResponse",
+ "AsyncUsageResourceWithRawResponse",
+ "UsageResourceWithStreamingResponse",
+ "AsyncUsageResourceWithStreamingResponse",
]
diff --git a/src/ark/resources/usage.py b/src/ark/resources/usage.py
new file mode 100644
index 0000000..cb9004f
--- /dev/null
+++ b/src/ark/resources/usage.py
@@ -0,0 +1,177 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from .._types import Body, Query, Headers, NotGiven, not_given
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.usage_retrieve_response import UsageRetrieveResponse
+
+__all__ = ["UsageResource", "AsyncUsageResource"]
+
+
+class UsageResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> UsageResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return UsageResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> UsageResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return UsageResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> UsageRetrieveResponse:
+ """
+ Returns current usage and limit information for your account.
+
+ This endpoint is designed for:
+
+ - **AI agents/MCP servers:** Check constraints before planning batch operations
+ - **Monitoring dashboards:** Display current usage status
+ - **Rate limit awareness:** Know remaining capacity before making requests
+
+ **Response includes:**
+
+ - `rateLimit` - API request rate limit (requests per second)
+ - `sendLimit` - Email sending limit (emails per hour)
+ - `billing` - Credit balance and auto-recharge configuration
+
+ **Notes:**
+
+ - This request counts against your rate limit
+ - `sendLimit` may be null if Postal is temporarily unavailable
+ - `billing` is null if billing is not configured
+ - Send limit resets at the top of each hour
+ """
+ return self._get(
+ "/usage",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UsageRetrieveResponse,
+ )
+
+
+class AsyncUsageResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncUsageResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncUsageResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncUsageResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/ArkHQ-io/ark-python#with_streaming_response
+ """
+ return AsyncUsageResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> UsageRetrieveResponse:
+ """
+ Returns current usage and limit information for your account.
+
+ This endpoint is designed for:
+
+ - **AI agents/MCP servers:** Check constraints before planning batch operations
+ - **Monitoring dashboards:** Display current usage status
+ - **Rate limit awareness:** Know remaining capacity before making requests
+
+ **Response includes:**
+
+ - `rateLimit` - API request rate limit (requests per second)
+ - `sendLimit` - Email sending limit (emails per hour)
+ - `billing` - Credit balance and auto-recharge configuration
+
+ **Notes:**
+
+ - This request counts against your rate limit
+ - `sendLimit` may be null if Postal is temporarily unavailable
+ - `billing` is null if billing is not configured
+ - Send limit resets at the top of each hour
+ """
+ return await self._get(
+ "/usage",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UsageRetrieveResponse,
+ )
+
+
+class UsageResourceWithRawResponse:
+ def __init__(self, usage: UsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = to_raw_response_wrapper(
+ usage.retrieve,
+ )
+
+
+class AsyncUsageResourceWithRawResponse:
+ def __init__(self, usage: AsyncUsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = async_to_raw_response_wrapper(
+ usage.retrieve,
+ )
+
+
+class UsageResourceWithStreamingResponse:
+ def __init__(self, usage: UsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = to_streamed_response_wrapper(
+ usage.retrieve,
+ )
+
+
+class AsyncUsageResourceWithStreamingResponse:
+ def __init__(self, usage: AsyncUsageResource) -> None:
+ self._usage = usage
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ usage.retrieve,
+ )
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 c543a4e..6cd75fb 100644
--- a/src/ark/types/__init__.py
+++ b/src/ark/types/__init__.py
@@ -33,6 +33,7 @@
from .email_send_batch_params import EmailSendBatchParams as EmailSendBatchParams
from .email_send_raw_response import EmailSendRawResponse as EmailSendRawResponse
from .suppression_list_params import SuppressionListParams as SuppressionListParams
+from .usage_retrieve_response import UsageRetrieveResponse as UsageRetrieveResponse
from .webhook_create_response import WebhookCreateResponse as WebhookCreateResponse
from .webhook_delete_response import WebhookDeleteResponse as WebhookDeleteResponse
from .webhook_update_response import WebhookUpdateResponse as WebhookUpdateResponse
diff --git a/src/ark/types/usage_retrieve_response.py b/src/ark/types/usage_retrieve_response.py
new file mode 100644
index 0000000..86daa16
--- /dev/null
+++ b/src/ark/types/usage_retrieve_response.py
@@ -0,0 +1,109 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .shared.api_meta import APIMeta
+
+__all__ = ["UsageRetrieveResponse", "Data", "DataBilling", "DataBillingAutoRecharge", "DataRateLimit", "DataSendLimit"]
+
+
+class DataBillingAutoRecharge(BaseModel):
+ """Auto-recharge configuration"""
+
+ amount: str
+ """Amount to recharge when triggered"""
+
+ enabled: bool
+ """Whether auto-recharge is enabled"""
+
+ threshold: str
+ """Balance threshold that triggers recharge"""
+
+
+class DataBilling(BaseModel):
+ """Billing and credit information"""
+
+ auto_recharge: DataBillingAutoRecharge = FieldInfo(alias="autoRecharge")
+ """Auto-recharge configuration"""
+
+ credit_balance: str = FieldInfo(alias="creditBalance")
+ """Current credit balance as formatted string (e.g., "25.50")"""
+
+ credit_balance_cents: int = FieldInfo(alias="creditBalanceCents")
+ """Current credit balance in cents for precise calculations"""
+
+ has_payment_method: bool = FieldInfo(alias="hasPaymentMethod")
+ """Whether a payment method is configured"""
+
+
+class DataRateLimit(BaseModel):
+ """API rate limit status"""
+
+ limit: int
+ """Maximum requests allowed per period"""
+
+ period: Literal["second"]
+ """Time period for the limit"""
+
+ remaining: int
+ """Requests remaining in current window"""
+
+ reset: int
+ """Unix timestamp when the limit resets"""
+
+
+class DataSendLimit(BaseModel):
+ """Email send limit status (hourly cap)"""
+
+ approaching: bool
+ """Whether approaching the limit (>90%)"""
+
+ exceeded: bool
+ """Whether the limit has been exceeded"""
+
+ limit: Optional[int] = None
+ """Maximum emails allowed per hour (null = unlimited)"""
+
+ period: Literal["hour"]
+ """Time period for the limit"""
+
+ remaining: Optional[int] = None
+ """Emails remaining in current period (null if unlimited)"""
+
+ resets_at: datetime = FieldInfo(alias="resetsAt")
+ """ISO timestamp when the limit window resets (top of next hour)"""
+
+ usage_percent: Optional[float] = FieldInfo(alias="usagePercent", default=None)
+ """Usage as a percentage (null if unlimited)"""
+
+ used: int
+ """Emails sent in current period"""
+
+
+class Data(BaseModel):
+ """Current usage and limit information"""
+
+ billing: Optional[DataBilling] = None
+ """Billing and credit information"""
+
+ rate_limit: DataRateLimit = FieldInfo(alias="rateLimit")
+ """API rate limit status"""
+
+ send_limit: Optional[DataSendLimit] = FieldInfo(alias="sendLimit", default=None)
+ """Email send limit status (hourly cap)"""
+
+
+class UsageRetrieveResponse(BaseModel):
+ """Account usage and limits response"""
+
+ data: Data
+ """Current usage and limit information"""
+
+ meta: APIMeta
+
+ success: Literal[True]
diff --git a/src/ark/types/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_usage.py b/tests/api_resources/test_usage.py
new file mode 100644
index 0000000..767da80
--- /dev/null
+++ b/tests/api_resources/test_usage.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from ark import Ark, AsyncArk
+from ark.types import UsageRetrieveResponse
+from tests.utils import assert_matches_type
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestUsage:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: Ark) -> None:
+ usage = client.usage.retrieve()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Ark) -> None:
+ response = client.usage.with_raw_response.retrieve()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Ark) -> None:
+ with client.usage.with_streaming_response.retrieve() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncUsage:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncArk) -> None:
+ usage = await async_client.usage.retrieve()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncArk) -> None:
+ response = await async_client.usage.with_raw_response.retrieve()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ usage = await response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncArk) -> None:
+ async with async_client.usage.with_streaming_response.retrieve() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ usage = await response.parse()
+ assert_matches_type(UsageRetrieveResponse, usage, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
From 526997e2d34a19c345112ab062f55cf7adbde27e Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 29 Jan 2026 13:54:36 +0000
Subject: [PATCH 9/9] release: 0.14.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 15 +++++++++++++++
pyproject.toml | 2 +-
src/ark/_version.py | 2 +-
4 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index d52d2b9..a26ebfc 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.13.0"
+ ".": "0.14.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f186509..aabac85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## 0.14.0 (2026-01-29)
+
+Full Changelog: [v0.13.0...v0.14.0](https://github.com/ArkHQ-io/ark-python/compare/v0.13.0...v0.14.0)
+
+### Features
+
+* **api:** add usage and SendLimit Headers ([4281980](https://github.com/ArkHQ-io/ark-python/commit/4281980353ff7423dbc6627e37abefee952bc489))
+* **api:** api update ([10f496a](https://github.com/ArkHQ-io/ark-python/commit/10f496a29924ecd9bcc93d0551ea8bd29662aa89))
+* **api:** domain list improvement ([6929689](https://github.com/ArkHQ-io/ark-python/commit/69296893c13ad8e4f05047f312009e88a8f02830))
+
+
+### Bug Fixes
+
+* **docs:** fix mcp installation instructions for remote servers ([f559107](https://github.com/ArkHQ-io/ark-python/commit/f55910772f06f551b98a1ddc7c7dda45fc2176f4))
+
## 0.13.0 (2026-01-25)
Full Changelog: [v0.12.0...v0.13.0](https://github.com/ArkHQ-io/ark-python/compare/v0.12.0...v0.13.0)
diff --git a/pyproject.toml b/pyproject.toml
index fb937bb..0f30e15 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "ark-email"
-version = "0.13.0"
+version = "0.14.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 8997200..9e3345c 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.13.0" # x-release-please-version
+__version__ = "0.14.0" # x-release-please-version