Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions examples/domain_claims.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os

import resend

if not os.environ["RESEND_API_KEY"]:
raise EnvironmentError("RESEND_API_KEY is missing")


# Start a claim for a domain that another Resend account has already verified.
claim_params: resend.Domains.Claims.CreateParams = {
"name": "example.com",
"region": "us-east-1",
}
claim: resend.DomainClaim = resend.Domains.Claims.create(claim_params)
print(f"Created domain claim: {claim['id']}")
print(f"Status: {claim['status']}")
if claim.get("record"):
record = claim["record"]
print(
f"Add this TXT record to prove ownership: {record['name']} = {record['value']}"
)

# Get: poll the claim until the TXT record has been added and verification can run.
domain_id = claim["domain_id"]
if domain_id:
retrieved_claim: resend.DomainClaim = resend.Domains.Claims.get(domain_id=domain_id)
print(f"Retrieved domain claim status: {retrieved_claim['status']}")

# Verify: trigger asynchronous DNS verification and ownership transfer.
verified_claim: resend.DomainClaim = resend.Domains.Claims.verify(
domain_id=domain_id
)
print(f"Verification triggered, claim status: {verified_claim['status']}")
5 changes: 5 additions & 0 deletions resend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
from .contacts.segments._contact_segments import ContactSegments
from .domains._domain import Domain
from .domains._domains import Domains
from .domains.claims._domain_claim import DomainClaim, DomainClaimRecord
from .domains.claims._domain_claims import DomainClaims
from .emails._attachment import Attachment, RemoteAttachment
from .emails._attachments import Attachments as EmailAttachments
from .emails._batch import Batch, BatchValidationError
Expand Down Expand Up @@ -81,6 +83,7 @@
"Emails",
"ApiKeys",
"Domains",
"DomainClaims",
"Batch",
"Audiences",
"Automations",
Expand Down Expand Up @@ -121,6 +124,8 @@
"ContactTopic",
"TopicSubscriptionUpdate",
"Domain",
"DomainClaim",
"DomainClaimRecord",
"ApiKey",
"Log",
"Email",
Expand Down
4 changes: 4 additions & 0 deletions resend/domains/_domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from resend._base_response import BaseResponse
from resend.domains._domain import Domain
from resend.domains._record import Record
from resend.domains.claims._domain_claims import DomainClaims
from resend.pagination_helper import PaginationHelper

# Async imports (optional - only available with pip install resend[async])
Expand All @@ -19,6 +20,9 @@

class Domains:

class Claims(DomainClaims):
pass

class ListParams(TypedDict):
limit: NotRequired[int]
"""
Expand Down
4 changes: 4 additions & 0 deletions resend/domains/claims/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from ._domain_claim import DomainClaim, DomainClaimRecord
from ._domain_claims import DomainClaims

__all__ = ["DomainClaims", "DomainClaim", "DomainClaimRecord"]
70 changes: 70 additions & 0 deletions resend/domains/claims/_domain_claim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from typing import Optional

from typing_extensions import Literal, TypedDict

from resend._base_response import BaseResponse

DomainClaimStatus = Literal[
"pending",
"verified",
"completed",
"blocked",
"expired",
"superseded",
"canceled",
"failed",
]

DomainClaimBlockedReason = Literal[
"grace_period",
"recent_owner_activity",
"pending_scheduled_emails",
]


class DomainClaimRecord(TypedDict):
"""
DomainClaimRecord is the TXT record to add to your DNS to prove ownership of the claimed domain.

Attributes:
type (str): The DNS record type. Always 'TXT' for domain claims.
name (str): The name of the DNS record (the domain being claimed).
value (str): The value of the TXT record.
ttl (str): The time to live for the record.
"""

type: str
name: str
value: str
ttl: str


class DomainClaim(BaseResponse):
"""
DomainClaim represents a claim for a domain that another Resend account has already verified.

Attributes:
object (str): Always 'domain_claim'.
id (str): The ID of the claim.
name (str): The name of the domain being claimed.
status (DomainClaimStatus): The status of the claim.
domain_id (Optional[str]): The ID of the placeholder domain created for the claim.
region (Optional[str]): The region where the claimed domain will send from.
record (DomainClaimRecord): The TXT record to add to your DNS to prove ownership.
blocked_reason (Optional[DomainClaimBlockedReason]): Why the claim is currently blocked, if applicable.
failure_reason (Optional[str]): Why the claim failed, if applicable.
created_at (str): The date and time the claim was created.
expires_at (str): The date and time the claim expires if not verified.
"""

object: str
id: str
name: str
status: DomainClaimStatus
domain_id: Optional[str]
region: Optional[str]
record: DomainClaimRecord
blocked_reason: Optional[DomainClaimBlockedReason]
failure_reason: Optional[str]
created_at: str
expires_at: str
162 changes: 162 additions & 0 deletions resend/domains/claims/_domain_claims.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
from typing import Any, Dict, cast

from typing_extensions import NotRequired, TypedDict

from resend import request

from ._domain_claim import DomainClaim

# Async imports (optional - only available with pip install resend[async])
try:
from resend.async_request import AsyncRequest
except ImportError:
pass


class DomainClaims:
class CreateParams(TypedDict):
name: str
"""
The name of the domain you want to claim.
"""
region: NotRequired[str]
"""
The region where emails will be sent from.
Possible values: 'us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1'
"""
custom_return_path: NotRequired[str]
"""
By default, Resend will use the `send` subdomain for the Return-Path address.
You can change this by setting the optional `custom_return_path` parameter.
"""
tracking_subdomain: NotRequired[str]
"""
The custom subdomain used for click and open tracking links (e.g., "links").
"""
click_tracking: NotRequired[bool]
"""
Track clicks within the body of each HTML email.
"""
open_tracking: NotRequired[bool]
"""
Track the open rate of each email.
"""

@classmethod
def create(cls, params: CreateParams) -> DomainClaim:
"""
Start a claim for a domain that another Resend account has already verified.
The domain is recreated under your account with fresh DKIM keys, so the
previous account's DNS records cannot be reused.
see more: https://resend.com/docs/api-reference/domains/claim-domain

Args:
params (CreateParams): The domain claim parameters

Returns:
DomainClaim: The created domain claim, or an identical pending claim
if one already existed
"""
path = "/domains/claim"
resp = request.Request[DomainClaim](
path=path, params=cast(Dict[Any, Any], params), verb="post"
).perform_with_content()
return resp

@classmethod
def get(cls, domain_id: str) -> DomainClaim:
"""
Retrieve the latest claim for the placeholder domain created by the claim.
see more: https://resend.com/docs/api-reference/domains/get-domain-claim

Args:
domain_id (str): The ID of the placeholder domain created by the claim

Returns:
DomainClaim: The domain claim object
"""
path = f"/domains/{domain_id}/claim"
resp = request.Request[DomainClaim](
path=path, params={}, verb="get"
).perform_with_content()
return resp

@classmethod
def verify(cls, domain_id: str) -> DomainClaim:
"""
Trigger asynchronous DNS verification and ownership transfer for a domain
claim. The claim stays 'pending' while verification runs; poll `get` for
status.
see more: https://resend.com/docs/api-reference/domains/verify-domain-claim

Args:
domain_id (str): The ID of the placeholder domain created by the claim

Returns:
DomainClaim: The domain claim object
"""
path = f"/domains/{domain_id}/claim/verify"
resp = request.Request[DomainClaim](
path=path, params={}, verb="post"
).perform_with_content()
return resp

@classmethod
async def create_async(cls, params: CreateParams) -> DomainClaim:
"""
Start a claim for a domain that another Resend account has already verified
(async). The domain is recreated under your account with fresh DKIM keys, so
the previous account's DNS records cannot be reused.
see more: https://resend.com/docs/api-reference/domains/claim-domain

Args:
params (CreateParams): The domain claim parameters

Returns:
DomainClaim: The created domain claim, or an identical pending claim
if one already existed
"""
path = "/domains/claim"
resp = await AsyncRequest[DomainClaim](
path=path, params=cast(Dict[Any, Any], params), verb="post"
).perform_with_content()
return resp

@classmethod
async def get_async(cls, domain_id: str) -> DomainClaim:
"""
Retrieve the latest claim for the placeholder domain created by the claim
(async).
see more: https://resend.com/docs/api-reference/domains/get-domain-claim

Args:
domain_id (str): The ID of the placeholder domain created by the claim

Returns:
DomainClaim: The domain claim object
"""
path = f"/domains/{domain_id}/claim"
resp = await AsyncRequest[DomainClaim](
path=path, params={}, verb="get"
).perform_with_content()
return resp

@classmethod
async def verify_async(cls, domain_id: str) -> DomainClaim:
"""
Trigger asynchronous DNS verification and ownership transfer for a domain
claim (async). The claim stays 'pending' while verification runs; poll
`get_async` for status.
see more: https://resend.com/docs/api-reference/domains/verify-domain-claim

Args:
domain_id (str): The ID of the placeholder domain created by the claim

Returns:
DomainClaim: The domain claim object
"""
path = f"/domains/{domain_id}/claim/verify"
resp = await AsyncRequest[DomainClaim](
path=path, params={}, verb="post"
).perform_with_content()
return resp
Loading
Loading