Skip to content

Commit 645ed28

Browse files
authored
feat: implement automations and events APIs (#197)
1 parent 8d1b648 commit 645ed28

20 files changed

Lines changed: 3007 additions & 17 deletions
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import asyncio
2+
import os
3+
4+
import resend
5+
6+
if not os.environ["RESEND_API_KEY"]:
7+
raise EnvironmentError("RESEND_API_KEY is missing")
8+
9+
10+
async def main() -> None:
11+
# Create and publish a template to use in the automation
12+
print("--- Create and publish template ---")
13+
tpl: resend.Templates.CreateResponse = await resend.Templates.create_async(
14+
{
15+
"name": "welcome-email",
16+
"subject": "Welcome!",
17+
"html": "<strong>Welcome to our service!</strong>",
18+
}
19+
)
20+
await resend.Templates.publish_async(tpl["id"])
21+
print(f"Template: {tpl['id']}")
22+
23+
# --- Create a simple automation (trigger → send_email) ---
24+
print("\n--- Create automation ---")
25+
simple: resend.Automations.CreateResponse = await resend.Automations.create_async(
26+
{
27+
"name": "Welcome Flow",
28+
"status": "disabled",
29+
"steps": [
30+
{
31+
"key": "trigger_1",
32+
"type": "trigger",
33+
"config": {"event_name": "user.created"},
34+
},
35+
{
36+
"key": "send_1",
37+
"type": "send_email",
38+
"config": {"template": {"id": tpl["id"]}},
39+
},
40+
],
41+
"connections": [
42+
{"from": "trigger_1", "to": "send_1"},
43+
],
44+
}
45+
)
46+
automation_id = simple["id"]
47+
print(f"Created automation: {automation_id}")
48+
49+
# --- Get automation ---
50+
print("\n--- Get automation ---")
51+
automation: resend.Automation = await resend.Automations.get_async(automation_id)
52+
print(f"Name: {automation['name']}, status: {automation['status']}")
53+
for step in automation["steps"]:
54+
print(f" Step key={step['key']} type={step['type']}")
55+
56+
# --- Update automation ---
57+
print("\n--- Update automation ---")
58+
updated: resend.Automations.UpdateResponse = await resend.Automations.update_async(
59+
{
60+
"automation_id": automation_id,
61+
"status": "enabled",
62+
}
63+
)
64+
print(f"Updated: {updated['id']}")
65+
66+
# --- List automations ---
67+
print("\n--- List automations ---")
68+
list_resp: resend.Automations.ListResponse = await resend.Automations.list_async()
69+
print(f"Total: {len(list_resp['data'])}, has_more: {list_resp['has_more']}")
70+
71+
# --- Stop automation ---
72+
print("\n--- Stop automation ---")
73+
stopped: resend.Automations.StopResponse = await resend.Automations.stop_async(
74+
automation_id
75+
)
76+
print(f"Stopped: {stopped['id']}, status: {stopped['status']}")
77+
78+
# --- List runs ---
79+
print("\n--- List runs ---")
80+
runs: resend.Automations.Runs.ListResponse = (
81+
await resend.Automations.Runs.list_async(automation_id)
82+
)
83+
print(f"Total runs: {len(runs['data'])}")
84+
if runs["data"]:
85+
run_id = runs["data"][0]["id"]
86+
run: resend.AutomationRun = await resend.Automations.Runs.get_async(
87+
automation_id, run_id
88+
)
89+
print(f"Run status: {run['status']}")
90+
91+
# --- Multi-step automation: delay + wait_for_event ---
92+
print("\n--- Create multi-step automation (delay + wait_for_event) ---")
93+
multi: resend.Automations.CreateResponse = await resend.Automations.create_async(
94+
{
95+
"name": "Onboarding Flow",
96+
"status": "disabled",
97+
"steps": [
98+
{
99+
"key": "trigger_1",
100+
"type": "trigger",
101+
"config": {"event_name": "user.created"},
102+
},
103+
{
104+
"key": "delay_1",
105+
"type": "delay",
106+
# duration is a human-readable string; "seconds" (int) is also accepted
107+
"config": {"duration": "30 minutes"},
108+
},
109+
{
110+
"key": "wait_1",
111+
"type": "wait_for_event",
112+
# timeout is a human-readable string; timeout_seconds is NOT supported
113+
"config": {"event_name": "user.verified", "timeout": "1 hour"},
114+
},
115+
{
116+
"key": "send_1",
117+
"type": "send_email",
118+
"config": {"template": {"id": tpl["id"]}},
119+
},
120+
],
121+
"connections": [
122+
{"from": "trigger_1", "to": "delay_1"},
123+
{"from": "delay_1", "to": "wait_1"},
124+
{"from": "wait_1", "to": "send_1", "type": "event_received"},
125+
{"from": "wait_1", "to": "send_1", "type": "timeout"},
126+
],
127+
}
128+
)
129+
multi_id = multi["id"]
130+
print(f"Created: {multi_id}")
131+
132+
retrieved: resend.Automation = await resend.Automations.get_async(multi_id)
133+
for step in retrieved["steps"]:
134+
print(f" Step key={step['key']} type={step['type']} config={step['config']}")
135+
136+
# --- Cleanup ---
137+
print("\n--- Cleanup ---")
138+
del1: resend.Automations.DeleteResponse = await resend.Automations.remove_async(
139+
automation_id
140+
)
141+
print(f"Deleted automation {del1['id']}: {del1['deleted']}")
142+
del2: resend.Automations.DeleteResponse = await resend.Automations.remove_async(
143+
multi_id
144+
)
145+
print(f"Deleted automation {del2['id']}: {del2['deleted']}")
146+
await resend.Templates.remove_async(tpl["id"])
147+
print(f"Deleted template {tpl['id']}")
148+
149+
150+
asyncio.run(main())

examples/async/events_async.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import asyncio
2+
import os
3+
4+
import resend
5+
6+
if not os.environ["RESEND_API_KEY"]:
7+
raise EnvironmentError("RESEND_API_KEY is missing")
8+
9+
10+
async def main() -> None:
11+
# Create a contact to use with event sends
12+
print("--- Create contact ---")
13+
contact: resend.Contacts.CreateContactResponse = await resend.Contacts.create_async(
14+
{
15+
"email": "test-events-async@example.com",
16+
"first_name": "Test",
17+
"last_name": "User",
18+
}
19+
)
20+
contact_id = contact["id"]
21+
print(f"Contact: {contact_id}")
22+
23+
# --- Create event ---
24+
print("\n--- Create event ---")
25+
created: resend.Events.CreateResponse = await resend.Events.create_async(
26+
{
27+
"name": "user.signed_up",
28+
"schema": {
29+
"plan": "string",
30+
"trial_days": "number",
31+
"is_enterprise": "boolean",
32+
"upgraded_at": "date",
33+
},
34+
}
35+
)
36+
event_id = created["id"]
37+
print(f"Created event: {event_id}")
38+
39+
# --- Get event by ID ---
40+
print("\n--- Get event by ID ---")
41+
event: resend.Event = await resend.Events.get_async(event_id)
42+
print(f"Name: {event['name']}, schema: {event['schema']}")
43+
44+
# --- Get event by name ---
45+
print("\n--- Get event by name ---")
46+
event_by_name: resend.Event = await resend.Events.get_async("user.signed_up")
47+
print(f"Found by name: {event_by_name['name']}")
48+
49+
# --- Update event schema ---
50+
print("\n--- Update event schema ---")
51+
updated: resend.Events.UpdateResponse = await resend.Events.update_async(
52+
{
53+
"identifier": "user.signed_up",
54+
"schema": {"plan": "string", "source": "string"},
55+
}
56+
)
57+
print(f"Updated event: {updated['id']}")
58+
59+
# --- Send event with contact_id ---
60+
print("\n--- Send event with contact_id ---")
61+
sent: resend.Events.SendResponse = await resend.Events.send_async(
62+
{
63+
"event": "user.signed_up",
64+
"contact_id": contact_id,
65+
"payload": {"plan": "pro"},
66+
}
67+
)
68+
print(f"Sent event: {sent['event']}")
69+
70+
# --- Send event with email ---
71+
print("\n--- Send event with email ---")
72+
sent_email: resend.Events.SendResponse = await resend.Events.send_async(
73+
{
74+
"event": "user.signed_up",
75+
"email": "test-events-async@example.com",
76+
}
77+
)
78+
print(f"Sent event: {sent_email['event']}")
79+
80+
# --- List events ---
81+
print("\n--- List events ---")
82+
list_resp: resend.Events.ListResponse = await resend.Events.list_async()
83+
print(f"Total: {len(list_resp['data'])}, has_more: {list_resp['has_more']}")
84+
85+
# --- Cleanup ---
86+
print("\n--- Delete event ---")
87+
deleted: resend.Events.DeleteResponse = await resend.Events.remove_async(event_id)
88+
print(f"Deleted: {deleted['deleted']}")
89+
90+
print("\n--- Delete contact ---")
91+
await resend.Contacts.remove_async(id=contact_id)
92+
print(f"Deleted contact: {contact_id}")
93+
94+
95+
asyncio.run(main())

examples/async/receiving_email_async.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ async def main() -> None:
7979
print("No attachments")
8080

8181
print("\n--- Listing All Received Emails ---")
82-
all_emails: EmailsReceiving.ListResponse = await resend.Emails.Receiving.list_async()
82+
all_emails: EmailsReceiving.ListResponse = (
83+
await resend.Emails.Receiving.list_async()
84+
)
8385

8486
print(f"Total emails in this batch: {len(all_emails['data'])}")
8587
print(f"Has more emails: {all_emails['has_more']}")
@@ -138,7 +140,9 @@ async def main() -> None:
138140
first_attachment = received_email["attachments"][0]
139141
attachment_id = first_attachment["id"]
140142

141-
print(f"\n--- Retrieving Attachment Details: {first_attachment['filename']} ---")
143+
print(
144+
f"\n--- Retrieving Attachment Details: {first_attachment['filename']} ---"
145+
)
142146

143147
attachment_details: resend.EmailAttachmentDetails = (
144148
await resend.Emails.Receiving.Attachments.get_async(

0 commit comments

Comments
 (0)