Skip to content
Merged
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
143 changes: 143 additions & 0 deletions examples/receiving_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import os

import resend

# Type imports
from resend import AttachmentsReceiving, EmailsReceiving

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

# Retrieve a single received email by ID
email_id = "006e2796-ff6a-4436-91ad-0429e600bf8a"

received_email: resend.ReceivedEmail = resend.Emails.Receiving.get(email_id=email_id)

print(f"\nRetrieved received email: {received_email['id']}")
print("\n--- Email Details ---")
print(f"From: {received_email['from']}")
print(f"To: {received_email['to']}")
print(f"Subject: {received_email['subject']}")
print(f"Created at: {received_email['created_at']}")
print(f"Object type: {received_email['object']}")

print("\n--- Email Content ---")
html_content = received_email.get("html")
if html_content:
print(f"HTML: {html_content[:100]}...")
else:
print("HTML: None")

text_content = received_email.get("text")
if text_content:
print(f"Text: {text_content[:100]}...")
else:
print("Text: None")

print("\n--- Recipients ---")
print(f"CC: {received_email.get('cc', [])}")
print(f"BCC: {received_email.get('bcc', [])}")
print(f"Reply-To: {received_email.get('reply_to', [])}")

print("\n--- Headers ---")
if received_email.get("headers"):
for header_name, header_value in received_email["headers"].items():
print(f"{header_name}: {header_value}")
else:
print("No custom headers")

print("\n--- Attachments ---")
if received_email["attachments"]:
print(f"Total attachments: {len(received_email['attachments'])}")
for idx, attachment in enumerate(received_email["attachments"], 1):
print(f"\nAttachment {idx}:")
print(f" ID: {attachment['id']}")
print(f" Filename: {attachment['filename']}")
print(f" Content Type: {attachment['content_type']}")
print(f" Content Disposition: {attachment['content_disposition']}")
if attachment.get("content_id"):
print(f" Content ID: {attachment['content_id']}")
if attachment.get("size"):
print(f" Size: {attachment['size']} bytes")
else:
print("No attachments")

print("\n--- Listing All Received Emails ---")
all_emails: EmailsReceiving.ListResponse = resend.Emails.Receiving.list()

print(f"Total emails in this batch: {len(all_emails['data'])}")
print(f"Has more emails: {all_emails['has_more']}")

if all_emails["data"]:
for idx, email in enumerate(all_emails["data"], 1):
print(f"\nEmail {idx}:")
print(f" ID: {email['id']}")
print(f" From: {email['from']}")
print(f" To: {email['to']}")
print(f" Subject: {email['subject']}")
print(f" Created: {email['created_at']}")
print(f" Attachments: {len(email['attachments'])}")

print("\n--- Listing Received Emails with Pagination ---")
list_params: EmailsReceiving.ListParams = {
"limit": 5,
}
paginated_emails: EmailsReceiving.ListResponse = resend.Emails.Receiving.list(
params=list_params
)

print(f"Retrieved {len(paginated_emails['data'])} emails (limited to 5)")
print(f"Has more: {paginated_emails['has_more']}")

if paginated_emails["data"] and paginated_emails["has_more"]:
last_email_id = paginated_emails["data"][-1]["id"]
print(f"\n--- Getting Next Page (after {last_email_id}) ---")
next_page_params: EmailsReceiving.ListParams = {
"limit": 5,
"after": last_email_id,
}
next_page: EmailsReceiving.ListResponse = resend.Emails.Receiving.list(
params=next_page_params
)
print(f"Next page has {len(next_page['data'])} emails")
print(f"Next page has more: {next_page['has_more']}")

print("\n--- Listing All Attachments ---")
all_attachments: AttachmentsReceiving.ListResponse = resend.Attachments.Receiving.list(
email_id=email_id
)

print(f"Total attachments: {len(all_attachments['data'])}")
print(f"Has more: {all_attachments['has_more']}")

if all_attachments["data"]:
for idx, att in enumerate(all_attachments["data"], 1):
print(f"\nAttachment {idx}:")
print(f" ID: {att['id']}")
print(f" Filename: {att['filename']}")
print(f" Content Type: {att['content_type']}")
print(f" Size: {att.get('size', 'N/A')} bytes")

if received_email["attachments"] and len(received_email["attachments"]) > 0:
first_attachment = received_email["attachments"][0]
attachment_id = first_attachment["id"]

print(f"\n--- Retrieving Attachment Details: {first_attachment['filename']} ---")

attachment_details: resend.ReceivedEmailAttachmentDetails = (
resend.Attachments.Receiving.get(
email_id=email_id,
attachment_id=attachment_id,
)
)

print(f"Attachment ID: {attachment_details['id']}")
print(f"Filename: {attachment_details['filename']}")
print(f"Content Type: {attachment_details['content_type']}")
print(f"Content Disposition: {attachment_details['content_disposition']}")
if attachment_details.get("content_id"):
print(f" Content ID: {attachment_details['content_id']}")
print(f"Download URL: {attachment_details['download_url']}")
print(f"Expires At: {attachment_details['expires_at']}")
else:
print("\nNo attachments available to retrieve in this example.")
14 changes: 14 additions & 0 deletions resend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from .api_keys._api_key import ApiKey
from .api_keys._api_keys import ApiKeys
from .attachments._attachments import Attachments
from .attachments._receiving import Receiving as AttachmentsReceiving
from .audiences._audience import Audience
from .audiences._audiences import Audiences
from .broadcasts._broadcast import Broadcast
Expand All @@ -14,6 +16,10 @@
from .emails._batch import Batch, BatchValidationError
from .emails._email import Email
from .emails._emails import Emails
from .emails._received_email import (ListReceivedEmail, ReceivedEmail,
ReceivedEmailAttachment,
ReceivedEmailAttachmentDetails)
from .emails._receiving import Receiving as EmailsReceiving
from .emails._tag import Tag
from .http_client import HTTPClient
from .http_client_requests import RequestsClient
Expand Down Expand Up @@ -43,6 +49,7 @@
"Audiences",
"Contacts",
"Broadcasts",
"Attachments",
"Topics",
# Types
"Audience",
Expand All @@ -56,6 +63,13 @@
"Broadcast",
"Topic",
"BatchValidationError",
"ReceivedEmail",
"ReceivedEmailAttachment",
"ReceivedEmailAttachmentDetails",
"ListReceivedEmail",
# Receiving types (for type hints)
"EmailsReceiving",
"AttachmentsReceiving",
# Default HTTP Client
"RequestsClient",
]
Empty file added resend/attachments/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions resend/attachments/_attachments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from resend.attachments._receiving import Receiving


class Attachments:
"""
Attachments class that provides methods for managing email attachments.
"""

Receiving = Receiving
115 changes: 115 additions & 0 deletions resend/attachments/_receiving.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from typing import Any, Dict, List, Optional, cast

from typing_extensions import NotRequired, TypedDict

from resend import request
from resend.emails._received_email import (ReceivedEmailAttachment,
ReceivedEmailAttachmentDetails)
from resend.pagination_helper import PaginationHelper


# Internal wrapper type for get attachment API response
class _GetAttachmentResponse(TypedDict):
object: str
data: ReceivedEmailAttachmentDetails


class _ListParams(TypedDict):
limit: NotRequired[int]
"""
The maximum number of attachments to return. Maximum 100, minimum 1.
"""
after: NotRequired[str]
"""
Return attachments after this cursor for pagination.
"""
before: NotRequired[str]
"""
Return attachments before this cursor for pagination.
"""


class _ListResponse(TypedDict):
object: str
"""
The object type: "list"
"""
data: List[ReceivedEmailAttachment]
"""
The list of attachment objects.
"""
has_more: bool
"""
Whether there are more attachments available for pagination.
"""


class Receiving:
"""
Receiving class that provides methods for retrieving attachments from received emails.
"""

class ListParams(_ListParams):
"""
ListParams is the class that wraps the parameters for the list method.

Attributes:
limit (NotRequired[int]): The maximum number of attachments to return. Maximum 100, minimum 1.
after (NotRequired[str]): Return attachments after this cursor for pagination.
before (NotRequired[str]): Return attachments before this cursor for pagination.
"""

class ListResponse(_ListResponse):
"""
ListResponse is the type that wraps the response for listing attachments.

Attributes:
object (str): The object type: "list"
data (List[ReceivedEmailAttachment]): The list of attachment objects.
has_more (bool): Whether there are more attachments available for pagination.
"""

@classmethod
def get(cls, email_id: str, attachment_id: str) -> ReceivedEmailAttachmentDetails:
"""
Retrieve a single attachment from a received email.
see more: https://resend.com/docs/api-reference/attachments/retrieve-attachment

Args:
email_id (str): The ID of the received email
attachment_id (str): The ID of the attachment to retrieve

Returns:
ReceivedEmailAttachmentDetails: The attachment details including download URL
"""
path = f"/emails/receiving/{email_id}/attachments/{attachment_id}"
resp = request.Request[_GetAttachmentResponse](
path=path,
params={},
verb="get",
).perform_with_content()
# Extract the data field from the wrapped response
return resp["data"]

@classmethod
def list(cls, email_id: str, params: Optional[ListParams] = None) -> ListResponse:
"""
Retrieve a list of attachments from a received email.
see more: https://resend.com/docs/api-reference/attachments/list-attachments

Args:
email_id (str): The ID of the received email
params (Optional[ListParams]): The list parameters for pagination

Returns:
ListResponse: A paginated list of attachment objects
"""
base_path = f"/emails/receiving/{email_id}/attachments"
query_params = cast(Dict[Any, Any], params) if params else None
path = PaginationHelper.build_paginated_path(base_path, query_params)
resp = request.Request[Receiving.ListResponse](
path=path,
params={},
verb="get",
).perform_with_content()
return resp
2 changes: 2 additions & 0 deletions resend/emails/_emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from resend import request
from resend.emails._attachment import Attachment, RemoteAttachment
from resend.emails._email import Email
from resend.emails._receiving import Receiving
from resend.emails._tag import Tag
from resend.pagination_helper import PaginationHelper

Expand Down Expand Up @@ -103,6 +104,7 @@ class _SendParamsDefault(_SendParamsFrom):


class Emails:
Receiving = Receiving

class CancelScheduledEmailResponse(_CancelScheduledEmailResponse):
"""
Expand Down
Loading
Loading