mailtrap 2.10.0 → 2.11.1

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: b3a95002cbfb65622fa2d944bf2de1d41dc25a9a7224f8d244982fdf8154776d
4
- data.tar.gz: a228f3805ce687e8f34bd410595f6e39f9e7d73a426d1d06ca2b35ec972b5c69
3
+ metadata.gz: b6606da8d73e2112b813b87000d3aaa63c5490cf2d046dedf59eb5a4f56fc619
4
+ data.tar.gz: a7647dad5291d79758edd52e2b01c57e3e475d998a271ddcd5f6daf24455f59c
5
5
  SHA512:
6
- metadata.gz: fcec8536fae46762bd7289b7b098f45728c9d00b79bcd1386b6e4cb384fa0fcf859136db5cc956480da7aa1cb5063ed8dbee2312ac0e2bf6c8ac4b872443e650
7
- data.tar.gz: 127126f80cded1f05214a173b1c8e78b8acc6c39bdbf1f168b161ca221a5e4230aa11d098f26c8c188c14778a28c579ac0e2bea52bab2713c82068d43836b677
6
+ metadata.gz: e8bca46ba63dfa33175e89b2fcdaba9cce1701d904b6eee25a4808e57524b17dd7f97f483e6289c889fdb6006d35ced29c857beacdc1d2431f8c43a26d878161
7
+ data.tar.gz: 7dbeaaf7198cc263e2ab8264113b0992836a2141745d96edd031f9ff33934649ab4b1cf5bf9a882016a63e243c77d129be61f8f9ab3957b8091019fdb90e2593
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## [2.11.1] - 2026-07-02
2
+
3
+ ## What's Changed
4
+ * Release v2.11.0 by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/109
5
+ * MT-22022: Add webhook signature verification helper by @Rabsztok in https://github.com/mailtrap/mailtrap-ruby/pull/110
6
+ * Add draft-release workflow placeholder by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/111
7
+ * Implement draft-release workflow by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/112
8
+
9
+ ## New Contributors
10
+ * @Rabsztok made their first contribution in https://github.com/mailtrap/mailtrap-ruby/pull/110
11
+
12
+ **Full Changelog**: https://github.com/mailtrap/mailtrap-ruby/compare/v2.11.0...v2.11.1
13
+
14
+ ## [2.11.0] - 2026-05-14
15
+ ## What's Changed
16
+ * Update sandboxes api by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/103
17
+ * Sending domains endpoints by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/104
18
+ * Add contact endpoints by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/105
19
+ * Account management API by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/106
20
+ * Add api token endpoints by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/107
21
+ * Add webhook endpoints by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/108
22
+
23
+
1
24
  ## [2.10.0] - 2026-03-23
2
25
 
3
26
  - Add Email Logs API (list and get email sending logs with filters and cursor pagination)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mailtrap (2.10.0)
4
+ mailtrap (2.11.1)
5
5
  base64
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -8,7 +8,6 @@
8
8
 
9
9
  This client uses API v2, for v1 refer to [this documentation](https://mailtrap.docs.apiary.io/)
10
10
 
11
-
12
11
  ## Prerequisites
13
12
 
14
13
  To get the most out of this official Mailtrap.io Ruby SDK:
@@ -176,9 +175,10 @@ Email API:
176
175
 
177
176
  - Full Email Sending – [`full.rb`](examples/full.rb)
178
177
  - Batch Sending – [`batch.rb`](examples/batch.rb)
179
- - Sending Domains API – [`sending_domains_api.rb`](examples/sending_domains_api.rb)
178
+ - Sending Domains API – [`sending_domains_api.rb`](examples/sending_domains_api.rb)
180
179
  - Sending Stats API – [`stats_api.rb`](examples/stats_api.rb)
181
180
  - Email Logs API – [`email_logs_api.rb`](examples/email_logs_api.rb)
181
+ - Webhooks API – [`webhooks_api.rb`](examples/webhooks_api.rb)
182
182
 
183
183
  Email Sandbox (Testing):
184
184
 
@@ -198,6 +198,7 @@ General:
198
198
  - Billing API – [`billing_api.rb`](examples/billing_api.rb)
199
199
  - Templates API – [`email_templates_api.rb`](examples/email_templates_api.rb)
200
200
  - Action Mailer – [`action_mailer.rb`](examples/action_mailer.rb)
201
+ - Verifying webhook signatures – [`webhooks_signature_verification.ru`](examples/webhooks_signature_verification.ru)
201
202
 
202
203
  ## Migration guide v1 → v2
203
204
 
@@ -212,14 +213,14 @@ Bug reports and pull requests are welcome on [GitHub](https://github.com/railswa
212
213
  ## Development
213
214
 
214
215
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
215
- `rake spec` to run the tests. You can also run `bin/console` for an interactive
216
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
216
217
  prompt that will allow you to experiment.
217
218
 
218
219
  To install this gem onto your local machine, run `bundle exec rake install`.
219
220
 
220
221
  To release a new version, update the version number in `version.rb`, and then
221
222
  run `bundle exec rake release`, which will create a git tag for the version,
222
- push git commits and the created tag, and push the `.gem` file to
223
+ push git commits and the created tag, and push the `.gem` file to
223
224
  [rubygems.org](https://rubygems.org).
224
225
 
225
226
  To run the documentation server, first generate the documentation with
@@ -240,4 +241,4 @@ Everyone interacting in the Mailtrap project's codebases, issue trackers, chat r
240
241
 
241
242
  ## Compatibility with previous releases
242
243
 
243
- Versions of this package up to 2.0.2 were an [unofficial client](https://github.com/vchin/mailtrap-client) developed by [@vchin](https://github.com/vchin). Package version 3 is a completely new package.
244
+ Versions of this package up to 2.0.2 were an [unofficial client](https://github.com/vchin/mailtrap-client) developed by [@vchin](https://github.com/vchin). Package version 3 is a completely new package.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for API Token
5
+ # @attr_reader id [Integer] The API token ID
6
+ # @attr_reader name [String] Token display name
7
+ # @attr_reader last_4_digits [String] Last 4 characters of the token
8
+ # @attr_reader created_by [String] Name of the user or token that created this token
9
+ # @attr_reader expires_at [String, nil] When the token expires (ISO 8601); nil if it does not expire
10
+ # @attr_reader resources [Array<Hash>] Permissions granted to this token
11
+ # @attr_reader token [String, nil] Full token value — only populated by #create and #reset
12
+ ApiToken = Struct.new(
13
+ :id,
14
+ :name,
15
+ :last_4_digits,
16
+ :created_by,
17
+ :expires_at,
18
+ :resources,
19
+ :token,
20
+ keyword_init: true
21
+ )
22
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'api_token'
5
+
6
+ module Mailtrap
7
+ class ApiTokensAPI
8
+ include BaseAPI
9
+
10
+ self.supported_options = %i[name resources].freeze
11
+
12
+ self.response_class = ApiToken
13
+
14
+ # Lists API tokens visible to the current API token
15
+ # @return [Array<ApiToken>] Array of API tokens
16
+ # @!macro api_errors
17
+ def list
18
+ base_list
19
+ end
20
+
21
+ # Retrieves a single API token by ID
22
+ # @param token_id [Integer] The API token ID
23
+ # @return [ApiToken] API token object (the `token` field is nil — full value is only
24
+ # returned by #create and #reset)
25
+ # @!macro api_errors
26
+ def get(token_id)
27
+ base_get(token_id)
28
+ end
29
+
30
+ # Creates a new API token. The full `token` value is returned ONLY ONCE — store it securely.
31
+ # @param [Hash] options The parameters to create
32
+ # @option options [String] :name Display name for the token
33
+ # @option options [Array<Hash>] :resources Permissions to assign
34
+ # - `{ resource_type:, resource_id:, access_level: }`
35
+ # @return [ApiToken] Created token (full `token` value populated)
36
+ # @!macro api_errors
37
+ # @raise [ArgumentError] If invalid options are provided
38
+ def create(options)
39
+ base_create(options)
40
+ end
41
+
42
+ # Expires the requested token and returns a new one with the same permissions.
43
+ # The old token stops working after a short grace period. The new `token` value is
44
+ # returned ONLY ONCE — store it securely
45
+ # @param token_id [Integer] The API token ID
46
+ # @return [ApiToken] New token (full `token` value populated)
47
+ # @!macro api_errors
48
+ def reset(token_id)
49
+ response = client.post("#{base_path}/#{token_id}/reset")
50
+ handle_response(response)
51
+ end
52
+
53
+ # Permanently deletes an API token
54
+ # @param token_id [Integer] The API token ID
55
+ # @return nil
56
+ # @!macro api_errors
57
+ def delete(token_id)
58
+ base_delete(token_id)
59
+ end
60
+
61
+ private
62
+
63
+ def base_path
64
+ "/api/accounts/#{account_id}/api_tokens"
65
+ end
66
+ end
67
+ end
@@ -207,6 +207,20 @@ module Mailtrap
207
207
  )
208
208
  end
209
209
 
210
+ # Performs a PUT request to the specified path
211
+ # @param path [String] The request path
212
+ # @param body [Hash] The request body
213
+ # @return [Hash, String, nil] JSON response or raw response body
214
+ # @!macro api_errors
215
+ def put(path, body = nil)
216
+ perform_request(
217
+ method: :put,
218
+ host: general_api_host,
219
+ path:,
220
+ body:
221
+ )
222
+ end
223
+
210
224
  # Performs a DELETE request to the specified path
211
225
  # @param path [String] The request path
212
226
  # @return [Hash, String, nil] JSON response or raw response body
@@ -261,6 +275,8 @@ module Mailtrap
261
275
  Net::HTTP::Post.new(uri_or_path)
262
276
  when :patch
263
277
  Net::HTTP::Patch.new(uri_or_path)
278
+ when :put
279
+ Net::HTTP::Put.new(uri_or_path)
264
280
  when :delete
265
281
  Net::HTTP::Delete.new(uri_or_path)
266
282
  else
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for Sending Domain Company Info
5
+ # @see https://docs.mailtrap.io/developers/management/sending-domains
6
+ # @attr_reader name [String, nil] Company or individual name
7
+ # @attr_reader address [String, nil] Street address
8
+ # @attr_reader city [String, nil] City
9
+ # @attr_reader country [String, nil] Country
10
+ # @attr_reader phone [String, nil] Phone number
11
+ # @attr_reader zip_code [String, nil] ZIP or postal code
12
+ # @attr_reader privacy_policy_url [String, nil] URL to the privacy policy page
13
+ # @attr_reader terms_of_service_url [String, nil] URL to the terms of service page
14
+ # @attr_reader website_url [String, nil] Company website URL or LinkedIn / personal website
15
+ # @attr_reader info_level [String, nil] Whether the sender is a "business" or "individual"
16
+ CompanyInfo = Struct.new(
17
+ :name,
18
+ :address,
19
+ :city,
20
+ :country,
21
+ :phone,
22
+ :zip_code,
23
+ :privacy_policy_url,
24
+ :terms_of_service_url,
25
+ :website_url,
26
+ :info_level,
27
+ keyword_init: true
28
+ )
29
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'company_info'
5
+
6
+ module Mailtrap
7
+ class CompanyInfoAPI
8
+ include BaseAPI
9
+
10
+ self.supported_options = %i[
11
+ name
12
+ address
13
+ city
14
+ country
15
+ phone
16
+ zip_code
17
+ privacy_policy_url
18
+ terms_of_service_url
19
+ website_url
20
+ info_level
21
+ ].freeze
22
+
23
+ self.response_class = CompanyInfo
24
+
25
+ # Gets company information for a sending domain
26
+ # @param sending_domain_id [Integer] The sending domain ID
27
+ # @return [CompanyInfo] Company information
28
+ # @!macro api_errors
29
+ def get(sending_domain_id)
30
+ response = client.get(base_path(sending_domain_id))
31
+ build_entity(response[:data], CompanyInfo)
32
+ end
33
+
34
+ # Creates company information for a sending domain
35
+ # @param sending_domain_id [Integer] The sending domain ID
36
+ # @param [Hash] options The company info attributes
37
+ # @option options [String] :name Company or individual name
38
+ # @option options [String] :address Street address
39
+ # @option options [String] :city City
40
+ # @option options [String] :country Country
41
+ # @option options [String] :phone Phone number
42
+ # @option options [String] :zip_code ZIP or postal code
43
+ # @option options [String] :privacy_policy_url URL to the privacy policy page
44
+ # @option options [String] :terms_of_service_url URL to the terms of service page
45
+ # @option options [String] :website_url Company website URL
46
+ # @option options [String] :info_level Whether the sender is a "business" or "individual"
47
+ # @return [CompanyInfo] Created company information
48
+ # @!macro api_errors
49
+ # @raise [ArgumentError] If invalid options are provided
50
+ def create(sending_domain_id, options)
51
+ validate_options!(options, supported_options)
52
+ response = client.post(base_path(sending_domain_id), wrap_request(options))
53
+ build_entity(response[:data], CompanyInfo)
54
+ end
55
+
56
+ # Updates company information for a sending domain
57
+ # @param sending_domain_id [Integer] The sending domain ID
58
+ # @param [Hash] options The company info attributes to update
59
+ # @return [CompanyInfo] Updated company information
60
+ # @!macro api_errors
61
+ # @raise [ArgumentError] If invalid options are provided
62
+ def update(sending_domain_id, options)
63
+ validate_options!(options, supported_options)
64
+ response = client.patch(base_path(sending_domain_id), wrap_request(options))
65
+ build_entity(response[:data], CompanyInfo)
66
+ end
67
+
68
+ private
69
+
70
+ def base_path(sending_domain_id)
71
+ "/api/accounts/#{account_id}/sending_domains/#{sending_domain_id}/company_info"
72
+ end
73
+
74
+ def wrap_request(options)
75
+ { company_info: options }
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for Contact Event
5
+ # @attr_reader contact_id [String] The contact ID (UUID)
6
+ # @attr_reader contact_email [String] The contact email
7
+ # @attr_reader name [String] The event name
8
+ # @attr_reader params [Hash] The event parameters (string keys, scalar values)
9
+ ContactEvent = Struct.new(
10
+ :contact_id,
11
+ :contact_email,
12
+ :name,
13
+ :params,
14
+ keyword_init: true
15
+ )
16
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'contact_event'
5
+
6
+ module Mailtrap
7
+ class ContactEventsAPI
8
+ include BaseAPI
9
+
10
+ self.supported_options = %i[name params].freeze
11
+
12
+ self.response_class = ContactEvent
13
+
14
+ # Creates a contact event
15
+ # @param contact_identifier [String] The contact UUID or email address
16
+ # @param [Hash] options The event parameters
17
+ # @option options [String] :name The event name (max 255 characters)
18
+ # @option options [Hash] :params A hash of string keys and scalar values
19
+ # @return [ContactEvent] Created contact event object
20
+ # @!macro api_errors
21
+ # @raise [ArgumentError] If invalid options are provided
22
+ def create(contact_identifier, options)
23
+ validate_options!(options, supported_options)
24
+ response = client.post(base_path(contact_identifier), options)
25
+ build_entity(response, ContactEvent)
26
+ end
27
+
28
+ private
29
+
30
+ def base_path(contact_identifier)
31
+ "/api/accounts/#{account_id}/contacts/#{contact_identifier}/events"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for Contact Export
5
+ # @attr_reader id [Integer] The contact export ID
6
+ # @attr_reader status [String] The export status (created, started, finished)
7
+ # @attr_reader created_at [String] When the export was created
8
+ # @attr_reader updated_at [String] When the export was last updated
9
+ # @attr_reader url [String, nil] URL of the exported file (only when status is "finished")
10
+ ContactExport = Struct.new(
11
+ :id,
12
+ :status,
13
+ :created_at,
14
+ :updated_at,
15
+ :url,
16
+ keyword_init: true
17
+ )
18
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'contact_export'
5
+
6
+ module Mailtrap
7
+ class ContactExportsAPI
8
+ include BaseAPI
9
+
10
+ self.supported_options = %i[filters].freeze
11
+
12
+ self.response_class = ContactExport
13
+
14
+ # Retrieves a specific contact export
15
+ # @param export_id [Integer] The contact export ID
16
+ # @return [ContactExport] Contact export object
17
+ # @!macro api_errors
18
+ def get(export_id)
19
+ base_get(export_id)
20
+ end
21
+
22
+ # Creates a new contact export
23
+ # @param [Hash] options The export parameters
24
+ # @option options [Array<Hash>] :filters Filters to apply to the export
25
+ # - `{ name: 'list_id', operator: 'equal', value: [Integer, ...] }`
26
+ # - `{ name: 'subscription_status', operator: 'equal', value: 'subscribed' | 'unsubscribed' }`
27
+ # @return [ContactExport] Created contact export object
28
+ # @!macro api_errors
29
+ # @raise [ArgumentError] If invalid options are provided
30
+ def create(options)
31
+ base_create(options)
32
+ end
33
+
34
+ private
35
+
36
+ def base_path
37
+ "/api/accounts/#{account_id}/contacts/exports"
38
+ end
39
+ end
40
+ end
@@ -85,6 +85,24 @@ module Mailtrap
85
85
  handle_response(response)
86
86
  end
87
87
 
88
+ # Turn the email address of the inbox on/off
89
+ # @param inbox_id [Integer] The Inbox identifier
90
+ # @return [Inbox] Updated Inbox object
91
+ # @!macro api_errors
92
+ def toggle_email_username(inbox_id)
93
+ response = client.patch("#{base_path}/#{inbox_id}/toggle_email_username")
94
+ handle_response(response)
95
+ end
96
+
97
+ # Reset username of email address per inbox
98
+ # @param inbox_id [Integer] The Inbox identifier
99
+ # @return [Inbox] Updated Inbox object
100
+ # @!macro api_errors
101
+ def reset_email_username(inbox_id)
102
+ response = client.patch("#{base_path}/#{inbox_id}/reset_email_username")
103
+ handle_response(response)
104
+ end
105
+
88
106
  private
89
107
 
90
108
  def wrap_request(options)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for a node in the Permissions resources tree
5
+ # @attr_reader id [Integer] Resource ID
6
+ # @attr_reader name [String] Resource name
7
+ # @attr_reader type [String] Resource type (account, project, inbox, sending_domain, ...)
8
+ # @attr_reader access_level [Integer] The access level the current token has on this resource
9
+ # @attr_reader resources [Array<PermissionResource>] Nested child resources
10
+ PermissionResource = Struct.new(
11
+ :id,
12
+ :name,
13
+ :type,
14
+ :access_level,
15
+ :resources,
16
+ keyword_init: true
17
+ )
18
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'permission_resource'
5
+
6
+ module Mailtrap
7
+ class PermissionsAPI
8
+ include BaseAPI
9
+
10
+ # Bulk-updates user or token permissions on an account access
11
+ # @param account_access_id [Integer] The account access ID
12
+ # @param permissions [Array<Hash>] Array of permission entries
13
+ # - `{ resource_id:, resource_type:, access_level: }` to create or update
14
+ # - `{ resource_id:, resource_type:, _destroy: true }` to remove
15
+ # @return [Hash] API response (e.g. `{ message: 'Permissions have been updated!' }`)
16
+ # @!macro api_errors
17
+ def bulk_update(account_access_id, permissions)
18
+ client.put(
19
+ "#{base_path}/account_accesses/#{account_access_id}/permissions/bulk",
20
+ { permissions: permissions }
21
+ )
22
+ end
23
+
24
+ # Returns the recursive tree of resources the current token can access.
25
+ # Each node carries the token's access_level and any nested child resources.
26
+ # @return [Array<PermissionResource>] Top-level resources, each with nested children
27
+ # @!macro api_errors
28
+ def resources
29
+ response = client.get("#{base_path}/permissions/resources")
30
+ build_resource_tree(response)
31
+ end
32
+
33
+ private
34
+
35
+ def base_path
36
+ "/api/accounts/#{account_id}"
37
+ end
38
+
39
+ def build_resource_tree(items)
40
+ items.map do |item|
41
+ PermissionResource.new(
42
+ id: item[:id],
43
+ name: item[:name],
44
+ type: item[:type],
45
+ access_level: item[:access_level],
46
+ resources: build_resource_tree(Array(item[:resources]))
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
@@ -7,7 +7,7 @@ module Mailtrap
7
7
  class SendingDomainsAPI
8
8
  include BaseAPI
9
9
 
10
- self.supported_options = %i[domain_name]
10
+ self.supported_options = %i[open_tracking_enabled click_tracking_enabled auto_unsubscribe_link_enabled]
11
11
 
12
12
  self.response_class = SendingDomain
13
13
 
@@ -34,7 +34,7 @@ module Mailtrap
34
34
  # @!macro api_errors
35
35
  # @raise [ArgumentError] If invalid options are provided
36
36
  def create(options)
37
- base_create(options)
37
+ base_create(options, %i[domain_name])
38
38
  end
39
39
 
40
40
  # Deletes a sending domain
@@ -45,6 +45,19 @@ module Mailtrap
45
45
  base_delete(domain_id)
46
46
  end
47
47
 
48
+ # Updates configuration settings for a sending domain
49
+ # @param domain_id [Integer] The sending domain ID
50
+ # @param [Hash] options The parameters to update
51
+ # @option options [Boolean] :open_tracking_enabled Enable open tracking for emails sent from this domain
52
+ # @option options [Boolean] :click_tracking_enabled Enable click tracking for links in emails sent from this domain
53
+ # @option options [Boolean] :auto_unsubscribe_link_enabled Automatically add an unsubscribe link to emails
54
+ # @return [SendingDomain] Updated sending domain
55
+ # @!macro api_errors
56
+ # @raise [ArgumentError] If invalid options are provided
57
+ def update(domain_id, options)
58
+ base_update(domain_id, options)
59
+ end
60
+
48
61
  # Email DNS configuration instructions for the sending domain
49
62
  # @param domain_id [Integer] The sending domain ID
50
63
  # @param email [String] The email for instructions
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for an organization sub account
5
+ # @attr_reader id [Integer] The sub account ID
6
+ # @attr_reader name [String] The sub account name
7
+ SubAccount = Struct.new(
8
+ :id,
9
+ :name,
10
+ keyword_init: true
11
+ )
12
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'sub_account'
5
+
6
+ module Mailtrap
7
+ class SubAccountsAPI
8
+ include BaseAPI
9
+
10
+ attr_reader :organization_id
11
+
12
+ self.supported_options = %i[name].freeze
13
+
14
+ self.response_class = SubAccount
15
+
16
+ # @param organization_id [Integer] The organization ID
17
+ # @param client [Mailtrap::Client] The client instance
18
+ # @raise [ArgumentError] If organization_id is nil
19
+ def initialize(organization_id, client = Mailtrap::Client.new)
20
+ raise ArgumentError, 'organization_id is required' if organization_id.nil?
21
+
22
+ @organization_id = organization_id
23
+ @client = client
24
+ end
25
+
26
+ # Lists all sub accounts under the organization
27
+ # @return [Array<SubAccount>] Array of sub accounts
28
+ # @!macro api_errors
29
+ def list
30
+ base_list
31
+ end
32
+
33
+ # Creates a new sub account under the organization
34
+ # @param [Hash] options The parameters to create
35
+ # @option options [String] :name Name of the sub account
36
+ # @return [SubAccount] Created sub account
37
+ # @!macro api_errors
38
+ # @raise [ArgumentError] If invalid options are provided
39
+ def create(options)
40
+ base_create(options)
41
+ end
42
+
43
+ private
44
+
45
+ def base_path
46
+ "/api/organizations/#{organization_id}/sub_accounts"
47
+ end
48
+
49
+ def wrap_request(options)
50
+ { account: options }
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailtrap
4
- VERSION = '2.10.0'
4
+ VERSION = '2.11.1'
5
5
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailtrap
4
+ # Data Transfer Object for Webhook
5
+ # @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/8d553c46c2d33-webhooks
6
+ # @attr_reader id [Integer] The webhook ID
7
+ # @attr_reader url [String] The URL that will receive webhook payloads
8
+ # @attr_reader active [Boolean] Whether the webhook is active
9
+ # @attr_reader webhook_type [String] The type of webhook (`email_sending` or `audit_log`)
10
+ # @attr_reader payload_format [String] The webhook payload format (`json` or `jsonlines`)
11
+ # @attr_reader sending_stream [String, nil] The sending stream (`transactional` or `bulk`).
12
+ # Applicable only for `email_sending` webhooks.
13
+ # @attr_reader domain_id [Integer, nil] The sending domain ID the webhook is scoped to,
14
+ # or nil for all domains. Applicable only for `email_sending` webhooks.
15
+ # @attr_reader event_types [Array<String>] The event types the webhook is subscribed to.
16
+ # Applicable only for `email_sending` webhooks.
17
+ # @attr_reader signing_secret [String, nil] HMAC SHA-256 signing secret. Returned only on creation.
18
+ Webhook = Struct.new(
19
+ :id,
20
+ :url,
21
+ :active,
22
+ :webhook_type,
23
+ :payload_format,
24
+ :sending_stream,
25
+ :domain_id,
26
+ :event_types,
27
+ :signing_secret,
28
+ keyword_init: true
29
+ )
30
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Mailtrap
6
+ # Helpers for working with inbound Mailtrap webhooks.
7
+ #
8
+ # @see https://docs.mailtrap.io/email-api-smtp/advanced/webhooks#verifying-the-signature
9
+ module Webhooks
10
+ # Hex-encoded HMAC-SHA256 signature length.
11
+ SIGNATURE_HEX_LENGTH = 64
12
+
13
+ # Verifies the HMAC-SHA256 signature of a Mailtrap webhook payload.
14
+ #
15
+ # Mailtrap signs every outbound webhook by computing
16
+ # `HMAC-SHA256(signing_secret, raw_request_body)` and sending the lowercase
17
+ # hex digest in the `Mailtrap-Signature` HTTP header. Compute the same
18
+ # digest on your side and compare it in constant time.
19
+ #
20
+ # The comparison is performed with {OpenSSL.fixed_length_secure_compare} to
21
+ # avoid timing side-channels.
22
+ #
23
+ # The method never raises on inputs that could plausibly arrive over the
24
+ # wire (empty strings, wrong-length signatures, non-hex characters, missing
25
+ # secret) — it simply returns `false`. This makes it safe to call directly
26
+ # from a controller without rescuing.
27
+ #
28
+ # @param payload [String] The raw request body, exactly as received.
29
+ # **Do not** parse and re-serialize the JSON — re-encoding may reorder
30
+ # keys or alter whitespace and invalidate the signature.
31
+ # @param signature [String] The value of the `Mailtrap-Signature` HTTP
32
+ # header (lowercase hex string).
33
+ # @param signing_secret [String] The webhook's `signing_secret`, returned
34
+ # by {WebhooksAPI#create} on webhook creation.
35
+ # @return [Boolean] `true` if the signature is valid for the given payload
36
+ # and secret, `false` otherwise.
37
+ def self.verify_signature(payload:, signature:, signing_secret:)
38
+ return false unless [payload, signature, signing_secret].all? { |v| v.is_a?(String) && !v.empty? }
39
+ return false if signature.bytesize != SIGNATURE_HEX_LENGTH
40
+
41
+ expected = OpenSSL::HMAC.hexdigest('SHA256', signing_secret, payload)
42
+
43
+ OpenSSL.fixed_length_secure_compare(expected, signature)
44
+ rescue ArgumentError
45
+ # fixed_length_secure_compare raises ArgumentError on length mismatch
46
+ false
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_api'
4
+ require_relative 'webhook'
5
+
6
+ module Mailtrap
7
+ class WebhooksAPI
8
+ include BaseAPI
9
+
10
+ self.supported_options = %i[url webhook_type active payload_format sending_stream event_types domain_id]
11
+
12
+ self.response_class = Webhook
13
+
14
+ # Lists all webhooks for the account
15
+ # @return [Array<Webhook>] Array of webhooks
16
+ # @!macro api_errors
17
+ def list
18
+ response = client.get(base_path)
19
+ response[:data].map { |item| build_entity(item, response_class) }
20
+ end
21
+
22
+ # Retrieves a specific webhook
23
+ # @param webhook_id [Integer] The webhook ID
24
+ # @return [Webhook] Webhook object
25
+ # @!macro api_errors
26
+ def get(webhook_id)
27
+ base_get(webhook_id)
28
+ end
29
+
30
+ # Creates a new webhook
31
+ # @param [Hash] options The parameters to create
32
+ # @option options [String] :url The URL that will receive webhook payloads
33
+ # @option options [String] :webhook_type The type of webhook (`email_sending` or `audit_log`)
34
+ # @option options [Boolean] :active Whether the webhook is active. Defaults to true.
35
+ # @option options [String] :payload_format Payload format (`json` or `jsonlines`). Defaults to `json`.
36
+ # @option options [String] :sending_stream Sending stream (`transactional` or `bulk`).
37
+ # Required for `email_sending` webhook type.
38
+ # @option options [Array<String>] :event_types Event types to subscribe to.
39
+ # Required for `email_sending` webhook type.
40
+ # @option options [Integer] :domain_id Sending domain ID to scope the webhook to.
41
+ # Applicable only for `email_sending` webhooks.
42
+ # @return [Webhook] Created webhook (includes `signing_secret`)
43
+ # @!macro api_errors
44
+ # @raise [ArgumentError] If invalid options are provided
45
+ def create(options)
46
+ base_create(options)
47
+ end
48
+
49
+ # Updates an existing webhook
50
+ # @param webhook_id [Integer] The webhook ID
51
+ # @param [Hash] options The parameters to update
52
+ # @option options [String] :url The URL that will receive webhook payloads
53
+ # @option options [Boolean] :active Whether the webhook is active
54
+ # @option options [String] :payload_format Payload format (`json` or `jsonlines`)
55
+ # @option options [Array<String>] :event_types Event types to subscribe to.
56
+ # Applicable only for `email_sending` webhooks.
57
+ # @return [Webhook] Updated webhook
58
+ # @!macro api_errors
59
+ # @raise [ArgumentError] If invalid options are provided
60
+ def update(webhook_id, options)
61
+ base_update(webhook_id, options, %i[url active payload_format event_types])
62
+ end
63
+
64
+ # Deletes a webhook
65
+ # @param webhook_id [Integer] The webhook ID
66
+ # @return [Webhook] Deleted webhook
67
+ # @!macro api_errors
68
+ def delete(webhook_id)
69
+ response = client.delete("#{base_path}/#{webhook_id}")
70
+ handle_response(response) if response
71
+ end
72
+
73
+ private
74
+
75
+ def base_path
76
+ "/api/accounts/#{account_id}/webhooks"
77
+ end
78
+
79
+ def wrap_request(options)
80
+ { webhook: options }
81
+ end
82
+
83
+ def handle_response(response)
84
+ build_entity(response[:data], response_class)
85
+ end
86
+ end
87
+ end
data/lib/mailtrap.rb CHANGED
@@ -6,20 +6,28 @@ require_relative 'mailtrap/errors'
6
6
  require_relative 'mailtrap/version'
7
7
  require_relative 'mailtrap/accounts_api'
8
8
  require_relative 'mailtrap/account_accesses_api'
9
+ require_relative 'mailtrap/api_tokens_api'
10
+ require_relative 'mailtrap/permissions_api'
11
+ require_relative 'mailtrap/sub_accounts_api'
9
12
  require_relative 'mailtrap/billing_api'
10
13
  require_relative 'mailtrap/email_templates_api'
11
14
  require_relative 'mailtrap/contacts_api'
12
15
  require_relative 'mailtrap/contact_lists_api'
13
16
  require_relative 'mailtrap/contact_fields_api'
14
17
  require_relative 'mailtrap/contact_imports_api'
18
+ require_relative 'mailtrap/contact_exports_api'
19
+ require_relative 'mailtrap/contact_events_api'
15
20
  require_relative 'mailtrap/suppressions_api'
16
21
  require_relative 'mailtrap/sending_domains_api'
22
+ require_relative 'mailtrap/company_info_api'
17
23
  require_relative 'mailtrap/email_logs_api'
18
24
  require_relative 'mailtrap/projects_api'
19
25
  require_relative 'mailtrap/inboxes_api'
20
26
  require_relative 'mailtrap/sandbox_messages_api'
21
27
  require_relative 'mailtrap/sandbox_attachments_api'
22
28
  require_relative 'mailtrap/stats_api'
29
+ require_relative 'mailtrap/webhooks_api'
30
+ require_relative 'mailtrap/webhooks'
23
31
 
24
32
  module Mailtrap
25
33
  # @!macro api_errors
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailtrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.0
4
+ version: 2.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Railsware Products Studio LLC
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-07-02 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: base64
@@ -49,12 +50,20 @@ files:
49
50
  - lib/mailtrap/action_mailer.rb
50
51
  - lib/mailtrap/action_mailer/delivery_method.rb
51
52
  - lib/mailtrap/action_mailer/railtie.rb
53
+ - lib/mailtrap/api_token.rb
54
+ - lib/mailtrap/api_tokens_api.rb
52
55
  - lib/mailtrap/attachment.rb
53
56
  - lib/mailtrap/base_api.rb
54
57
  - lib/mailtrap/billing_api.rb
55
58
  - lib/mailtrap/billing_usage.rb
56
59
  - lib/mailtrap/client.rb
60
+ - lib/mailtrap/company_info.rb
61
+ - lib/mailtrap/company_info_api.rb
57
62
  - lib/mailtrap/contact.rb
63
+ - lib/mailtrap/contact_event.rb
64
+ - lib/mailtrap/contact_events_api.rb
65
+ - lib/mailtrap/contact_export.rb
66
+ - lib/mailtrap/contact_exports_api.rb
58
67
  - lib/mailtrap/contact_field.rb
59
68
  - lib/mailtrap/contact_fields_api.rb
60
69
  - lib/mailtrap/contact_import.rb
@@ -76,6 +85,8 @@ files:
76
85
  - lib/mailtrap/mail.rb
77
86
  - lib/mailtrap/mail/base.rb
78
87
  - lib/mailtrap/mail/from_template.rb
88
+ - lib/mailtrap/permission_resource.rb
89
+ - lib/mailtrap/permissions_api.rb
79
90
  - lib/mailtrap/project.rb
80
91
  - lib/mailtrap/projects_api.rb
81
92
  - lib/mailtrap/sandbox_attachment.rb
@@ -87,9 +98,14 @@ files:
87
98
  - lib/mailtrap/sending_stat_group.rb
88
99
  - lib/mailtrap/sending_stats.rb
89
100
  - lib/mailtrap/stats_api.rb
101
+ - lib/mailtrap/sub_account.rb
102
+ - lib/mailtrap/sub_accounts_api.rb
90
103
  - lib/mailtrap/suppression.rb
91
104
  - lib/mailtrap/suppressions_api.rb
92
105
  - lib/mailtrap/version.rb
106
+ - lib/mailtrap/webhook.rb
107
+ - lib/mailtrap/webhooks.rb
108
+ - lib/mailtrap/webhooks_api.rb
93
109
  - mailtrap.gemspec
94
110
  homepage: https://github.com/mailtrap/mailtrap-ruby
95
111
  licenses:
@@ -99,6 +115,7 @@ metadata:
99
115
  source_code_uri: https://github.com/mailtrap/mailtrap-ruby
100
116
  changelog_uri: https://github.com/mailtrap/mailtrap-ruby/blob/main/CHANGELOG.md
101
117
  rubygems_mfa_required: 'true'
118
+ post_install_message:
102
119
  rdoc_options: []
103
120
  require_paths:
104
121
  - lib
@@ -113,7 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
130
  - !ruby/object:Gem::Version
114
131
  version: '0'
115
132
  requirements: []
116
- rubygems_version: 4.0.3
133
+ rubygems_version: 3.5.22
134
+ signing_key:
117
135
  specification_version: 4
118
136
  summary: Official mailtrap.io API client
119
137
  test_files: []