Skip to content

Commit 54b8fa2

Browse files
authored
feat: Inline attachment support (#152)
1 parent 7b114dc commit 54b8fa2

6 files changed

Lines changed: 122 additions & 4 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import os
2+
3+
import resend
4+
5+
if not os.environ["RESEND_API_KEY"]:
6+
raise EnvironmentError("RESEND_API_KEY is missing")
7+
8+
f: bytes = open(
9+
os.path.join(os.path.dirname(__file__), "../resources/resend-wordmark-black.png"),
10+
"rb",
11+
).read()
12+
13+
# Send email with local inline attachment
14+
local_attachment: resend.Attachment = {
15+
"filename": "resend-wordmark-black.png",
16+
"content": list(f),
17+
"content_type": "image/png",
18+
# This is the content ID that will be used in the HTML to reference the image
19+
"inline_content_id": "my-test-image",
20+
}
21+
22+
local_params: resend.Emails.SendParams = {
23+
"from": "onboarding@resend.dev",
24+
"to": ["delivered@resend.dev"],
25+
"subject": "Local inline attachment test from Resend's python SDK",
26+
"html": '<p>This email contains a local inline attachment: <img width=100 height=40 src="cid:my-test-image" /></p>',
27+
"attachments": [local_attachment],
28+
}
29+
30+
local_email: resend.Email = resend.Emails.send(local_params)
31+
print("Sent email with local inline attachment")
32+
print(local_email)
33+
34+
# Send email with remote inline attachment
35+
remote_attachment: resend.RemoteAttachment = {
36+
"filename": "remote-resend-wordmark-black.png",
37+
"path": "https://resend.com/static/brand/resend-wordmark-black.png",
38+
"inline_content_id": "my-test-image",
39+
}
40+
41+
remote_params: resend.Emails.SendParams = {
42+
"from": "onboarding@resend.dev",
43+
"to": ["delivered@resend.dev"],
44+
"subject": "Remote inline attachment test from Resend's python SDK",
45+
"html": '<p>This email contains a remote inline attachment: <img width=100 height=40 src="cid:my-test-image" /></p>',
46+
"attachments": [remote_attachment],
47+
}
48+
49+
remote_email: resend.Email = resend.Emails.send(remote_params)
50+
print("Sent email with remote inline attachment")
51+
print(remote_email)

resend/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .contacts._contacts import Contacts
1111
from .domains._domain import Domain
1212
from .domains._domains import Domains
13-
from .emails._attachment import Attachment
13+
from .emails._attachment import Attachment, RemoteAttachment
1414
from .emails._batch import Batch
1515
from .emails._email import Email
1616
from .emails._emails import Emails
@@ -48,6 +48,7 @@
4848
"ApiKey",
4949
"Email",
5050
"Attachment",
51+
"RemoteAttachment",
5152
"Tag",
5253
"Broadcast",
5354
# Default HTTP Client

resend/emails/_attachment.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,22 @@ class Attachment(TypedDict):
2323
"""
2424
Content type for the attachment, if not set will be derived from the filename property
2525
"""
26+
inline_content_id: NotRequired[str]
27+
"""
28+
Content ID for inline attachments used in HTML content with cid: references
29+
"""
30+
31+
32+
class RemoteAttachment(TypedDict):
33+
path: str
34+
"""
35+
Path where the remote attachment file is hosted
36+
"""
37+
filename: NotRequired[str]
38+
"""
39+
Name of attached file.
40+
"""
41+
inline_content_id: NotRequired[str]
42+
"""
43+
Content ID for inline attachments used in HTML content with cid: references
44+
"""

resend/emails/_emails.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing_extensions import NotRequired, TypedDict
44

55
from resend import request
6-
from resend.emails._attachment import Attachment
6+
from resend.emails._attachment import Attachment, RemoteAttachment
77
from resend.emails._email import Email
88
from resend.emails._tag import Tag
99

@@ -95,7 +95,7 @@ class _SendParamsDefault(_SendParamsFrom):
9595
"""
9696
Custom headers to be added to the email.
9797
"""
98-
attachments: NotRequired[List[Attachment]]
98+
attachments: NotRequired[List[Union[Attachment, RemoteAttachment]]]
9999
"""
100100
List of attachments to be added to the email.
101101
"""
@@ -153,7 +153,7 @@ class SendParams(_SendParamsDefault):
153153
html (NotRequired[str]): The HTML content of the email.
154154
text (NotRequired[str]): The text content of the email.
155155
headers (NotRequired[Dict[str, str]]): Custom headers to be added to the email.
156-
attachments (NotRequired[List[Attachment]]): List of attachments to be added to the email.
156+
attachments (NotRequired[List[Union[Attachment, RemoteAttachment]]]): List of attachments to be added to the email.
157157
tags (NotRequired[List[Tag]]): List of tags to be added to the email.
158158
"""
159159

24.6 KB
Loading

tests/emails_test.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,50 @@ def test_cancel_scheduled_email(self) -> None:
109109
email_id="49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
110110
)
111111
assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
112+
113+
def test_email_send_with_attachment(self) -> None:
114+
self.set_mock_json(
115+
{
116+
"id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
117+
}
118+
)
119+
120+
attachment: resend.Attachment = {
121+
"filename": "test.pdf",
122+
"content": [1, 2, 3, 4, 5],
123+
"content_type": "application/pdf",
124+
}
125+
126+
params: resend.Emails.SendParams = {
127+
"to": "to@email.com",
128+
"from": "from@email.com",
129+
"subject": "subject",
130+
"html": "html",
131+
"attachments": [attachment],
132+
}
133+
email: resend.Email = resend.Emails.send(params)
134+
assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
135+
136+
def test_email_send_with_inline_attachment(self) -> None:
137+
self.set_mock_json(
138+
{
139+
"id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
140+
}
141+
)
142+
143+
attachment: resend.Attachment = {
144+
"filename": "image.png",
145+
"content": [1, 2, 3, 4, 5],
146+
"content_type": "image/png",
147+
"inline_content_id": "my-image",
148+
}
149+
150+
params: resend.Emails.SendParams = {
151+
"to": "to@email.com",
152+
"from": "from@email.com",
153+
"subject": "subject",
154+
"html": '<img src="cid:my-image" />',
155+
"attachments": [attachment],
156+
}
157+
email: resend.Email = resend.Emails.send(params)
158+
assert email["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"

0 commit comments

Comments
 (0)