Skip to content

Commit db7a739

Browse files
authored
feat: contact properties + contact topic update/get (#144)
1 parent 910b5eb commit db7a739

8 files changed

Lines changed: 631 additions & 1 deletion

File tree

examples/contact_properties.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
def example
10+
# Create a contact property with string type
11+
puts "Creating a string contact property..."
12+
string_property = Resend::ContactProperties.create({
13+
key: "company_name",
14+
type: "string",
15+
fallback_value: "Acme Corp"
16+
})
17+
puts "Created property: #{string_property}"
18+
19+
property_id = string_property[:id]
20+
21+
# Create a contact property with number type
22+
puts "\nCreating a number contact property..."
23+
number_property = Resend::ContactProperties.create({
24+
key: "age",
25+
type: "number",
26+
fallback_value: 0
27+
})
28+
puts "Created property: #{number_property}"
29+
30+
# Retrieve a contact property
31+
puts "\nRetrieving contact property by ID..."
32+
retrieved_property = Resend::ContactProperties.get(property_id)
33+
puts "Retrieved property: #{retrieved_property}"
34+
35+
# List all contact properties
36+
puts "\nListing all contact properties..."
37+
all_properties = Resend::ContactProperties.list
38+
puts "All properties: #{all_properties}"
39+
40+
# List contact properties with pagination
41+
puts "\nListing contact properties with pagination..."
42+
paginated_properties = Resend::ContactProperties.list({ limit: 10 })
43+
puts "Paginated properties: #{paginated_properties}"
44+
45+
# Update a contact property
46+
puts "\nUpdating contact property..."
47+
updated_property = Resend::ContactProperties.update({
48+
id: property_id,
49+
fallback_value: "Example Company"
50+
})
51+
puts "Updated property: #{updated_property}"
52+
53+
# Delete a contact property
54+
puts "\nDeleting contact property..."
55+
deleted = Resend::ContactProperties.remove(property_id)
56+
puts "Deleted property: #{deleted}"
57+
end
58+
59+
example

examples/contacts.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,56 @@ def example
4848
puts "Paginated contacts (limit 10):"
4949
puts paginated_contacts
5050

51+
# === Contact Topics ===
52+
puts "\n=== Contact Topics ==="
53+
54+
# Create a topic first (required for contact topics operations)
55+
topic_params = {
56+
name: "Product Updates #{Time.now.to_i}",
57+
description: "Updates about our products",
58+
default_subscription: "opt_out"
59+
}
60+
61+
topic = Resend::Topics.create(topic_params)
62+
puts "Topic created: #{topic}"
63+
topic_id = topic[:id]
64+
65+
# Get contact topics
66+
puts "\nGetting contact topics..."
67+
contact_topics = Resend::Contacts::Topics.get(contact[:id])
68+
puts "Contact topics: #{contact_topics}"
69+
70+
# Update contact topic subscriptions - opt in to the topic
71+
puts "\nOpting in to topic..."
72+
update_topics_params = {
73+
id: contact[:id],
74+
topics: [
75+
{ id: topic_id, subscription: "opt_in" }
76+
]
77+
}
78+
79+
updated_topics = Resend::Contacts::Topics.update(update_topics_params)
80+
puts "Updated topic subscription: #{updated_topics}"
81+
82+
# Get contact topics again to see the updated subscription
83+
puts "\nGetting contact topics after opt-in..."
84+
contact_topics_after = Resend::Contacts::Topics.get(contact[:id])
85+
puts "Contact topics after update: #{contact_topics_after}"
86+
87+
# Update contact topic subscriptions - opt out from the topic
88+
puts "\nOpting out from topic..."
89+
update_topics_params[:topics] = [
90+
{ id: topic_id, subscription: "opt_out" }
91+
]
92+
93+
updated_topics_opt_out = Resend::Contacts::Topics.update(update_topics_params)
94+
puts "Opted out from topic: #{updated_topics_opt_out}"
95+
96+
# Clean up: delete the topic
97+
puts "\nCleaning up topic..."
98+
Resend::Topics.remove(topic_id)
99+
puts "Topic deleted"
100+
51101
# delete by id
52102
del = Resend::Contacts.remove(audience_id, contact[:id])
53103

examples/simple_mail.rb

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,39 @@
5555
puts "\nGetting next page after ID: #{last_id}"
5656
next_page = Resend::Emails.list(limit: 5, after: last_id)
5757
puts "Next page has #{next_page["data"].length} emails"
58-
end
58+
end
59+
60+
attachments = Resend::Emails::Attachments.list(email_id: email[:id])
61+
62+
puts "Total attachments: #{attachments[:data].length}"
63+
puts "Has more: #{attachments[:has_more]}"
64+
65+
if attachments[:data] && !attachments[:data].empty?
66+
attachments[:data].each do |attachment|
67+
puts "\n Attachment:"
68+
puts " ID: #{attachment[:id]}"
69+
puts " Filename: #{attachment[:filename]}"
70+
puts " Size: #{attachment[:size]} bytes"
71+
puts " Content Type: #{attachment[:content_type]}"
72+
end
73+
74+
# Retrieve details for the first attachment
75+
puts "\n=== Retrieving Single Attachment ==="
76+
attachment_id = attachments[:data].first[:id]
77+
puts "Retrieving attachment: #{attachment_id}"
78+
79+
attachment = Resend::Emails::Attachments.get(
80+
id: attachment_id,
81+
email_id: email[:id]
82+
)
83+
84+
puts "\nAttachment Details:"
85+
puts " ID: #{attachment[:id]}"
86+
puts " Filename: #{attachment[:filename]}"
87+
puts " Size: #{attachment[:size]} bytes"
88+
puts " Content Type: #{attachment[:content_type]}"
89+
puts " Download URL: #{attachment[:download_url]}"
90+
puts " Expires At: #{attachment[:expires_at]}"
91+
else
92+
puts "No attachments found for this email"
93+
end

lib/resend.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
require "resend/broadcasts"
1919
require "resend/batch"
2020
require "resend/contacts"
21+
require "resend/contacts/topics"
22+
require "resend/contact_properties"
2123
require "resend/domains"
2224
require "resend/emails"
2325
require "resend/templates"

lib/resend/contact_properties.rb

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# frozen_string_literal: true
2+
3+
module Resend
4+
# Module for managing contact properties
5+
#
6+
# Contact properties allow you to store custom data about your contacts
7+
module ContactProperties
8+
class << self
9+
# Create a custom property for your contacts
10+
#
11+
# @param params [Hash] Parameters for creating a contact property
12+
# @option params [String] :key The property key (max 50 characters, alphanumeric and underscores only) (required)
13+
# @option params [String] :type The property type ('string' or 'number') (required)
14+
# @option params [String, Integer] :fallback_value The default value when property is not set (must match type)
15+
#
16+
# @return [Hash] Response containing the created contact property
17+
#
18+
# @example Create a string property
19+
# Resend::ContactProperties.create({
20+
# key: 'company_name',
21+
# type: 'string',
22+
# fallback_value: 'Acme Corp'
23+
# })
24+
#
25+
# @example Create a number property
26+
# Resend::ContactProperties.create({
27+
# key: 'age',
28+
# type: 'number',
29+
# fallback_value: 0
30+
# })
31+
def create(params)
32+
path = "contact-properties"
33+
Resend::Request.new(path, params, "post").perform
34+
end
35+
36+
# Retrieve a contact property by its ID
37+
#
38+
# @param contact_property_id [String] The Contact Property ID
39+
#
40+
# @return [Hash] Response containing the contact property details
41+
#
42+
# @example Get a contact property
43+
# Resend::ContactProperties.get('b6d24b8e-af0b-4c3c-be0c-359bbd97381e')
44+
def get(contact_property_id = "")
45+
path = "contact-properties/#{contact_property_id}"
46+
Resend::Request.new(path, {}, "get").perform
47+
end
48+
49+
# Retrieve a list of contact properties
50+
#
51+
# @param params [Hash] Optional query parameters
52+
# @option params [Integer] :limit Number of contact properties to retrieve (1-100, default: 20)
53+
# @option params [String] :after The ID after which to retrieve more contact properties
54+
# @option params [String] :before The ID before which to retrieve more contact properties
55+
#
56+
# @return [Hash] Response containing list of contact properties
57+
#
58+
# @example List all contact properties
59+
# Resend::ContactProperties.list
60+
#
61+
# @example List with pagination
62+
# Resend::ContactProperties.list({ limit: 10, after: 'cursor_123' })
63+
def list(params = {})
64+
path = Resend::PaginationHelper.build_paginated_path("contact-properties", params)
65+
Resend::Request.new(path, {}, "get").perform
66+
end
67+
68+
# Update an existing contact property
69+
#
70+
# Note: The 'key' and 'type' fields cannot be changed after creation
71+
#
72+
# @param params [Hash] Parameters for updating a contact property
73+
# @option params [String] :id The Contact Property ID (required)
74+
# @option params [String, Integer] :fallback_value The default value when property is not set
75+
# (must match property type)
76+
#
77+
# @return [Hash] Response containing the updated contact property
78+
#
79+
# @example Update fallback value
80+
# Resend::ContactProperties.update({
81+
# id: 'b6d24b8e-af0b-4c3c-be0c-359bbd97381e',
82+
# fallback_value: 'Example Company'
83+
# })
84+
def update(params)
85+
contact_property_id = params[:id]
86+
path = "contact-properties/#{contact_property_id}"
87+
Resend::Request.new(path, params, "patch").perform
88+
end
89+
90+
# Remove an existing contact property
91+
#
92+
# @param contact_property_id [String] The Contact Property ID
93+
#
94+
# @return [Hash] Response containing the deleted property ID and confirmation
95+
#
96+
# @example Delete a contact property
97+
# Resend::ContactProperties.remove('b6d24b8e-af0b-4c3c-be0c-359bbd97381e')
98+
def remove(contact_property_id = "")
99+
path = "contact-properties/#{contact_property_id}"
100+
Resend::Request.new(path, {}, "delete").perform
101+
end
102+
end
103+
end
104+
end

lib/resend/contacts/topics.rb

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# frozen_string_literal: true
2+
3+
module Resend
4+
module Contacts
5+
# Module for managing contact topic subscriptions
6+
#
7+
# Allows you to manage which topics contacts are subscribed to
8+
module Topics
9+
class << self
10+
# Retrieve a list of topics subscriptions for a contact
11+
#
12+
# @param contact_identifier [String] The Contact ID or Email
13+
# @param params [Hash] Optional query parameters
14+
# @option params [Integer] :limit Number of topics to retrieve (1-100)
15+
# @option params [String] :after The ID after which to retrieve more topics
16+
# @option params [String] :before The ID before which to retrieve more topics
17+
#
18+
# @return [Hash] Response containing list of topics with subscription status
19+
#
20+
# @example Get topics by contact ID
21+
# Resend::Contacts::Topics.get('e169aa45-1ecf-4183-9955-b1499d5701d3')
22+
#
23+
# @example Get topics by contact email
24+
# Resend::Contacts::Topics.get('steve.wozniak@gmail.com')
25+
#
26+
# @example Get topics with pagination
27+
# Resend::Contacts::Topics.get('contact-id', { limit: 10, after: 'cursor_123' })
28+
def get(contact_identifier, params = {})
29+
pagination_params = params.slice(:limit, :after, :before)
30+
base_path = "contacts/#{contact_identifier}/topics"
31+
path = Resend::PaginationHelper.build_paginated_path(base_path, pagination_params)
32+
33+
Resend::Request.new(path, {}, "get").perform
34+
end
35+
36+
# Update topic subscriptions for a contact
37+
#
38+
# @param params [Hash] Parameters for updating topics
39+
# @option params [String] :id The Contact ID (either :id or :email must be provided)
40+
# @option params [String] :email The Contact Email (either :id or :email must be provided)
41+
# @option params [Array<Hash>] :topics Array of topic subscription updates
42+
# Each topic hash should contain:
43+
# - :id [String] The Topic ID (required)
44+
# - :subscription [String] The subscription action: 'opt_in' or 'opt_out' (required)
45+
#
46+
# @return [Hash] Response containing the contact ID
47+
#
48+
# @example Update by contact ID
49+
# Resend::Contacts::Topics.update({
50+
# id: 'e169aa45-1ecf-4183-9955-b1499d5701d3',
51+
# topics: [
52+
# { id: 'b6d24b8e-af0b-4c3c-be0c-359bbd97381e', subscription: 'opt_out' },
53+
# { id: '07d84122-7224-4881-9c31-1c048e204602', subscription: 'opt_in' }
54+
# ]
55+
# })
56+
#
57+
# @example Update by contact email
58+
# Resend::Contacts::Topics.update({
59+
# email: 'steve.wozniak@gmail.com',
60+
# topics: [
61+
# { id: '07d84122-7224-4881-9c31-1c048e204602', subscription: 'opt_out' }
62+
# ]
63+
# })
64+
def update(params)
65+
contact_identifier = params[:id] || params[:email]
66+
raise ArgumentError, "Either :id or :email must be provided" if contact_identifier.nil?
67+
68+
path = "contacts/#{contact_identifier}/topics"
69+
body = params[:topics]
70+
71+
Resend::Request.new(path, body, "patch").perform
72+
end
73+
end
74+
end
75+
end
76+
end

0 commit comments

Comments
 (0)