Skip to content

Commit 57d1129

Browse files
authored
feat: Impl templates API (#171)
1 parent 45e451e commit 57d1129

11 files changed

Lines changed: 1045 additions & 10 deletions

File tree

examples/receiving_email.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@
102102
print(f"Next page has more: {next_page['has_more']}")
103103

104104
print("\n--- Listing All Attachments ---")
105-
all_attachments: EmailsReceiving.Attachments.ListResponse = resend.Emails.Receiving.Attachments.list(
106-
email_id=email_id
105+
all_attachments: EmailsReceiving.Attachments.ListResponse = (
106+
resend.Emails.Receiving.Attachments.list(email_id=email_id)
107107
)
108108

109109
print(f"Total attachments: {len(all_attachments['data'])}")
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import os
2+
from typing import List
3+
4+
import resend
5+
6+
if not os.environ["RESEND_API_KEY"]:
7+
raise EnvironmentError("RESEND_API_KEY is missing")
8+
9+
print("Step 1: Creating template variables...")
10+
variables: List[resend.Variable] = [
11+
{
12+
"key": "NAME",
13+
"type": "string",
14+
"fallback_value": "user",
15+
},
16+
{
17+
"key": "AGE",
18+
"type": "number",
19+
"fallback_value": 25,
20+
},
21+
]
22+
23+
print("Step 2: Creating a new template...")
24+
create_params: resend.Templates.CreateParams = {
25+
"name": "user-welcome-template",
26+
"subject": "Welcome to our platform!",
27+
"html": """
28+
<html>
29+
<body>
30+
<h1>Welcome, {{{NAME}}}!</h1>
31+
<p>Thank you for joining us. Your age is {{{AGE}}}.</p>
32+
</body>
33+
</html>
34+
""",
35+
"variables": variables,
36+
}
37+
38+
template: resend.Templates.CreateResponse = resend.Templates.create(create_params)
39+
print(f"Created template: {template['id']}")
40+
41+
print("Publishing the template...")
42+
published: resend.Templates.PublishResponse = resend.Templates.publish(template["id"])
43+
print(f"Published template: {published['id']}")
44+
45+
# Create the template configuration with variables
46+
email_template: resend.EmailTemplate = {
47+
"id": template["id"],
48+
"variables": {
49+
"NAME": "John Doe",
50+
"AGE": 30,
51+
},
52+
}
53+
54+
# Send the email with the template
55+
send_params: resend.Emails.SendParams = {
56+
"from": "onboarding@resend.dev",
57+
"to": ["delivered@resend.dev"],
58+
"template": email_template,
59+
}
60+
61+
email: resend.Emails.SendResponse = resend.Emails.send(send_params)
62+
print(f"✓ Sent email using template: {email['id']}")
63+
64+
sent_email: resend.Email = resend.Emails.get(email["id"])
65+
print(f"✓ Retrieved email: {sent_email['id']}")
66+
print(f" From: {sent_email['from']}")
67+
print(f" To: {sent_email['to']}")
68+
print(f" Subject: {sent_email.get('subject', 'N/A')}")
69+
70+
print("Cleaning up - removing the template...")
71+
removed: resend.Templates.RemoveResponse = resend.Templates.remove(template["id"])
72+
print(f"Deleted template: {removed['id']}, deleted={removed['deleted']}")

examples/templates.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""Example usage of the Templates API."""
2+
3+
import os
4+
from typing import List
5+
6+
import resend
7+
8+
if not os.environ["RESEND_API_KEY"]:
9+
raise EnvironmentError("RESEND_API_KEY is missing")
10+
11+
# Define template variables with explicit typing
12+
variables: List[resend.Variable] = [
13+
{
14+
"key": "NAME",
15+
"type": "string",
16+
"fallback_value": "user",
17+
},
18+
{
19+
"key": "AGE",
20+
"type": "number",
21+
"fallback_value": 25,
22+
},
23+
]
24+
25+
# Create a new template with variables
26+
create_params: resend.Templates.CreateParams = {
27+
"name": "welcome-email",
28+
"html": "<strong>Hey, {{{NAME}}}, you are {{{AGE}}} years old.</strong>",
29+
"variables": variables,
30+
}
31+
32+
template: resend.Templates.CreateResponse = resend.Templates.create(create_params)
33+
print(f"Created template: {template['id']}")
34+
35+
# Publish the template to make it available for use
36+
published: resend.Templates.PublishResponse = resend.Templates.publish(template["id"])
37+
print(f"Published template: {published['id']}")
38+
39+
# Duplicate a template
40+
duplicated: resend.Templates.DuplicateResponse = resend.Templates.duplicate(
41+
template["id"]
42+
)
43+
print(f"Duplicated template: {duplicated['id']}")
44+
45+
# Get a template by ID
46+
retrieved_template: resend.Template = resend.Templates.get(template["id"])
47+
print(f"Retrieved template: {retrieved_template['name']}")
48+
49+
# Update a template (update just the name, keep HTML/variables the same)
50+
update_params: resend.Templates.UpdateParams = {
51+
"id": template["id"],
52+
"name": "updated-welcome-email",
53+
}
54+
updated: resend.Templates.UpdateResponse = resend.Templates.update(update_params)
55+
print(f"Updated template: {updated['id']}")
56+
57+
# List all templates with pagination
58+
list_params: resend.Templates.ListParams = {
59+
"limit": 10,
60+
}
61+
templates: resend.Templates.ListResponse = resend.Templates.list(list_params)
62+
print(f"Total templates: {len(templates['data'])}")
63+
for t in templates["data"]:
64+
print(f" - {t['name']} ({t['id']})")
65+
66+
# Delete templates (cleanup)
67+
removed: resend.Templates.RemoveResponse = resend.Templates.remove(template["id"])
68+
print(f"Deleted template: {removed['id']}, deleted={removed['deleted']}")
69+
70+
removed_dup: resend.Templates.RemoveResponse = resend.Templates.remove(duplicated["id"])
71+
print(
72+
f"Deleted duplicated template: {removed_dup['id']}, deleted={removed_dup['deleted']}"
73+
)

examples/with_attachments.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import os
22

33
import resend
4+
# Type imports
5+
from resend import EmailAttachments
46

57
if not os.environ["RESEND_API_KEY"]:
68
raise EnvironmentError("RESEND_API_KEY is missing")
@@ -30,3 +32,38 @@
3032
email: resend.Emails.SendResponse = resend.Emails.send(params)
3133
print("Sent email with attachment")
3234
print(email)
35+
36+
print("\n--- Retrieving Attachments ---")
37+
38+
# List all attachments from the sent email
39+
attachments = resend.Emails.Attachments.list(email_id=email["id"])
40+
print(f"Email has {len(attachments['data'])} attachment(s)")
41+
print(f"Has more attachments: {attachments['has_more']}")
42+
43+
# Get details of each attachment
44+
if attachments["data"]:
45+
for i, attachment_item in enumerate(attachments["data"], 1):
46+
print(f"\nAttachment {i}: {attachment_item['filename']}")
47+
print(f" - ID: {attachment_item['id']}")
48+
print(f" - Content Type: {attachment_item['content_type']}")
49+
print(f" - Size: {attachment_item['size']} bytes")
50+
print(
51+
f" - Content Disposition: {attachment_item.get('content_disposition', 'N/A')}"
52+
)
53+
54+
# Get detailed information about this attachment (including download URL)
55+
attachment_details = resend.Emails.Attachments.get(
56+
email_id=email["id"], attachment_id=attachment_item["id"]
57+
)
58+
print(f" - Download URL: {attachment_details['download_url']}")
59+
print(f" - Expires at: {attachment_details['expires_at']}")
60+
61+
# Example with pagination for attachments (useful when emails have many attachments)
62+
print("\n--- Paginating Attachments ---")
63+
attachment_params: EmailAttachments.ListParams = {
64+
"limit": 10,
65+
}
66+
paginated_attachments = resend.Emails.Attachments.list(
67+
email_id=email["id"], params=attachment_params
68+
)
69+
print(f"Retrieved {len(paginated_attachments['data'])} attachment(s) (limited to 10)")

resend/__init__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
from .emails._attachments import Attachments as EmailAttachments
1515
from .emails._batch import Batch, BatchValidationError
1616
from .emails._email import Email
17-
from .emails._emails import Emails
17+
from .emails._emails import Emails, EmailTemplate
1818
from .emails._received_email import (EmailAttachment, EmailAttachmentDetails,
1919
ListReceivedEmail, ReceivedEmail)
2020
from .emails._receiving import Receiving as EmailsReceiving
2121
from .emails._tag import Tag
2222
from .http_client import HTTPClient
2323
from .http_client_requests import RequestsClient
2424
from .request import Request
25+
from .templates._template import Template, TemplateListItem, Variable
26+
from .templates._templates import Templates
2527
from .topics._topic import Topic
2628
from .topics._topics import Topics
2729
from .version import __version__, get_version
@@ -36,9 +38,6 @@
3638
# HTTP Client
3739
default_http_client: HTTPClient = RequestsClient()
3840

39-
# API resources
40-
from .emails._emails import Emails # noqa
41-
4241
__all__ = [
4342
"__version__",
4443
"get_version",
@@ -50,6 +49,7 @@
5049
"Audiences",
5150
"Contacts",
5251
"Broadcasts",
52+
"Templates",
5353
"Webhooks",
5454
"Topics",
5555
# Types
@@ -60,8 +60,12 @@
6060
"Email",
6161
"Attachment",
6262
"RemoteAttachment",
63+
"EmailTemplate",
6364
"Tag",
6465
"Broadcast",
66+
"Template",
67+
"TemplateListItem",
68+
"Variable",
6569
"Webhook",
6670
"WebhookEvent",
6771
"WebhookHeaders",

resend/emails/_emails.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@
1111
from resend.pagination_helper import PaginationHelper
1212

1313

14+
class EmailTemplate(TypedDict):
15+
"""
16+
EmailTemplate is the class that wraps template configuration for email sending.
17+
18+
Attributes:
19+
id (str): The template ID.
20+
variables (NotRequired[Dict[str, Union[str, int]]]): Optional variables to be used in the template.
21+
"""
22+
23+
id: str
24+
"""
25+
The template ID.
26+
"""
27+
variables: NotRequired[Dict[str, Union[str, int]]]
28+
"""
29+
Optional variables to be used in the template.
30+
"""
31+
32+
1433
class _UpdateParams(TypedDict):
1534
id: str
1635
"""
@@ -51,7 +70,7 @@ class _CancelScheduledEmailResponse(TypedDict):
5170
_SendParamsFrom = TypedDict(
5271
"_SendParamsFrom",
5372
{
54-
"from": str,
73+
"from": NotRequired[str],
5574
},
5675
)
5776

@@ -61,7 +80,7 @@ class _SendParamsDefault(_SendParamsFrom):
6180
"""
6281
List of email addresses to send the email to.
6382
"""
64-
subject: str
83+
subject: NotRequired[str]
6584
"""
6685
The subject of the email.
6786
"""
@@ -102,6 +121,10 @@ class _SendParamsDefault(_SendParamsFrom):
102121
Schedule email to be sent later.
103122
The date should be in ISO 8601 format (e.g: 2024-08-05T11:52:01.858Z).
104123
"""
124+
template: NotRequired[EmailTemplate]
125+
"""
126+
Template configuration for sending emails using predefined templates.
127+
"""
105128

106129

107130
class Emails:
@@ -140,9 +163,9 @@ class SendParams(_SendParamsDefault):
140163
"""SendParams is the class that wraps the parameters for the send method.
141164
142165
Attributes:
143-
from (str): The email address to send the email from.
166+
from (NotRequired[str]): The email address to send the email from.
144167
to (Union[str, List[str]]): List of email addresses to send the email to.
145-
subject (str): The subject of the email.
168+
subject (NotRequired[str]): The subject of the email.
146169
bcc (NotRequired[Union[List[str], str]]): Bcc
147170
cc (NotRequired[Union[List[str], str]]): Cc
148171
reply_to (NotRequired[Union[List[str], str]]): Reply to
@@ -151,6 +174,7 @@ class SendParams(_SendParamsDefault):
151174
headers (NotRequired[Dict[str, str]]): Custom headers to be added to the email.
152175
attachments (NotRequired[List[Union[Attachment, RemoteAttachment]]]): List of attachments to be added to the email.
153176
tags (NotRequired[List[Tag]]): List of tags to be added to the email.
177+
template (NotRequired[EmailTemplate]): Template configuration for sending emails using predefined templates.
154178
"""
155179

156180
class SendOptions(TypedDict):

resend/templates/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Templates module."""

0 commit comments

Comments
 (0)