resend 0.27.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d14aa64ac124d647f4fb79b7f408e485fb91c67289a6e085fe39bca75e602793
4
- data.tar.gz: c4302a083a1b6c2ed08ae62825ea1383ee53644967b59e9279d6489863dfdbdd
3
+ metadata.gz: 23a4b97089acf88e3b1a768ebb7b9db1c5733ea1b225ca94cdeaae55abe4d0bc
4
+ data.tar.gz: 24e7052354d80707fe5d1d4aafeac46b85926deec93089f853c11c22965501a8
5
5
  SHA512:
6
- metadata.gz: e1d6b730a10d67e4643d11b24ab11c9c26007ef874505fa0afd569009e16449c073c978a82d734f108f3f71660762b7591c3c8b95f2fabd15574622b30ff80b7
7
- data.tar.gz: 8f0bab76e49b940813ce73dc71daebe1a3a3f423d27b4fecfb89db06460949f80c15da300a1d5d82c4768006ba0bff0e7b641b5a0360830751f413932ae886e8
6
+ metadata.gz: df538ce18a8a477d1527a270088691c98c5c2f1f6a793502f6abafd605f04e47a9c23b7366a7ef6c694c90ec4804ac2a909d8288d80c61062af7dc179c7d8d33
7
+ data.tar.gz: 73cbbda1da1cf01ebdb93c5f542481a6e33bebd2ae9d7a2ddbe696fac3d6ff550f2f270e3ce805fb72546a0af72557826dcf9c74509205c14935dfa49f8cb817
data/CHANGELOG.md ADDED
@@ -0,0 +1,138 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.6.0] - 2026-07-09
9
+
10
+ ### Added
11
+
12
+ - **OAuth grants** - New `Resend::OAuthGrants` module to list (`GET /oauth/grants`) and revoke (`DELETE /oauth/grants/:id`) OAuth grants. `list` supports the standard `limit`/`after`/`before` pagination params. ([#197](https://github.com/resend/resend-ruby/pull/197))
13
+ - **Domain claims** - New `Resend::Domains::Claims` module to claim a domain verified by another account (`create`), retrieve the latest claim (`get`), and trigger verification/ownership transfer (`verify`). ([#194](https://github.com/resend/resend-ruby/pull/194))
14
+
15
+ ## [1.0.0] - 2025-10-31
16
+
17
+ ### Overview
18
+
19
+ This major release introduces breaking changes to the Contacts API and deprecates the Audiences API in favor of Segments. **If you only use the SDK for sending emails or the Audiences API, you can upgrade to v1.0.0 without any code changes.** The breaking changes are limited to the Contacts API only.
20
+
21
+ ### ⚠️ BREAKING CHANGES
22
+
23
+ #### Contacts API Changes
24
+
25
+ - ⚠️ **Change `Contacts.create` to accept hash parameter with optional `audience_id`** - Previously required `audience_id` in params, now it's optional. Supports global contacts.
26
+ - ⚠️ **Change `Contacts.get` from `get(audience_id, id)` to `get(params)`** - Now accepts a hash with optional `audience_id` and required `id` or `email`. Raises `ArgumentError: "Missing \`id\` or \`email\` field"` when neither is provided.
27
+ - ⚠️ **Change `Contacts.list` from `list(audience_id, params = {})` to `list(params = {})`** - Now accepts a hash with optional `audience_id` and pagination params
28
+ - ⚠️ **Change `Contacts.remove` from `remove(audience_id, contact_id)` to `remove(params)`** - Now accepts a hash with optional `audience_id` and required `id` or `email`. Raises `ArgumentError: "Missing \`id\` or \`email\` field"` when neither is provided.
29
+ - ⚠️ **Change `Contacts.update` error message** - Error changed from `"id or email is required"` to `"Missing \`id\` or \`email\` field"` to match Node.js SDK format
30
+ - ⚠️ **Change `Contacts.update` to accept optional `audience_id`** - Previously required `audience_id` in params, now it's optional
31
+
32
+ **Before (v0.x):**
33
+
34
+ ```ruby
35
+ # Methods used positional arguments and required audience_id
36
+ Resend::Contacts.create(audience_id: "aud_123", email: "user@example.com", first_name: "John")
37
+ contact = Resend::Contacts.get("aud_123", "contact_123")
38
+ contacts = Resend::Contacts.list("aud_123")
39
+ contacts = Resend::Contacts.list("aud_123", limit: 10)
40
+ Resend::Contacts.update(audience_id: "aud_123", id: "contact_123", first_name: "Jane")
41
+ Resend::Contacts.remove("aud_123", "contact_123")
42
+ ```
43
+
44
+ **After (v1.0.0):**
45
+
46
+ ```ruby
47
+ # Methods use hash parameters and support optional audience_id
48
+ # Global contacts (no audience_id)
49
+ Resend::Contacts.create(email: "user@example.com", first_name: "John")
50
+ contact = Resend::Contacts.get(id: "contact_123")
51
+ contact = Resend::Contacts.get(email: "user@example.com")
52
+ contacts = Resend::Contacts.list
53
+ contacts = Resend::Contacts.list(limit: 10)
54
+ Resend::Contacts.update(id: "contact_123", first_name: "Jane")
55
+ Resend::Contacts.remove(id: "contact_123")
56
+
57
+ # Audience-scoped contacts (with audience_id)
58
+ Resend::Contacts.create(audience_id: "aud_123", email: "user@example.com", first_name: "John")
59
+ contact = Resend::Contacts.get(audience_id: "aud_123", id: "contact_123")
60
+ contacts = Resend::Contacts.list(audience_id: "aud_123", limit: 10)
61
+ Resend::Contacts.update(audience_id: "aud_123", id: "contact_123", first_name: "Jane")
62
+ Resend::Contacts.remove(audience_id: "aud_123", id: "contact_123")
63
+ ```
64
+
65
+ #### Audiences API Deprecated
66
+
67
+ - ⚠️ **Deprecate `Resend::Audiences` in favor of `Resend::Segments`** - The Audiences module has been replaced with Segments. A backward-compatible alias `Audiences = Segments` has been added, so existing code will continue to work.
68
+
69
+ **Migration (Recommended):**
70
+
71
+ Update your code to use `Segments` instead of `Audiences`:
72
+
73
+ ```ruby
74
+ # Before (still works but deprecated)
75
+ Resend::Audiences.create(name: "My Audience")
76
+ Resend::Audiences.get("audience_123")
77
+ Resend::Audiences.list
78
+ Resend::Audiences.remove("audience_123")
79
+
80
+ # After (recommended)
81
+ Resend::Segments.create(name: "My Segment")
82
+ Resend::Segments.get("segment_123")
83
+ Resend::Segments.list
84
+ Resend::Segments.remove("segment_123")
85
+ ```
86
+
87
+ **Note:** The `Audiences` alias is deprecated and may be removed in a future major version. Please migrate to `Segments`.
88
+
89
+ ### Added
90
+
91
+ #### New API Modules
92
+
93
+ - Add `Resend::Templates` API for managing email templates
94
+ - `Templates.create` - Create a new template
95
+ - `Templates.get` - Retrieve a template by ID
96
+ - `Templates.update` - Update an existing template
97
+ - `Templates.publish` - Publish a template
98
+ - `Templates.duplicate` - Duplicate an existing template
99
+ - `Templates.list` - List all templates with pagination
100
+ - `Templates.remove` - Delete a template
101
+ - Add `Resend::Topics` API for managing topics
102
+ - `Topics.create` - Create a new topic
103
+ - `Topics.get` - Retrieve a topic by ID
104
+ - `Topics.update` - Update a topic
105
+ - `Topics.list` - List all topics with pagination
106
+ - `Topics.remove` - Delete a topic
107
+ - Add `Resend::Segments` API for managing segments (replacement for Audiences)
108
+ - `Segments.create` - Create a new segment
109
+ - `Segments.get` - Retrieve a segment by ID
110
+ - `Segments.list` - List all segments
111
+ - `Segments.remove` - Delete a segment
112
+ - Add `Resend::ContactProperties` API for managing custom contact properties
113
+ - `ContactProperties.update` - Update contact properties (validates and raises `ArgumentError: "Missing \`id\` field"` when id is not provided)
114
+ - `ContactProperties.get` - Retrieve contact properties by ID
115
+ - Add `Resend::Contacts::Segments` API for managing contact-segment relationships
116
+ - `Contacts::Segments.list` - List all segments for a contact
117
+ - `Contacts::Segments.add` - Add a contact to a segment
118
+ - `Contacts::Segments.remove` - Remove a contact from a segment
119
+ - Add `Resend::Contacts::Topics` API for managing contact topic subscriptions
120
+ - `Contacts::Topics.list` - List topic subscriptions for a contact
121
+ - `Contacts::Topics.update` - Update topic subscriptions (opt-in/opt-out) for a contact
122
+
123
+ #### Contacts API Enhancements
124
+
125
+ - Add support for `email` parameter in `Contacts.get` and `Contacts.remove` methods (can now use email instead of ID)
126
+ - Add `audience_id` support in Contacts API methods for scoped operations
127
+ - Add support for global contacts (contacts not scoped to a specific audience/segment)
128
+ - Add validation error messages matching Node.js SDK format with backticks around field names
129
+
130
+ #### Broadcasts API Updates
131
+
132
+ - Add deprecation warnings for `audience_id` in `Broadcasts.create` and `Broadcasts.update` (use `segment_id` instead)
133
+
134
+ ### Removed
135
+
136
+ - Remove deprecated `send_email` method from `Resend::Emails` module (use `Resend::Emails.send` instead)
137
+
138
+ [1.0.0]: https://github.com/resend/resend-ruby/compare/v0.26.0...v1.0.0
@@ -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,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
@@ -5,13 +5,26 @@ module Resend
5
5
  module Broadcasts
6
6
  class << self
7
7
  # https://resend.com/docs/api-reference/broadcasts/create-broadcast
8
+ # @note Supports both segment_id and audience_id. At least one is required.
9
+ # audience_id is deprecated - use segment_id instead.
10
+ # @note When send: true is passed, the broadcast is sent immediately instead of
11
+ # creating a draft. When using send: true, you can also include scheduled_at
12
+ # to schedule the broadcast. Passing scheduled_at without send: true is an error.
8
13
  def create(params = {})
14
+ if params[:audience_id] && !params[:segment_id]
15
+ warn "[DEPRECATION] Using audience_id in broadcasts is deprecated. Use segment_id instead."
16
+ end
9
17
  path = "broadcasts"
10
18
  Resend::Request.new(path, params, "post").perform
11
19
  end
12
20
 
13
21
  # https://resend.com/docs/api-reference/broadcasts/update-broadcast
22
+ # @note Supports both segment_id and audience_id. At least one may be required.
23
+ # audience_id is deprecated - use segment_id instead.
14
24
  def update(params = {})
25
+ if params[:audience_id] && !params[:segment_id]
26
+ warn "[DEPRECATION] Using audience_id in broadcasts is deprecated. Use segment_id instead."
27
+ end
15
28
  path = "broadcasts/#{params[:broadcast_id]}"
16
29
  Resend::Request.new(path, params, "patch").perform
17
30
  end
@@ -0,0 +1,106 @@
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
+ raise ArgumentError, "Missing `id` field" if params[:id].nil?
86
+
87
+ contact_property_id = params[:id]
88
+ path = "contact-properties/#{contact_property_id}"
89
+ Resend::Request.new(path, params, "patch").perform
90
+ end
91
+
92
+ # Remove an existing contact property
93
+ #
94
+ # @param contact_property_id [String] The Contact Property ID
95
+ #
96
+ # @return [Hash] Response containing the deleted property ID and confirmation
97
+ #
98
+ # @example Delete a contact property
99
+ # Resend::ContactProperties.remove('b6d24b8e-af0b-4c3c-be0c-359bbd97381e')
100
+ def remove(contact_property_id = "")
101
+ path = "contact-properties/#{contact_property_id}"
102
+ Resend::Request.new(path, {}, "delete").perform
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resend
4
+ module Contacts
5
+ # Contact Imports API wrapper
6
+ module Imports
7
+ class << self
8
+ #
9
+ # Create a new contact import from a CSV file.
10
+ #
11
+ # @param params [Hash] the parameters
12
+ # @option params [String, IO] :file CSV file content (required). Maximum size is 50MB.
13
+ # @option params [Hash, String] :column_map optional mapping of contact fields to CSV columns.
14
+ # Accepts a Hash (will be JSON-encoded) or a pre-encoded JSON String.
15
+ # @option params [String] :on_conflict optional conflict strategy: 'upsert' or 'skip' (default 'skip').
16
+ # @option params [Array, String] :segments optional list of segment IDs to add contacts to.
17
+ # Accepts an Array of segment ID strings (will be JSON-encoded as [{"id":"..."}])
18
+ # or a pre-encoded JSON String.
19
+ # @option params [Array, String] :topics optional list of topic subscription objects.
20
+ # Each element must be a Hash with `id` (String) and `subscription` ('opt_in' or 'opt_out').
21
+ # Accepts an Array of Hashes (will be JSON-encoded) or a pre-encoded JSON String.
22
+ #
23
+ # https://resend.com/docs/api-reference/contacts/create-contact-import
24
+ def create(params)
25
+ raise ArgumentError, "Missing required `file` field" if params[:file].nil?
26
+
27
+ # Normalize segments: convert array of IDs to [{id: ...}] format
28
+ if params[:segments].is_a?(Array)
29
+ params = params.merge(segments: params[:segments].map { |s| s.is_a?(String) ? { id: s } : s })
30
+ end
31
+
32
+ Resend::MultipartRequest.new("contacts/imports", params, "post").perform
33
+ end
34
+
35
+ #
36
+ # Retrieve a single contact import by ID.
37
+ #
38
+ # @param id [String] the contact import ID (required)
39
+ #
40
+ # https://resend.com/docs/api-reference/contacts/get-contact-import
41
+ def get(id)
42
+ raise ArgumentError, "Missing required `id` field" if id.nil? || id.empty?
43
+
44
+ Resend::Request.new("contacts/imports/#{id}", {}, "get").perform
45
+ end
46
+
47
+ #
48
+ # Retrieve a list of contact imports.
49
+ #
50
+ # @param params [Hash] optional filtering and pagination parameters
51
+ # @option params [String] :status filter by status: 'queued', 'in_progress', 'completed', or 'failed'
52
+ # @option params [Integer] :limit number of results to return
53
+ # @option params [String] :after cursor for forward pagination
54
+ # @option params [String] :before cursor for backward pagination
55
+ #
56
+ # https://resend.com/docs/api-reference/contacts/list-contact-imports
57
+ def list(params = {})
58
+ path = Resend::PaginationHelper.build_paginated_path("contacts/imports", params)
59
+ Resend::Request.new(path, {}, "get").perform
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resend
4
+ module Contacts
5
+ # Contact Segments api wrapper
6
+ module Segments
7
+ class << self
8
+ #
9
+ # List all segments for a contact
10
+ #
11
+ # @param params [Hash] the parameters
12
+ # @option params [String] :contact_id the contact id (either contact_id or email is required)
13
+ # @option params [String] :email the contact email (either contact_id or email is required)
14
+ # @option params [Integer] :limit the maximum number of results to return (optional)
15
+ # @option params [String] :after the cursor for pagination (optional)
16
+ # @option params [String] :before the cursor for pagination (optional)
17
+ #
18
+ # https://resend.com/docs/api-reference/contacts/list-contact-segments
19
+ def list(params)
20
+ raise ArgumentError, "contact_id or email is required" if params[:contact_id].nil? && params[:email].nil?
21
+
22
+ identifier = params[:contact_id] || params[:email]
23
+ base_path = "contacts/#{identifier}/segments"
24
+ path = Resend::PaginationHelper.build_paginated_path(base_path, params)
25
+ Resend::Request.new(path, {}, "get").perform
26
+ end
27
+
28
+ #
29
+ # Add a contact to a segment
30
+ #
31
+ # @param params [Hash] the parameters
32
+ # @option params [String] :contact_id the contact id (either contact_id or email is required)
33
+ # @option params [String] :email the contact email (either contact_id or email is required)
34
+ # @option params [String] :segment_id the segment id (required)
35
+ #
36
+ # https://resend.com/docs/api-reference/contacts/add-contact-to-segment
37
+ def add(params)
38
+ raise ArgumentError, "contact_id or email is required" if params[:contact_id].nil? && params[:email].nil?
39
+ raise ArgumentError, "segment_id is required" if params[:segment_id].nil?
40
+
41
+ identifier = params[:contact_id] || params[:email]
42
+ path = "contacts/#{identifier}/segments/#{params[:segment_id]}"
43
+ Resend::Request.new(path, {}, "post").perform
44
+ end
45
+
46
+ #
47
+ # Remove a contact from a segment
48
+ #
49
+ # @param params [Hash] the parameters
50
+ # @option params [String] :contact_id the contact id (either contact_id or email is required)
51
+ # @option params [String] :email the contact email (either contact_id or email is required)
52
+ # @option params [String] :segment_id the segment id (required)
53
+ #
54
+ # https://resend.com/docs/api-reference/contacts/remove-contact-from-segment
55
+ def remove(params)
56
+ raise ArgumentError, "contact_id or email is required" if params[:contact_id].nil? && params[:email].nil?
57
+ raise ArgumentError, "segment_id is required" if params[:segment_id].nil?
58
+
59
+ identifier = params[:contact_id] || params[:email]
60
+ path = "contacts/#{identifier}/segments/#{params[:segment_id]}"
61
+ Resend::Request.new(path, {}, "delete").perform
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,80 @@
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 params [Hash] Parameters for listing topics
13
+ # @option params [String] :id The Contact ID (either :id or :email must be provided)
14
+ # @option params [String] :email The Contact Email (either :id or :email must be provided)
15
+ # @option params [Integer] :limit Number of topics to retrieve (1-100)
16
+ # @option params [String] :after The ID after which to retrieve more topics
17
+ # @option params [String] :before The ID before which to retrieve more topics
18
+ #
19
+ # @return [Hash] Response containing list of topics with subscription status
20
+ #
21
+ # @example List topics by contact ID
22
+ # Resend::Contacts::Topics.list(id: 'e169aa45-1ecf-4183-9955-b1499d5701d3')
23
+ #
24
+ # @example List topics by contact email
25
+ # Resend::Contacts::Topics.list(email: 'steve.wozniak@gmail.com')
26
+ #
27
+ # @example List topics with pagination
28
+ # Resend::Contacts::Topics.list(id: 'contact-id', limit: 10, after: 'cursor_123')
29
+ def list(params = {})
30
+ contact_identifier = params[:id] || params[:email]
31
+ raise ArgumentError, "Either :id or :email must be provided" if contact_identifier.nil?
32
+
33
+ pagination_params = params.slice(:limit, :after, :before)
34
+ base_path = "contacts/#{contact_identifier}/topics"
35
+ path = Resend::PaginationHelper.build_paginated_path(base_path, pagination_params)
36
+
37
+ Resend::Request.new(path, {}, "get").perform
38
+ end
39
+
40
+ # Update topic subscriptions for a contact
41
+ #
42
+ # @param params [Hash] Parameters for updating topics
43
+ # @option params [String] :id The Contact ID (either :id or :email must be provided)
44
+ # @option params [String] :email The Contact Email (either :id or :email must be provided)
45
+ # @option params [Array<Hash>] :topics Array of topic subscription updates
46
+ # Each topic hash should contain:
47
+ # - :id [String] The Topic ID (required)
48
+ # - :subscription [String] The subscription action: 'opt_in' or 'opt_out' (required)
49
+ #
50
+ # @return [Hash] Response containing the contact ID
51
+ #
52
+ # @example Update by contact ID
53
+ # Resend::Contacts::Topics.update({
54
+ # id: 'e169aa45-1ecf-4183-9955-b1499d5701d3',
55
+ # topics: [
56
+ # { id: 'b6d24b8e-af0b-4c3c-be0c-359bbd97381e', subscription: 'opt_out' },
57
+ # { id: '07d84122-7224-4881-9c31-1c048e204602', subscription: 'opt_in' }
58
+ # ]
59
+ # })
60
+ #
61
+ # @example Update by contact email
62
+ # Resend::Contacts::Topics.update({
63
+ # email: 'steve.wozniak@gmail.com',
64
+ # topics: [
65
+ # { id: '07d84122-7224-4881-9c31-1c048e204602', subscription: 'opt_out' }
66
+ # ]
67
+ # })
68
+ def update(params)
69
+ contact_identifier = params[:id] || params[:email]
70
+ raise ArgumentError, "Either :id or :email must be provided" if contact_identifier.nil?
71
+
72
+ path = "contacts/#{contact_identifier}/topics"
73
+ body = params[:topics]
74
+
75
+ Resend::Request.new(path, body, "patch").perform
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end