Skip to content

Commit bb4b974

Browse files
committed
feat(automations,events): add Automations and Events API namespaces
- Add Resend::Automations with create, get, update, remove, stop, list - Add Resend::Automations::Runs with list and get (nested namespace) - Add Resend::Events with create, get, update, remove, send, list - Add specs and examples for both namespaces - Add base64 as explicit runtime dependency (removed from Ruby 3.4 stdlib) - Move rails to Gemfile development group, remove from gemspec - Update Dockerfile to bundle install --without development
1 parent 29bf4fe commit bb4b974

12 files changed

Lines changed: 873 additions & 10 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ WORKDIR /app
77

88
ADD . /app/
99

10-
RUN bundle install
10+
RUN bundle install --without development

Gemfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ gem "rspec", "~> 3.0"
1111

1212
gem "rubocop", "~> 1.21"
1313

14-
gem "httparty", "~> 0.21.0"
14+
gem "httparty", "~> 0.22.0"
1515

1616
gem "pry-byebug"
17+
18+
group :development do
19+
gem "rails"
20+
end

Gemfile.lock

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
PATH
22
remote: .
33
specs:
4-
resend (1.0.1)
5-
httparty (>= 0.21.0)
4+
resend (1.1.0)
5+
base64
6+
httparty (>= 0.21.0, < 1)
67

78
GEM
89
remote: https://rubygems.org/
@@ -73,17 +74,21 @@ GEM
7374
minitest (>= 5.1)
7475
tzinfo (~> 2.0)
7576
ast (2.4.3)
77+
base64 (0.3.0)
78+
bigdecimal (4.1.1)
7679
builder (3.2.4)
7780
byebug (11.1.3)
7881
coderay (1.1.3)
7982
concurrent-ruby (1.1.10)
8083
crass (1.0.6)
84+
csv (3.3.5)
8185
date (3.3.3)
8286
diff-lcs (1.5.1)
8387
erubi (1.12.0)
8488
globalid (1.0.0)
8589
activesupport (>= 5.0)
86-
httparty (0.21.0)
90+
httparty (0.22.0)
91+
csv
8792
mini_mime (>= 1.0.0)
8893
multi_xml (>= 0.5.2)
8994
i18n (1.12.0)
@@ -101,10 +106,11 @@ GEM
101106
net-smtp
102107
marcel (1.0.2)
103108
method_source (1.0.0)
104-
mini_mime (1.1.2)
109+
mini_mime (1.1.5)
105110
mini_portile2 (2.8.1)
106111
minitest (5.16.3)
107-
multi_xml (0.6.0)
112+
multi_xml (0.8.1)
113+
bigdecimal (>= 3.1, < 5)
108114
net-imap (0.3.4)
109115
date
110116
net-protocol
@@ -203,12 +209,13 @@ GEM
203209
PLATFORMS
204210
aarch64-linux
205211
arm64-darwin-22
212+
arm64-darwin-25
206213
x86_64-darwin-20
207214
x86_64-darwin-21
208215
x86_64-linux
209216

210217
DEPENDENCIES
211-
httparty (~> 0.21.0)
218+
httparty (~> 0.22.0)
212219
pry-byebug
213220
rails
214221
rake (~> 13.0)

examples/automations.rb

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../lib/resend"
4+
5+
raise if ENV["RESEND_API_KEY"].nil?
6+
7+
Resend.api_key = ENV["RESEND_API_KEY"]
8+
9+
# 1. Create a template (needed for the send_email step config)
10+
template = Resend::Templates.create({
11+
name: "Welcome Email",
12+
from: "onboarding@resend.dev",
13+
subject: "Welcome!",
14+
html: "<p>Welcome to our platform!</p>"
15+
})
16+
template_id = template[:id]
17+
puts "created template: #{template_id}"
18+
19+
Resend::Templates.publish(template_id)
20+
puts "published template: #{template_id}"
21+
22+
# 2. Create a simple automation: trigger → send_email
23+
simple_automation = Resend::Automations.create({
24+
name: "Simple Welcome Automation",
25+
steps: [
26+
{
27+
key: "trigger",
28+
type: "trigger",
29+
config: { event_name: "user.created" }
30+
},
31+
{
32+
key: "send_welcome",
33+
type: "send_email",
34+
config: { template: { id: template_id } }
35+
}
36+
],
37+
connections: [
38+
{ from: "trigger", to: "send_welcome", type: "default" }
39+
]
40+
})
41+
automation_id = simple_automation[:id]
42+
puts "created simple automation: #{automation_id}"
43+
44+
# 3. Get automation
45+
retrieved = Resend::Automations.get(automation_id)
46+
puts "retrieved automation: #{retrieved[:id]}, status: #{retrieved[:status]}"
47+
48+
# 4. Update automation (change status to enabled)
49+
updated = Resend::Automations.update({
50+
automation_id: automation_id,
51+
status: "enabled"
52+
})
53+
puts "updated automation: #{updated[:id]}"
54+
55+
# 5. List automations (without filter)
56+
all_automations = Resend::Automations.list
57+
puts "automations: #{all_automations[:data].length}, has_more: #{all_automations[:has_more]}"
58+
59+
# List automations with status filter
60+
enabled_automations = Resend::Automations.list({ status: "enabled" })
61+
puts "enabled automations: #{enabled_automations[:data].length}"
62+
63+
# 6. Stop automation
64+
stopped = Resend::Automations.stop(automation_id)
65+
puts "stopped automation: #{stopped[:id]}"
66+
67+
# 7. List runs (Resend::Automations::Runs.list)
68+
runs = Resend::Automations::Runs.list(automation_id)
69+
puts "runs: #{runs[:data].length}, has_more: #{runs[:has_more]}"
70+
71+
# 8. Get a run if any exist (Resend::Automations::Runs.get)
72+
if runs[:data].any?
73+
run_id = runs[:data].first[:id]
74+
run = Resend::Automations::Runs.get(automation_id, run_id)
75+
puts "run: #{run[:id]}, status: #{run[:status]}"
76+
else
77+
puts "no runs yet for this automation"
78+
end
79+
80+
# 9. Multi-step automation: trigger → delay → wait_for_event → send_email
81+
multi_automation = Resend::Automations.create({
82+
name: "Multi-step Onboarding Automation",
83+
steps: [
84+
{
85+
key: "trigger",
86+
type: "trigger",
87+
config: { event_name: "user.created" }
88+
},
89+
{
90+
key: "wait_30_min",
91+
type: "delay",
92+
config: { duration: "30 minutes" }
93+
},
94+
{
95+
key: "wait_verified",
96+
type: "wait_for_event",
97+
config: { event_name: "user.verified", timeout: "1 hour" }
98+
},
99+
{
100+
key: "send_welcome",
101+
type: "send_email",
102+
config: { template: { id: template_id } }
103+
}
104+
],
105+
connections: [
106+
{ from: "trigger", to: "wait_30_min", type: "default" },
107+
{ from: "wait_30_min", to: "wait_verified", type: "default" },
108+
{ from: "wait_verified", to: "send_welcome", type: "event_received" }
109+
]
110+
})
111+
multi_automation_id = multi_automation[:id]
112+
puts "created multi-step automation: #{multi_automation_id}"
113+
114+
# 10. Cleanup: delete both automations and the template
115+
Resend::Automations.remove(automation_id)
116+
puts "removed automation: #{automation_id}"
117+
118+
Resend::Automations.remove(multi_automation_id)
119+
puts "removed multi-step automation: #{multi_automation_id}"
120+
121+
Resend::Templates.remove(template_id)
122+
puts "removed template: #{template_id}"

examples/events.rb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../lib/resend"
4+
5+
raise if ENV["RESEND_API_KEY"].nil?
6+
7+
Resend.api_key = ENV["RESEND_API_KEY"]
8+
9+
# 1. Create a global contact (needed for event sends)
10+
contact = Resend::Contacts.create({
11+
email: "test.events@example.com",
12+
first_name: "Test",
13+
last_name: "User"
14+
})
15+
contact_id = contact[:id]
16+
puts "created contact: #{contact_id}"
17+
18+
# 2. Create event without schema
19+
event_no_schema = Resend::Events.create({ name: "user.signed_up" })
20+
puts "created event without schema: #{event_no_schema[:id]}"
21+
22+
# 3. Create event with schema
23+
schema = {
24+
"plan" => "string",
25+
"trial_days" => "number",
26+
"is_enterprise" => "boolean",
27+
"upgraded_at" => "date"
28+
}
29+
event_with_schema = Resend::Events.create({ name: "user.upgraded", schema: schema })
30+
event_id = event_with_schema[:id]
31+
puts "created event with schema: #{event_id}"
32+
33+
# 4. Get event by ID
34+
fetched_by_id = Resend::Events.get(event_id)
35+
puts "fetched event by ID: #{fetched_by_id[:id]}, name: #{fetched_by_id[:name]}"
36+
37+
# 5. Get event by name
38+
fetched_by_name = Resend::Events.get("user.upgraded")
39+
puts "fetched event by name: #{fetched_by_name[:id]}, name: #{fetched_by_name[:name]}"
40+
41+
# 6. Update event schema
42+
new_schema = {
43+
"plan" => "string",
44+
"trial_days" => "number",
45+
"is_enterprise" => "boolean",
46+
"upgraded_at" => "date",
47+
"referral_code" => "string"
48+
}
49+
updated_event = Resend::Events.update({ identifier: event_id, schema: new_schema })
50+
puts "updated event: #{updated_event[:id]}"
51+
52+
# 7. Send event with contact_id
53+
sent_with_contact_id = Resend::Events.send({
54+
event: "user.upgraded",
55+
contact_id: contact_id,
56+
payload: { plan: "pro", trial_days: 14, is_enterprise: false }
57+
})
58+
puts "sent event with contact_id: #{sent_with_contact_id[:event]}"
59+
60+
# 8. Send event with email
61+
sent_with_email = Resend::Events.send({
62+
event: "user.signed_up",
63+
email: "test.events@example.com"
64+
})
65+
puts "sent event with email: #{sent_with_email[:event]}"
66+
67+
# 9. List events
68+
events = Resend::Events.list
69+
puts "total events: #{events[:data].length}, has_more: #{events[:has_more]}"
70+
71+
# 10. List events with pagination params
72+
paginated = Resend::Events.list({ limit: 5 })
73+
puts "paginated events (limit 5): #{paginated[:data].length}"
74+
75+
if paginated[:has_more]
76+
last_id = paginated[:data].last[:id]
77+
more_events = Resend::Events.list({ limit: 5, after: last_id })
78+
puts "next page: #{more_events[:data].length}"
79+
end
80+
81+
# 11. Delete event by ID
82+
deleted_by_id = Resend::Events.remove(event_id)
83+
puts "deleted event by ID: #{deleted_by_id[:id]}, deleted: #{deleted_by_id[:deleted]}"
84+
85+
# 12. Delete event by name
86+
deleted_by_name = Resend::Events.remove("user.signed_up")
87+
puts "deleted event by name: #{deleted_by_name[:deleted]}"
88+
89+
# 13. Cleanup: delete contact
90+
Resend::Contacts.remove({ id: contact_id })
91+
puts "removed contact: #{contact_id}"

lib/resend.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
require "resend/logs"
3232
require "resend/topics"
3333
require "resend/webhooks"
34+
require "resend/automations"
35+
require "resend/automations/runs"
36+
require "resend/events"
3437

3538
# Rails
3639
require "resend/railtie" if defined?(Rails) && defined?(ActionMailer)

lib/resend/automations.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module Resend
4+
# automations api wrapper
5+
module Automations
6+
class << self
7+
# https://resend.com/docs/api-reference/automations/create-automation
8+
def create(params = {})
9+
Resend::Request.new("automations", params, "post").perform
10+
end
11+
12+
# https://resend.com/docs/api-reference/automations/get-automation
13+
def get(automation_id = "")
14+
Resend::Request.new("automations/#{automation_id}", {}, "get").perform
15+
end
16+
17+
# https://resend.com/docs/api-reference/automations/update-automation
18+
def update(params = {})
19+
path = "automations/#{params[:automation_id]}"
20+
payload = params.reject { |k, _| k == :automation_id }
21+
Resend::Request.new(path, payload, "patch").perform
22+
end
23+
24+
# https://resend.com/docs/api-reference/automations/delete-automation
25+
def remove(automation_id = "")
26+
Resend::Request.new("automations/#{automation_id}", {}, "delete").perform
27+
end
28+
29+
# https://resend.com/docs/api-reference/automations/stop-automation
30+
def stop(automation_id = "")
31+
Resend::Request.new("automations/#{automation_id}/stop", {}, "post").perform
32+
end
33+
34+
# https://resend.com/docs/api-reference/automations/list-automations
35+
def list(params = {})
36+
path = Resend::PaginationHelper.build_paginated_path("automations", params)
37+
Resend::Request.new(path, {}, "get").perform
38+
end
39+
end
40+
end
41+
end

lib/resend/automations/runs.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
module Resend
4+
module Automations
5+
# Automation Runs api wrapper
6+
module Runs
7+
class << self
8+
# https://resend.com/docs/api-reference/automations/list-automation-runs
9+
def list(automation_id, params = {})
10+
base_path = "automations/#{automation_id}/runs"
11+
path = Resend::PaginationHelper.build_paginated_path(base_path, params)
12+
Resend::Request.new(path, {}, "get").perform
13+
end
14+
15+
# https://resend.com/docs/api-reference/automations/get-automation-run
16+
def get(automation_id, run_id)
17+
Resend::Request.new("automations/#{automation_id}/runs/#{run_id}", {}, "get").perform
18+
end
19+
end
20+
end
21+
end
22+
end

0 commit comments

Comments
 (0)