From ea33238e8a8fe28b7d9f22e0a2d14327c051f15e Mon Sep 17 00:00:00 2001 From: Derich Pacheco Date: Sun, 3 Aug 2025 22:35:44 -0300 Subject: [PATCH 1/2] feat: inline attachment support --- examples/with_inline_attachments.py | 33 +++++++++++++++++++ resend/emails/_attachment.py | 4 +++ resources/resend-wordmark-black.png | Bin 0 -> 25217 bytes tests/emails_test.py | 47 ++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 examples/with_inline_attachments.py create mode 100644 resources/resend-wordmark-black.png diff --git a/examples/with_inline_attachments.py b/examples/with_inline_attachments.py new file mode 100644 index 0000000..90b141b --- /dev/null +++ b/examples/with_inline_attachments.py @@ -0,0 +1,33 @@ +import os + +import resend + +if not os.environ["RESEND_API_KEY"]: + raise EnvironmentError("RESEND_API_KEY is missing") + +f: bytes = open( + os.path.join(os.path.dirname(__file__), "../resources/resend-wordmark-black.png"), + "rb", +).read() + +# Define the file attachment +attachment: resend.Attachment = { + "filename": "resend-wordmark-black.png", + "content": list(f), + "content_type": "image/png", + # This is the content ID that will be used in the HTML to reference the image + "inline_content_id": "my-test-image", +} + +# Define the email parameters +params: resend.Emails.SendParams = { + "from": "onboarding@resend.dev", + "to": ["delivered@resend.dev"], + "subject": "Inline attachment test from Resend's python SDK", + "html": '

This is an email with an embed image

', + "attachments": [attachment], +} + +email: resend.Email = resend.Emails.send(params) +print("Sent email with attachment") +print(email) diff --git a/resend/emails/_attachment.py b/resend/emails/_attachment.py index 7e84eee..4a155cb 100644 --- a/resend/emails/_attachment.py +++ b/resend/emails/_attachment.py @@ -23,3 +23,7 @@ class Attachment(TypedDict): """ Content type for the attachment, if not set will be derived from the filename property """ + inline_content_id: NotRequired[str] + """ + Content ID for inline attachments used in HTML content with cid: references + """ diff --git a/resources/resend-wordmark-black.png b/resources/resend-wordmark-black.png new file mode 100644 index 0000000000000000000000000000000000000000..b80f9aba17590613895054103319c3fdcc08e5e6 GIT binary patch literal 25217 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 literal 0 HcmV?d00001 diff --git a/tests/emails_test.py b/tests/emails_test.py index c7d8ada..00cfac4 100644 --- a/tests/emails_test.py +++ b/tests/emails_test.py @@ -109,3 +109,50 @@ def test_cancel_scheduled_email(self) -> None: email_id="49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" ) assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" + + def test_email_send_with_attachment(self) -> None: + self.set_mock_json( + { + "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", + } + ) + + attachment: resend.Attachment = { + "filename": "test.pdf", + "content": [1, 2, 3, 4, 5], + "content_type": "application/pdf", + } + + params: resend.Emails.SendParams = { + "to": "to@email.com", + "from": "from@email.com", + "subject": "subject", + "html": "html", + "attachments": [attachment], + } + email: resend.Email = resend.Emails.send(params) + assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" + + def test_email_send_with_inline_attachment(self) -> None: + self.set_mock_json( + { + "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", + } + ) + + attachment: resend.Attachment = { + "filename": "image.png", + "content": [1, 2, 3, 4, 5], + "content_type": "image/png", + "inline_content_id": "my-image", + } + + params: resend.Emails.SendParams = { + "to": "to@email.com", + "from": "from@email.com", + "subject": "subject", + "html": '', + "attachments": [attachment], + } + email: resend.Email = resend.Emails.send(params) + assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" From 2335a83ce2671034cbeeabe699ededf37bdefad6 Mon Sep 17 00:00:00 2001 From: Derich Pacheco Date: Sun, 3 Aug 2025 23:39:05 -0300 Subject: [PATCH 2/2] feat: support for remote path --- examples/with_inline_attachments.py | 38 +++++++++++++++++++++-------- resend/__init__.py | 3 ++- resend/emails/_attachment.py | 15 ++++++++++++ resend/emails/_emails.py | 6 ++--- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/examples/with_inline_attachments.py b/examples/with_inline_attachments.py index 90b141b..fc0c492 100644 --- a/examples/with_inline_attachments.py +++ b/examples/with_inline_attachments.py @@ -10,8 +10,8 @@ "rb", ).read() -# Define the file attachment -attachment: resend.Attachment = { +# Send email with local inline attachment +local_attachment: resend.Attachment = { "filename": "resend-wordmark-black.png", "content": list(f), "content_type": "image/png", @@ -19,15 +19,33 @@ "inline_content_id": "my-test-image", } -# Define the email parameters -params: resend.Emails.SendParams = { +local_params: resend.Emails.SendParams = { "from": "onboarding@resend.dev", "to": ["delivered@resend.dev"], - "subject": "Inline attachment test from Resend's python SDK", - "html": '

This is an email with an embed image

', - "attachments": [attachment], + "subject": "Local inline attachment test from Resend's python SDK", + "html": '

This email contains a local inline attachment:

', + "attachments": [local_attachment], } -email: resend.Email = resend.Emails.send(params) -print("Sent email with attachment") -print(email) +local_email: resend.Email = resend.Emails.send(local_params) +print("Sent email with local inline attachment") +print(local_email) + +# Send email with remote inline attachment +remote_attachment: resend.RemoteAttachment = { + "filename": "remote-resend-wordmark-black.png", + "path": "https://resend.com/static/brand/resend-wordmark-black.png", + "inline_content_id": "my-test-image", +} + +remote_params: resend.Emails.SendParams = { + "from": "onboarding@resend.dev", + "to": ["delivered@resend.dev"], + "subject": "Remote inline attachment test from Resend's python SDK", + "html": '

This email contains a remote inline attachment:

', + "attachments": [remote_attachment], +} + +remote_email: resend.Email = resend.Emails.send(remote_params) +print("Sent email with remote inline attachment") +print(remote_email) diff --git a/resend/__init__.py b/resend/__init__.py index b1fcb7e..40694ef 100644 --- a/resend/__init__.py +++ b/resend/__init__.py @@ -10,7 +10,7 @@ from .contacts._contacts import Contacts from .domains._domain import Domain from .domains._domains import Domains -from .emails._attachment import Attachment +from .emails._attachment import Attachment, RemoteAttachment from .emails._batch import Batch from .emails._email import Email from .emails._emails import Emails @@ -48,6 +48,7 @@ "ApiKey", "Email", "Attachment", + "RemoteAttachment", "Tag", "Broadcast", # Default HTTP Client diff --git a/resend/emails/_attachment.py b/resend/emails/_attachment.py index 4a155cb..24376ce 100644 --- a/resend/emails/_attachment.py +++ b/resend/emails/_attachment.py @@ -27,3 +27,18 @@ class Attachment(TypedDict): """ Content ID for inline attachments used in HTML content with cid: references """ + + +class RemoteAttachment(TypedDict): + path: str + """ + Path where the remote attachment file is hosted + """ + filename: NotRequired[str] + """ + Name of attached file. + """ + inline_content_id: NotRequired[str] + """ + Content ID for inline attachments used in HTML content with cid: references + """ diff --git a/resend/emails/_emails.py b/resend/emails/_emails.py index b5dacc5..dfd2c54 100644 --- a/resend/emails/_emails.py +++ b/resend/emails/_emails.py @@ -3,7 +3,7 @@ from typing_extensions import NotRequired, TypedDict from resend import request -from resend.emails._attachment import Attachment +from resend.emails._attachment import Attachment, RemoteAttachment from resend.emails._email import Email from resend.emails._tag import Tag @@ -95,7 +95,7 @@ class _SendParamsDefault(_SendParamsFrom): """ Custom headers to be added to the email. """ - attachments: NotRequired[List[Attachment]] + attachments: NotRequired[List[Union[Attachment, RemoteAttachment]]] """ List of attachments to be added to the email. """ @@ -153,7 +153,7 @@ class SendParams(_SendParamsDefault): html (NotRequired[str]): The HTML content of the email. text (NotRequired[str]): The text content of the email. headers (NotRequired[Dict[str, str]]): Custom headers to be added to the email. - attachments (NotRequired[List[Attachment]]): List of attachments to be added to the email. + attachments (NotRequired[List[Union[Attachment, RemoteAttachment]]]): List of attachments to be added to the email. tags (NotRequired[List[Tag]]): List of tags to be added to the email. """