resend 1.4.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: 5bac6323a992135612079621a1d9933831c44e6202838cff57377397c2214f2c
4
- data.tar.gz: c584c139bad503543f7cad52639f9dc6fe86812da1550b38729cd96638b9988b
3
+ metadata.gz: 23a4b97089acf88e3b1a768ebb7b9db1c5733ea1b225ca94cdeaae55abe4d0bc
4
+ data.tar.gz: 24e7052354d80707fe5d1d4aafeac46b85926deec93089f853c11c22965501a8
5
5
  SHA512:
6
- metadata.gz: 4411842066220cc450056f799a94d6cba513a6bf5c0c67c8f467e68e1cc8320911c3f47f983ec32430dc09961d4e005fb1f1ce429997b2ae5be5045eb7b85de6
7
- data.tar.gz: '0586cc9b333744f728d34847cc7de9bb18109b5658e870354f3b6a69b1988d4366e0bf4f37e3564a6a63f16de9e33f1070b3f15410be0dc88a996e8d3eff9336'
6
+ metadata.gz: df538ce18a8a477d1527a270088691c98c5c2f1f6a793502f6abafd605f04e47a9c23b7366a7ef6c694c90ec4804ac2a909d8288d80c61062af7dc179c7d8d33
7
+ data.tar.gz: 73cbbda1da1cf01ebdb93c5f542481a6e33bebd2ae9d7a2ddbe696fac3d6ff550f2f270e3ce805fb72546a0af72557826dcf9c74509205c14935dfa49f8cb817
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
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
+
8
15
  ## [1.0.0] - 2025-10-31
9
16
 
10
17
  ### Overview
@@ -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,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resend
4
+ module Domains
5
+ # Domain Claims API wrapper
6
+ module Claims
7
+ class << self
8
+ #
9
+ # Start a claim for a domain that another Resend account has already verified.
10
+ # The domain is recreated under your account with fresh DKIM keys, so the
11
+ # previous account's DNS records cannot be reused. Returns a TXT record to add
12
+ # to your DNS to prove ownership. Uses the same request body as creating a domain.
13
+ #
14
+ # @param params [Hash] the parameters
15
+ # @option params [String] :name the name of the domain you want to claim (required)
16
+ # @option params [String] :region the region where emails will be sent from.
17
+ # Possible values: 'us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1'
18
+ # @option params [String] :custom_return_path subdomain for the Return-Path address (default 'send')
19
+ # @option params [String] :tracking_subdomain the custom subdomain used for click and open tracking links
20
+ # @option params [Boolean] :click_tracking track clicks within the body of each HTML email
21
+ # @option params [Boolean] :open_tracking track the open rate of each email
22
+ #
23
+ # https://resend.com/docs/api-reference/domains/claim-domain
24
+ def create(params)
25
+ path = "domains/claim"
26
+ Resend::Request.new(path, params, "post").perform
27
+ end
28
+
29
+ #
30
+ # Retrieve the latest claim for the placeholder domain created by the claim.
31
+ #
32
+ # @param domain_id [String] the ID of the placeholder domain created by the claim
33
+ #
34
+ # https://resend.com/docs/api-reference/domains/get-domain-claim
35
+ def get(domain_id = "")
36
+ path = "domains/#{domain_id}/claim"
37
+ Resend::Request.new(path, {}, "get").perform
38
+ end
39
+
40
+ #
41
+ # Trigger asynchronous DNS verification and ownership transfer for a domain claim.
42
+ # The claim stays 'pending' while verification runs; poll `get` for status. Once
43
+ # 'completed', the transferred domain has new DKIM records that must be added to
44
+ # DNS and verified via Resend::Domains.verify.
45
+ #
46
+ # @param domain_id [String] the ID of the placeholder domain created by the claim
47
+ #
48
+ # https://resend.com/docs/api-reference/domains/verify-domain-claim
49
+ def verify(domain_id = "")
50
+ path = "domains/#{domain_id}/claim/verify"
51
+ Resend::Request.new(path, {}, "post").perform
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Resend
6
+ # Handles multipart/form-data requests (file uploads).
7
+ # Builds the multipart body directly instead of relying on HTTParty's
8
+ # duck-typing file detection, which requires a :path method.
9
+ class MultipartRequest < Request
10
+ NEWLINE = "\r\n"
11
+ private_constant :NEWLINE
12
+
13
+ private
14
+
15
+ def build_request_options
16
+ boundary = SecureRandom.hex(16)
17
+ headers = @headers.merge("Content-Type" => "multipart/form-data; boundary=#{boundary}")
18
+ { headers: headers, body: build_multipart_body(boundary) }
19
+ end
20
+
21
+ def build_multipart_body(boundary)
22
+ body = "".b
23
+
24
+ body << part(boundary, "file", read_file(@body[:file]), filename: "import.csv", content_type: "text/csv")
25
+ optional_fields.each { |name, value| body << part(boundary, name, value) }
26
+ body << "--#{boundary}--#{NEWLINE}".b
27
+
28
+ body
29
+ end
30
+
31
+ def part(boundary, name, value, filename: nil, content_type: nil)
32
+ disposition = %(Content-Disposition: form-data; name="#{name}")
33
+ disposition += %(; filename="#{filename}") if filename
34
+
35
+ header = "--#{boundary}#{NEWLINE}#{disposition}#{NEWLINE}"
36
+ header += "Content-Type: #{content_type}#{NEWLINE}" if content_type
37
+ header += NEWLINE
38
+
39
+ header.b + value.to_s.b + NEWLINE.b
40
+ end
41
+
42
+ def read_file(file_data)
43
+ if file_data.respond_to?(:read)
44
+ content = file_data.read
45
+ file_data.rewind if file_data.respond_to?(:rewind)
46
+ content.b
47
+ else
48
+ file_data.to_s.b
49
+ end
50
+ end
51
+
52
+ def optional_fields
53
+ {
54
+ column_map: @body[:column_map] && serialize_json(@body[:column_map]),
55
+ on_conflict: @body[:on_conflict],
56
+ segments: @body[:segments] && serialize_json(@body[:segments]),
57
+ topics: @body[:topics] && serialize_json(@body[:topics])
58
+ }.compact
59
+ end
60
+
61
+ def serialize_json(value)
62
+ return value if value.is_a?(String)
63
+
64
+ value.to_json
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resend
4
+ # oauth grants api wrapper
5
+ module OAuthGrants
6
+ class << self
7
+ # https://resend.com/docs/api-reference/oauth/list-grants
8
+ def list(params = {})
9
+ path = Resend::PaginationHelper.build_paginated_path("oauth/grants", params)
10
+ Resend::Request.new(path, {}, "get").perform
11
+ end
12
+
13
+ # https://resend.com/docs/api-reference/oauth/revoke-grant
14
+ def revoke(oauth_grant_id = "")
15
+ path = "oauth/grants/#{oauth_grant_id}"
16
+ Resend::Request.new(path, {}, "delete").perform
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Resend
4
- VERSION = "1.4.0"
4
+ VERSION = "1.6.0"
5
5
  end
data/lib/resend.rb CHANGED
@@ -11,6 +11,7 @@ require "resend/errors"
11
11
  require "resend/response"
12
12
  require "resend/client"
13
13
  require "resend/request"
14
+ require "resend/multipart_request"
14
15
  require "resend/pagination_helper"
15
16
 
16
17
  # API Operations
@@ -19,10 +20,12 @@ require "resend/api_keys"
19
20
  require "resend/broadcasts"
20
21
  require "resend/batch"
21
22
  require "resend/contacts"
23
+ require "resend/contacts/imports"
22
24
  require "resend/contacts/segments"
23
25
  require "resend/contacts/topics"
24
26
  require "resend/contact_properties"
25
27
  require "resend/domains"
28
+ require "resend/domains/claims"
26
29
  require "resend/emails"
27
30
  require "resend/templates"
28
31
  require "resend/emails/receiving"
@@ -31,6 +34,7 @@ require "resend/emails/receiving/attachments"
31
34
  require "resend/logs"
32
35
  require "resend/topics"
33
36
  require "resend/webhooks"
37
+ require "resend/oauth_grants"
34
38
  require "resend/automations"
35
39
  require "resend/automations/runs"
36
40
  require "resend/events"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resend
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derich Pacheco
@@ -53,9 +53,11 @@ files:
53
53
  - lib/resend/client.rb
54
54
  - lib/resend/contact_properties.rb
55
55
  - lib/resend/contacts.rb
56
+ - lib/resend/contacts/imports.rb
56
57
  - lib/resend/contacts/segments.rb
57
58
  - lib/resend/contacts/topics.rb
58
59
  - lib/resend/domains.rb
60
+ - lib/resend/domains/claims.rb
59
61
  - lib/resend/emails.rb
60
62
  - lib/resend/emails/attachments.rb
61
63
  - lib/resend/emails/receiving.rb
@@ -64,6 +66,8 @@ files:
64
66
  - lib/resend/events.rb
65
67
  - lib/resend/logs.rb
66
68
  - lib/resend/mailer.rb
69
+ - lib/resend/multipart_request.rb
70
+ - lib/resend/oauth_grants.rb
67
71
  - lib/resend/pagination_helper.rb
68
72
  - lib/resend/railtie.rb
69
73
  - lib/resend/request.rb