From 2b9193f4ac7b0629e30fb0a847ba979d90dc4026 Mon Sep 17 00:00:00 2001 From: Marcin Klocek Date: Fri, 24 Jul 2026 06:18:17 +0200 Subject: [PATCH 1/6] Add inbound v2 fields to email log and sending domain DTOs EmailLogMessage gains rfc_message_id, in_reply_to, references, and thread_id. SendingDomain gains inbound_enabled and inbound_verified. Specs and VCR cassettes for both endpoints updated to cover the new fields. Cassette bodies were hand-edited rather than re-recorded, to avoid churning live resources and to keep the curated email-log fixture stable. --- lib/mailtrap/email_log_message.rb | 8 ++++++++ lib/mailtrap/sending_domain.rb | 4 ++++ ...to_EmailLogMessage_with_events_and_raw_message_url.yml | 2 +- ...sponse_data_to_EmailLogsListResponse_with_messages.yml | 2 +- .../maps_response_data_to_sending_domain_object.yml | 2 +- .../_get/maps_response_data_to_SendingDomain_object.yml | 2 +- spec/mailtrap/email_logs_api_spec.rb | 8 ++++++++ spec/mailtrap/sending_domains_api_spec.rb | 4 ++++ 8 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/mailtrap/email_log_message.rb b/lib/mailtrap/email_log_message.rb index f71f3dc..c595900 100644 --- a/lib/mailtrap/email_log_message.rb +++ b/lib/mailtrap/email_log_message.rb @@ -6,6 +6,10 @@ module Mailtrap # @attr_reader message_id [String] Message UUID # @attr_reader status [String] delivered, not_delivered, enqueued, opted_out # @attr_reader subject [String, nil] Email subject + # @attr_reader rfc_message_id [String, nil] Value of the RFC 5322 Message-ID header + # @attr_reader in_reply_to [String, nil] Value of the RFC 5322 In-Reply-To header + # @attr_reader references [Array] Values of the RFC 5322 References header + # @attr_reader thread_id [String, nil] ID of the inbound thread this message belongs to, if any # @attr_reader from [String] Sender address # @attr_reader to [String] Recipient address # @attr_reader sent_at [String] ISO 8601 timestamp @@ -24,6 +28,10 @@ module Mailtrap :message_id, :status, :subject, + :rfc_message_id, + :in_reply_to, + :references, + :thread_id, :from, :to, :sent_at, diff --git a/lib/mailtrap/sending_domain.rb b/lib/mailtrap/sending_domain.rb index c09f776..4fe72d0 100644 --- a/lib/mailtrap/sending_domain.rb +++ b/lib/mailtrap/sending_domain.rb @@ -17,6 +17,8 @@ module Mailtrap # @attr_reader health_alerts_enabled [Boolean] Whether health alerts are enabled # @attr_reader critical_alerts_enabled [Boolean] Whether critical alerts are enabled # @attr_reader alert_recipient_email [String, nil] The email address for alert recipients + # @attr_reader inbound_enabled [Boolean] Whether inbound email is enabled for this domain + # @attr_reader inbound_verified [Boolean] Whether the domain's inbound MX records are verified # @attr_reader permissions [Hash] The permissions for the sending domain # SendingDomain = Struct.new( @@ -34,6 +36,8 @@ module Mailtrap :health_alerts_enabled, :critical_alerts_enabled, :alert_recipient_email, + :inbound_enabled, + :inbound_verified, :permissions, :created_at, :updated_at, diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_get/maps_response_data_to_EmailLogMessage_with_events_and_raw_message_url.yml b/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_get/maps_response_data_to_EmailLogMessage_with_events_and_raw_message_url.yml index ba683fa..6489490 100644 --- a/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_get/maps_response_data_to_EmailLogMessage_with_events_and_raw_message_url.yml +++ b/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_get/maps_response_data_to_EmailLogMessage_with_events_and_raw_message_url.yml @@ -69,7 +69,7 @@ http_interactions: - h3=":443"; ma=86400 body: encoding: ASCII-8BIT - string: '{"message_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","status":"delivered","subject":"Test + string: '{"message_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","status":"delivered","rfc_message_id":"","in_reply_to":null,"references":[],"thread_id":null,"subject":"Test Subject","from":"sender@example.com","to":"recipient@example.com","sent_at":"2025-01-15T10:30:00Z","client_ip":"192.0.2.1","category":"Test Category","custom_variables":null,"sending_stream":"transactional","sending_domain_id":3938,"template_id":null,"template_variables":null,"raw_message_url":"https://example.com/raw/test-message.eml","events":[{"event_type":"delivery","created_at":"2025-01-15T10:30:01Z","details":{"sending_ip":"192.0.2.2","recipient_mx":"mx.example.com","email_service_provider":"Example Provider"}}],"opens_count":0,"clicks_count":0}' diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_list/maps_response_data_to_EmailLogsListResponse_with_messages.yml b/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_list/maps_response_data_to_EmailLogsListResponse_with_messages.yml index 215b811..efa2e86 100644 --- a/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_list/maps_response_data_to_EmailLogsListResponse_with_messages.yml +++ b/spec/fixtures/vcr_cassettes/Mailtrap_EmailLogsAPI/_list/maps_response_data_to_EmailLogsListResponse_with_messages.yml @@ -68,7 +68,7 @@ http_interactions: - h3=":443"; ma=86400 body: encoding: ASCII-8BIT - string: '{"messages":[{"message_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","status":"delivered","subject":"Test + string: '{"messages":[{"message_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","status":"delivered","rfc_message_id":"","in_reply_to":null,"references":[],"thread_id":null,"subject":"Test Subject","from":"sender@example.com","to":"recipient@example.com","sent_at":"2025-01-15T10:30:00Z","client_ip":"192.0.2.1","category":"Test Category","custom_variables":null,"sending_stream":"transactional","sending_domain_id":3938,"template_id":null,"template_variables":null,"opens_count":0,"clicks_count":0}],"total_count":1,"next_page_cursor":null}' recorded_at: Fri, 13 Mar 2026 13:38:30 GMT diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml index 6d795a8..1ddba50 100644 --- a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml @@ -75,6 +75,6 @@ http_interactions: - h3=":443"; ma=86400 body: encoding: ASCII-8BIT - string: '{"domain_name":"mailtrappio.com","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"demo":false,"id":943758,"dns_records":[{"key":"verification","domain":"mt07.mailtrappio.com","type":"CNAME","value":"smtp.mailtrap.live","status":"missing","name":"mt07"},{"key":"dkim1","domain":"rwmt1._domainkey.mailtrappio.com","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"missing","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.mailtrappio.com","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"missing","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.mailtrappio.com","type":"CNAME","value":"t.mailtrap.live","status":"missing","name":"mt-link"}],"dns_verified":false,"dns_verified_at":"","permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"unverified_dns"}' + string: '{"domain_name":"mailtrappio.com","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"inbound_enabled":false,"inbound_verified":false,"demo":false,"id":943758,"dns_records":[{"key":"verification","domain":"mt07.mailtrappio.com","type":"CNAME","value":"smtp.mailtrap.live","status":"missing","name":"mt07"},{"key":"dkim1","domain":"rwmt1._domainkey.mailtrappio.com","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"missing","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.mailtrappio.com","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"missing","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.mailtrappio.com","type":"CNAME","value":"t.mailtrap.live","status":"missing","name":"mt-link"}],"dns_verified":false,"dns_verified_at":"","permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"unverified_dns"}' recorded_at: Sun, 04 Jan 2026 16:27:07 GMT recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml index 93b9226..bfa206d 100644 --- a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml @@ -75,6 +75,6 @@ http_interactions: - h3=":443"; ma=86400 body: encoding: ASCII-8BIT - string: '{"domain_name":"demomailtrap.co","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"demo":true,"id":617882,"dns_records":[{"key":"verification","domain":"demo.demomailtrap.co","type":"CNAME","value":"smtp.mailtrap.live","status":"pass","name":"demo"},{"key":"dkim1","domain":"rwmt1._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.demomailtrap.co","type":"CNAME","value":"t.mailtrap.live","status":"pass","name":"mt-link"}],"dns_verified":true,"dns_verified_at":"2025-05-21T14:04:52.363Z","permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"demo"}' + string: '{"domain_name":"demomailtrap.co","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"demo":true,"id":617882,"dns_records":[{"key":"verification","domain":"demo.demomailtrap.co","type":"CNAME","value":"smtp.mailtrap.live","status":"pass","name":"demo"},{"key":"dkim1","domain":"rwmt1._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.demomailtrap.co","type":"CNAME","value":"t.mailtrap.live","status":"pass","name":"mt-link"}],"dns_verified":true,"dns_verified_at":"2025-05-21T14:04:52.363Z","inbound_enabled":false,"inbound_verified":false,"permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"demo"}' recorded_at: Sun, 04 Jan 2026 16:24:01 GMT recorded_with: VCR 6.2.0 diff --git a/spec/mailtrap/email_logs_api_spec.rb b/spec/mailtrap/email_logs_api_spec.rb index bf5624b..be97c3c 100644 --- a/spec/mailtrap/email_logs_api_spec.rb +++ b/spec/mailtrap/email_logs_api_spec.rb @@ -17,6 +17,10 @@ message_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', status: 'delivered', subject: 'Test Subject', + rfc_message_id: '', + in_reply_to: nil, + references: [], + thread_id: nil, from: 'sender@example.com', to: 'recipient@example.com', sent_at: '2025-01-15T10:30:00Z', @@ -160,6 +164,10 @@ message_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', status: 'delivered', subject: 'Test Subject', + rfc_message_id: '', + in_reply_to: nil, + references: [], + thread_id: nil, from: 'sender@example.com', to: 'recipient@example.com', sent_at: '2025-01-15T10:30:00Z', diff --git a/spec/mailtrap/sending_domains_api_spec.rb b/spec/mailtrap/sending_domains_api_spec.rb index a6b372b..300f97c 100644 --- a/spec/mailtrap/sending_domains_api_spec.rb +++ b/spec/mailtrap/sending_domains_api_spec.rb @@ -60,6 +60,8 @@ health_alerts_enabled: true, critical_alerts_enabled: true, alert_recipient_email: nil, + inbound_enabled: false, + inbound_verified: false, permissions: { can_read: true, can_update: true, can_destroy: true }, created_at: nil, updated_at: nil @@ -117,6 +119,8 @@ health_alerts_enabled: true, critical_alerts_enabled: true, alert_recipient_email: nil, + inbound_enabled: false, + inbound_verified: false, permissions: { can_read: true, can_update: true, can_destroy: true }, created_at: nil, updated_at: nil From 25d78eaaae825536fd62781c1311acc0f5815b55 Mon Sep 17 00:00:00 2001 From: Marcin Klocek Date: Fri, 24 Jul 2026 07:01:32 +0200 Subject: [PATCH 2/6] Add inbound v2 DTOs Struct DTOs for folders, inboxes, messages (with typed attachments), threads (with typed thread messages), the paginated list responses, and the reply/forward send result. Matches the OpenAPI spec. --- lib/mailtrap/inbound_attachment.rb | 27 +++++++++ lib/mailtrap/inbound_folder.rb | 13 +++++ lib/mailtrap/inbound_inbox.rb | 17 ++++++ lib/mailtrap/inbound_message.rb | 55 +++++++++++++++++++ .../inbound_messages_list_response.rb | 15 +++++ lib/mailtrap/inbound_send_result.rb | 11 ++++ lib/mailtrap/inbound_thread.rb | 37 +++++++++++++ lib/mailtrap/inbound_thread_message.rb | 53 ++++++++++++++++++ lib/mailtrap/inbound_threads_list_response.rb | 15 +++++ 9 files changed, 243 insertions(+) create mode 100644 lib/mailtrap/inbound_attachment.rb create mode 100644 lib/mailtrap/inbound_folder.rb create mode 100644 lib/mailtrap/inbound_inbox.rb create mode 100644 lib/mailtrap/inbound_message.rb create mode 100644 lib/mailtrap/inbound_messages_list_response.rb create mode 100644 lib/mailtrap/inbound_send_result.rb create mode 100644 lib/mailtrap/inbound_thread.rb create mode 100644 lib/mailtrap/inbound_thread_message.rb create mode 100644 lib/mailtrap/inbound_threads_list_response.rb diff --git a/lib/mailtrap/inbound_attachment.rb b/lib/mailtrap/inbound_attachment.rb new file mode 100644 index 0000000..2dac78f --- /dev/null +++ b/lib/mailtrap/inbound_attachment.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for an inbound message attachment + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader attachment_id [String] The attachment ID + # @attr_reader size [Integer, nil] The attachment size in bytes + # @attr_reader filename [String, nil] The attachment filename + # @attr_reader content_type [String, nil] The attachment MIME type + # @attr_reader content_disposition [String, nil] attachment or inline + # @attr_reader content_id [String, nil] The content ID for inline attachments + # @attr_reader download_url [String, nil] Signed URL to download the attachment (only when fetched by ID) + # @attr_reader download_url_expires_at [String, nil] When the download URL expires (only when fetched by ID) + # rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size + InboundAttachment = Struct.new( + :attachment_id, + :size, + :filename, + :content_type, + :content_disposition, + :content_id, + :download_url, + :download_url_expires_at, + keyword_init: true + ) + # rubocop:enable Lint/StructNewOverride +end diff --git a/lib/mailtrap/inbound_folder.rb b/lib/mailtrap/inbound_folder.rb new file mode 100644 index 0000000..f5ea2f0 --- /dev/null +++ b/lib/mailtrap/inbound_folder.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for an inbound folder + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader id [Integer] The folder ID + # @attr_reader name [String] The folder name + InboundFolder = Struct.new( + :id, + :name, + keyword_init: true + ) +end diff --git a/lib/mailtrap/inbound_inbox.rb b/lib/mailtrap/inbound_inbox.rb new file mode 100644 index 0000000..b28a553 --- /dev/null +++ b/lib/mailtrap/inbound_inbox.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for an inbound inbox + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader id [Integer] The inbox ID + # @attr_reader name [String] The inbox name + # @attr_reader address [String] The inbox's inbound address + # @attr_reader domain_id [Integer] The sending domain the inbox is attached to + InboundInbox = Struct.new( + :id, + :name, + :address, + :domain_id, + keyword_init: true + ) +end diff --git a/lib/mailtrap/inbound_message.rb b/lib/mailtrap/inbound_message.rb new file mode 100644 index 0000000..1da64a2 --- /dev/null +++ b/lib/mailtrap/inbound_message.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for an inbound message (summary in list, full details when fetched by ID) + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader id [String] The Mailtrap object ID (not the RFC Message-ID header) + # @attr_reader inbox_id [Integer] The inbox the message was received in + # @attr_reader from [String, nil] Sender address + # @attr_reader to [Array] Recipient addresses + # @attr_reader cc [Array] Cc addresses + # @attr_reader bcc [Array] Bcc addresses + # @attr_reader reply_to [String, nil] Reply-To address + # @attr_reader subject [String, nil] Email subject + # @attr_reader rfc_message_id [String, nil] Value of the RFC 5322 Message-ID header + # @attr_reader in_reply_to [String, nil] Value of the RFC 5322 In-Reply-To header + # @attr_reader references [Array] Values of the RFC 5322 References header + # @attr_reader headers [Hash, nil] Raw message headers + # @attr_reader size [Integer, nil] Total message size in bytes + # @attr_reader html_size [Integer, nil] HTML body size in bytes + # @attr_reader text_size [Integer, nil] Text body size in bytes + # @attr_reader received_at [String] ISO 8601 timestamp when the message was received + # @attr_reader thread_id [String, nil] ID of the thread this message belongs to, if any + # @attr_reader attachments [Array] The message attachments + # @attr_reader raw_message_url [String, nil] Signed URL to download raw .eml (only when fetched by ID) + # @attr_reader raw_message_expires_at [String, nil] When the raw message URL expires (only when fetched by ID) + # @attr_reader html_body [String, nil] Decoded HTML body (only when fetched by ID) + # @attr_reader text_body [String, nil] Decoded text body (only when fetched by ID) + # rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size + InboundMessage = Struct.new( + :id, + :inbox_id, + :from, + :to, + :cc, + :bcc, + :reply_to, + :subject, + :rfc_message_id, + :in_reply_to, + :references, + :headers, + :size, + :html_size, + :text_size, + :received_at, + :thread_id, + :attachments, + :raw_message_url, + :raw_message_expires_at, + :html_body, + :text_body, + keyword_init: true + ) + # rubocop:enable Lint/StructNewOverride +end diff --git a/lib/mailtrap/inbound_messages_list_response.rb b/lib/mailtrap/inbound_messages_list_response.rb new file mode 100644 index 0000000..35352be --- /dev/null +++ b/lib/mailtrap/inbound_messages_list_response.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for a paginated list of inbound messages + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader data [Array] The messages on this page + # @attr_reader total_count [Integer] Total number of messages in the inbox + # @attr_reader last_id [String, nil] Cursor for the next page, or nil if this is the last page + InboundMessagesListResponse = Struct.new( + :data, + :total_count, + :last_id, + keyword_init: true + ) +end diff --git a/lib/mailtrap/inbound_send_result.rb b/lib/mailtrap/inbound_send_result.rb new file mode 100644 index 0000000..17ae181 --- /dev/null +++ b/lib/mailtrap/inbound_send_result.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for the result of a reply, reply-all, or forward. + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader message_ids [Array] UUIDs of the sent messages, one per recipient + InboundSendResult = Struct.new( + :message_ids, + keyword_init: true + ) +end diff --git a/lib/mailtrap/inbound_thread.rb b/lib/mailtrap/inbound_thread.rb new file mode 100644 index 0000000..580e4aa --- /dev/null +++ b/lib/mailtrap/inbound_thread.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for an inbound thread (summary in list, with embedded messages when fetched by ID) + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader id [String] The thread ID + # @attr_reader subject [String, nil] The thread subject + # @attr_reader message_count [Integer] Number of messages in the thread + # @attr_reader size [Integer] Total size of the thread in bytes + # @attr_reader first_message_at [String] ISO 8601 timestamp of the first message + # @attr_reader last_received_at [String, nil] ISO 8601 timestamp of the last received message + # @attr_reader last_sent_at [String, nil] ISO 8601 timestamp of the last sent message + # @attr_reader last_activity_at [String] ISO 8601 timestamp of the last activity + # @attr_reader last_message_id [String, nil] ID of the most recent message + # @attr_reader senders [Array] Distinct sender addresses in the thread + # @attr_reader recipients [Array] Distinct recipient addresses in the thread + # @attr_reader attachments [Array] Attachments across the thread + # @attr_reader messages [Array, nil] The thread's messages (only when fetched by ID) + # rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size + InboundThread = Struct.new( + :id, + :subject, + :message_count, + :size, + :first_message_at, + :last_received_at, + :last_sent_at, + :last_activity_at, + :last_message_id, + :senders, + :recipients, + :attachments, + :messages, + keyword_init: true + ) + # rubocop:enable Lint/StructNewOverride +end diff --git a/lib/mailtrap/inbound_thread_message.rb b/lib/mailtrap/inbound_thread_message.rb new file mode 100644 index 0000000..fe7a702 --- /dev/null +++ b/lib/mailtrap/inbound_thread_message.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for a message inside an inbound thread. + # Only +visibility_status+ and +direction+ are always present; +placeholder+ entries omit the rest, + # and the delivery lifecycle fields are only set on available outbound messages. + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader visibility_status [String] available or placeholder + # @attr_reader direction [String] inbound or outbound + # @attr_reader id [String, nil] The message ID + # @attr_reader message_group_id [String, nil] Groups messages sent together as one logical send + # @attr_reader subject [String, nil] Email subject + # @attr_reader rfc_message_id [String, nil] Value of the RFC 5322 Message-ID header + # @attr_reader in_reply_to [String, nil] Value of the RFC 5322 In-Reply-To header + # @attr_reader references [Array, nil] Values of the RFC 5322 References header + # @attr_reader from [String, nil] Sender address + # @attr_reader to [Array, nil] Recipient addresses + # @attr_reader cc [Array, nil] Cc addresses + # @attr_reader bcc [Array, nil] Bcc addresses + # @attr_reader reply_to [String, nil] Reply-To address + # @attr_reader created_at [String, nil] ISO 8601 timestamp + # @attr_reader email_size [Integer, nil] Message size in bytes + # @attr_reader text_body [String, nil] Decoded text body + # @attr_reader html_body [String, nil] Decoded HTML body + # @attr_reader attachments [Array, nil] The message attachments + # @attr_reader delivery_status [String, nil] Delivery status (outbound messages only) + # @attr_reader delivered_at [String, nil] Delivery timestamp (outbound messages only) + # @attr_reader bounced_at [String, nil] Bounce timestamp (outbound messages only) + InboundThreadMessage = Struct.new( + :visibility_status, + :direction, + :id, + :message_group_id, + :subject, + :rfc_message_id, + :in_reply_to, + :references, + :from, + :to, + :cc, + :bcc, + :reply_to, + :created_at, + :email_size, + :text_body, + :html_body, + :attachments, + :delivery_status, + :delivered_at, + :bounced_at, + keyword_init: true + ) +end diff --git a/lib/mailtrap/inbound_threads_list_response.rb b/lib/mailtrap/inbound_threads_list_response.rb new file mode 100644 index 0000000..1516373 --- /dev/null +++ b/lib/mailtrap/inbound_threads_list_response.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for a paginated list of inbound threads + # @see https://docs.mailtrap.io/developers/inbound + # @attr_reader data [Array] The threads on this page + # @attr_reader total_count [Integer] Total number of threads in the inbox + # @attr_reader last_id [String, nil] Cursor for the next page, or nil if this is the last page + InboundThreadsListResponse = Struct.new( + :data, + :total_count, + :last_id, + keyword_init: true + ) +end From ce3426761f19e07e90b74eeacf1cc76fad41a823 Mon Sep 17 00:00:00 2001 From: Marcin Klocek Date: Fri, 24 Jul 2026 07:04:56 +0200 Subject: [PATCH 3/6] Add inbound v2 API classes InboundFoldersAPI (CRUD), InboundInboxesAPI (folder-scoped CRUD), InboundMessagesAPI (list/get/delete + reply/reply_all/forward) and InboundThreadsAPI (list/get/delete), wired into lib/mailtrap.rb. Inbound is token-scoped, so these override initialize to take just a client (no account_id). Nested attachments and thread messages are built into typed structs, mirroring EmailLogsAPI's event handling. --- lib/mailtrap.rb | 4 + lib/mailtrap/inbound_folders_api.rb | 69 ++++++++++++++++ lib/mailtrap/inbound_inboxes_api.rb | 79 +++++++++++++++++++ lib/mailtrap/inbound_messages_api.rb | 114 +++++++++++++++++++++++++++ lib/mailtrap/inbound_threads_api.rb | 84 ++++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100644 lib/mailtrap/inbound_folders_api.rb create mode 100644 lib/mailtrap/inbound_inboxes_api.rb create mode 100644 lib/mailtrap/inbound_messages_api.rb create mode 100644 lib/mailtrap/inbound_threads_api.rb diff --git a/lib/mailtrap.rb b/lib/mailtrap.rb index 35f1b2c..ff0103d 100644 --- a/lib/mailtrap.rb +++ b/lib/mailtrap.rb @@ -28,6 +28,10 @@ require_relative 'mailtrap/stats_api' require_relative 'mailtrap/webhooks_api' require_relative 'mailtrap/webhooks' +require_relative 'mailtrap/inbound_folders_api' +require_relative 'mailtrap/inbound_inboxes_api' +require_relative 'mailtrap/inbound_messages_api' +require_relative 'mailtrap/inbound_threads_api' module Mailtrap # @!macro api_errors diff --git a/lib/mailtrap/inbound_folders_api.rb b/lib/mailtrap/inbound_folders_api.rb new file mode 100644 index 0000000..2889ccb --- /dev/null +++ b/lib/mailtrap/inbound_folders_api.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'inbound_folder' + +module Mailtrap + class InboundFoldersAPI + include BaseAPI + + self.supported_options = %i[name] + self.response_class = InboundFolder + + # Inbound is scoped to the token's account, so no account_id is required. + # @param client [Mailtrap::Client] The client instance + def initialize(client = Mailtrap::Client.new) + @client = client + end + + # Lists all inbound folders in the account + # @return [Array] Array of folders + # @!macro api_errors + def list + base_list + end + + # Retrieves a specific inbound folder + # @param folder_id [Integer] The folder ID + # @return [InboundFolder] Folder object + # @!macro api_errors + def get(folder_id) + base_get(folder_id) + end + + # Creates a new inbound folder + # @param [Hash] options The parameters to create + # @option options [String] :name The folder name + # @return [InboundFolder] Created folder + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def create(options) + base_create(options) + end + + # Updates an inbound folder + # @param folder_id [Integer] The folder ID + # @param [Hash] options The parameters to update + # @option options [String] :name The folder name + # @return [InboundFolder] Updated folder + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def update(folder_id, options) + base_update(folder_id, options) + end + + # Deletes an inbound folder along with all of its inboxes + # @param folder_id [Integer] The folder ID + # @return nil + # @!macro api_errors + def delete(folder_id) + base_delete(folder_id) + end + + private + + def base_path + '/api/inbound/folders' + end + end +end diff --git a/lib/mailtrap/inbound_inboxes_api.rb b/lib/mailtrap/inbound_inboxes_api.rb new file mode 100644 index 0000000..a76ef9c --- /dev/null +++ b/lib/mailtrap/inbound_inboxes_api.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'inbound_inbox' + +module Mailtrap + class InboundInboxesAPI + include BaseAPI + + self.response_class = InboundInbox + + CREATE_OPTIONS = %i[name domain_id].freeze + UPDATE_OPTIONS = %i[name].freeze + + # Inbound is scoped to the token's account, so no account_id is required. + # @param client [Mailtrap::Client] The client instance + def initialize(client = Mailtrap::Client.new) + @client = client + end + + # Lists all inboxes in a folder + # @param folder_id [Integer] The folder ID + # @return [Array] Array of inboxes + # @!macro api_errors + def list(folder_id) + client.get(inboxes_path(folder_id)).map { |item| handle_response(item) } + end + + # Retrieves a specific inbox + # @param folder_id [Integer] The folder ID + # @param inbox_id [Integer] The inbox ID + # @return [InboundInbox] Inbox object + # @!macro api_errors + def get(folder_id, inbox_id) + handle_response(client.get("#{inboxes_path(folder_id)}/#{inbox_id}")) + end + + # Creates a new inbox in a folder + # @param folder_id [Integer] The folder ID + # @param [Hash] options The parameters to create + # @option options [String] :name The inbox name + # @option options [Integer] :domain_id Attach to a custom domain; omit for a Mailtrap-hosted inbox + # @return [InboundInbox] Created inbox + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def create(folder_id, options) + validate_options!(options, CREATE_OPTIONS) + handle_response(client.post(inboxes_path(folder_id), options)) + end + + # Updates an inbox + # @param folder_id [Integer] The folder ID + # @param inbox_id [Integer] The inbox ID + # @param [Hash] options The parameters to update + # @option options [String] :name The inbox name + # @return [InboundInbox] Updated inbox + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def update(folder_id, inbox_id, options) + validate_options!(options, UPDATE_OPTIONS) + handle_response(client.patch("#{inboxes_path(folder_id)}/#{inbox_id}", options)) + end + + # Deletes an inbox + # @param folder_id [Integer] The folder ID + # @param inbox_id [Integer] The inbox ID + # @return nil + # @!macro api_errors + def delete(folder_id, inbox_id) + client.delete("#{inboxes_path(folder_id)}/#{inbox_id}") + end + + private + + def inboxes_path(folder_id) + "/api/inbound/folders/#{folder_id}/inboxes" + end + end +end diff --git a/lib/mailtrap/inbound_messages_api.rb b/lib/mailtrap/inbound_messages_api.rb new file mode 100644 index 0000000..7c3774f --- /dev/null +++ b/lib/mailtrap/inbound_messages_api.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'inbound_message' +require_relative 'inbound_attachment' +require_relative 'inbound_messages_list_response' +require_relative 'inbound_send_result' + +module Mailtrap + class InboundMessagesAPI + include BaseAPI + + self.response_class = InboundMessage + + SEND_OPTIONS = %i[from to cc bcc reply_to text html category attachments headers custom_variables].freeze + + # Inbound is scoped to the token's account, so no account_id is required. + # @param client [Mailtrap::Client] The client instance + def initialize(client = Mailtrap::Client.new) + @client = client + end + + # Lists messages in an inbox (cursor-paginated) + # @param inbox_id [Integer] The inbox ID + # @param last_id [String, nil] Cursor from the previous response's +last_id+ for the next page + # @return [InboundMessagesListResponse] data, total_count, and last_id + # @!macro api_errors + def list(inbox_id, last_id: nil) + query_params = last_id ? { last_id: } : {} + response = client.get(messages_path(inbox_id), query_params) + + InboundMessagesListResponse.new( + data: Array(response[:data]).map { |item| build_message(item) }, + total_count: response[:total_count], + last_id: response[:last_id] + ) + end + + # Fetches a single message with its full body and attachment download URLs + # @param inbox_id [Integer] The inbox ID + # @param message_id [String] The message ID + # @return [InboundMessage] Message details + # @!macro api_errors + def get(inbox_id, message_id) + build_message(client.get("#{messages_path(inbox_id)}/#{message_id}")) + end + + # Deletes a message + # @param inbox_id [Integer] The inbox ID + # @param message_id [String] The message ID + # @return nil + # @!macro api_errors + def delete(inbox_id, message_id) + client.delete("#{messages_path(inbox_id)}/#{message_id}") + end + + # Replies to a message (sends to the original sender) + # @param inbox_id [Integer] The inbox ID + # @param message_id [String] The message ID + # @param [Hash] options The send parameters (see {SEND_OPTIONS}) + # @return [InboundSendResult] The sent message IDs + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def reply(inbox_id, message_id, options) + send_action(inbox_id, message_id, 'reply', options) + end + + # Replies to a message and copies the original's other recipients + # @param (see #reply) + # @return [InboundSendResult] The sent message IDs + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def reply_all(inbox_id, message_id, options) + send_action(inbox_id, message_id, 'reply_all', options) + end + + # Forwards a message to new recipients + # @param (see #reply) + # @return [InboundSendResult] The sent message IDs + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided, or if no +to+ recipient is given + def forward(inbox_id, message_id, options) + raise ArgumentError, 'to is required for forward' if Array(options[:to]).empty? + + send_action(inbox_id, message_id, 'forward', options) + end + + private + + def messages_path(inbox_id) + "/api/inbound/inboxes/#{inbox_id}/messages" + end + + def send_action(inbox_id, message_id, action, options) + validate_options!(options, SEND_OPTIONS) + response = client.post("#{messages_path(inbox_id)}/#{message_id}/#{action}", options) + + InboundSendResult.new(message_ids: response[:message_ids]) + end + + def build_message(hash) + attrs = hash.slice(*InboundMessage.members) + attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments] + + InboundMessage.new(**attrs) + end + + def build_attachments(attachments) + Array(attachments).map do |attachment| + InboundAttachment.new(**attachment.slice(*InboundAttachment.members)) + end + end + end +end diff --git a/lib/mailtrap/inbound_threads_api.rb b/lib/mailtrap/inbound_threads_api.rb new file mode 100644 index 0000000..92ab890 --- /dev/null +++ b/lib/mailtrap/inbound_threads_api.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'inbound_thread' +require_relative 'inbound_thread_message' +require_relative 'inbound_attachment' +require_relative 'inbound_threads_list_response' + +module Mailtrap + class InboundThreadsAPI + include BaseAPI + + self.response_class = InboundThread + + # Inbound is scoped to the token's account, so no account_id is required. + # @param client [Mailtrap::Client] The client instance + def initialize(client = Mailtrap::Client.new) + @client = client + end + + # Lists threads in an inbox (cursor-paginated) + # @param inbox_id [Integer] The inbox ID + # @param last_id [String, nil] Cursor from the previous response's +last_id+ for the next page + # @return [InboundThreadsListResponse] data, total_count, and last_id + # @!macro api_errors + def list(inbox_id, last_id: nil) + query_params = last_id ? { last_id: } : {} + response = client.get(threads_path(inbox_id), query_params) + + InboundThreadsListResponse.new( + data: Array(response[:data]).map { |item| build_thread(item) }, + total_count: response[:total_count], + last_id: response[:last_id] + ) + end + + # Fetches a single thread with its messages embedded (oldest first) + # @param inbox_id [Integer] The inbox ID + # @param thread_id [String] The thread ID + # @return [InboundThread] Thread with messages + # @!macro api_errors + def get(inbox_id, thread_id) + build_thread(client.get("#{threads_path(inbox_id)}/#{thread_id}")) + end + + # Deletes a thread + # @param inbox_id [Integer] The inbox ID + # @param thread_id [String] The thread ID + # @return nil + # @!macro api_errors + def delete(inbox_id, thread_id) + client.delete("#{threads_path(inbox_id)}/#{thread_id}") + end + + private + + def threads_path(inbox_id) + "/api/inbound/inboxes/#{inbox_id}/threads" + end + + def build_thread(hash) + attrs = hash.slice(*InboundThread.members) + attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments] + attrs[:messages] = build_messages(attrs[:messages]) if attrs[:messages] + + InboundThread.new(**attrs) + end + + def build_messages(messages) + Array(messages).map do |message| + attrs = message.slice(*InboundThreadMessage.members) + attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments] + + InboundThreadMessage.new(**attrs) + end + end + + def build_attachments(attachments) + Array(attachments).map do |attachment| + InboundAttachment.new(**attachment.slice(*InboundAttachment.members)) + end + end + end +end From afe4039413a70d1320ff7195d2377d608db5dc76 Mon Sep 17 00:00:00 2001 From: Marcin Klocek Date: Fri, 24 Jul 2026 07:11:32 +0200 Subject: [PATCH 4/6] Add inbound v2 specs Offline DTO specs for all inbound structs, and VCR-based API specs for folders, inboxes, messages and threads (happy paths plus ArgumentError and Mailtrap::Error cases). Cassettes to be recorded against the live API. --- spec/mailtrap/inbound_attachment_spec.rb | 30 ++++++ spec/mailtrap/inbound_folder_spec.rb | 11 +++ spec/mailtrap/inbound_folders_api_spec.rb | 91 +++++++++++++++++ spec/mailtrap/inbound_inbox_spec.rb | 23 +++++ spec/mailtrap/inbound_inboxes_api_spec.rb | 92 +++++++++++++++++ spec/mailtrap/inbound_message_spec.rb | 35 +++++++ spec/mailtrap/inbound_messages_api_spec.rb | 98 +++++++++++++++++++ .../inbound_messages_list_response_spec.rb | 13 +++ spec/mailtrap/inbound_send_result_spec.rb | 15 +++ spec/mailtrap/inbound_thread_message_spec.rb | 27 +++++ spec/mailtrap/inbound_thread_spec.rb | 35 +++++++ spec/mailtrap/inbound_threads_api_spec.rb | 61 ++++++++++++ .../inbound_threads_list_response_spec.rb | 13 +++ 13 files changed, 544 insertions(+) create mode 100644 spec/mailtrap/inbound_attachment_spec.rb create mode 100644 spec/mailtrap/inbound_folder_spec.rb create mode 100644 spec/mailtrap/inbound_folders_api_spec.rb create mode 100644 spec/mailtrap/inbound_inbox_spec.rb create mode 100644 spec/mailtrap/inbound_inboxes_api_spec.rb create mode 100644 spec/mailtrap/inbound_message_spec.rb create mode 100644 spec/mailtrap/inbound_messages_api_spec.rb create mode 100644 spec/mailtrap/inbound_messages_list_response_spec.rb create mode 100644 spec/mailtrap/inbound_send_result_spec.rb create mode 100644 spec/mailtrap/inbound_thread_message_spec.rb create mode 100644 spec/mailtrap/inbound_thread_spec.rb create mode 100644 spec/mailtrap/inbound_threads_api_spec.rb create mode 100644 spec/mailtrap/inbound_threads_list_response_spec.rb diff --git a/spec/mailtrap/inbound_attachment_spec.rb b/spec/mailtrap/inbound_attachment_spec.rb new file mode 100644 index 0000000..e493508 --- /dev/null +++ b/spec/mailtrap/inbound_attachment_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundAttachment do + describe '#initialize' do + subject(:attachment) do + described_class.new( + attachment_id: 'att-1', + size: 8192, + filename: 'report.pdf', + content_type: 'application/pdf', + content_disposition: 'attachment', + content_id: nil, + download_url: 'https://example.com/att-1', + download_url_expires_at: '2026-01-15T10:30:00Z' + ) + end + + it 'creates an attachment with all attributes' do + expect(attachment).to have_attributes( + attachment_id: 'att-1', + size: 8192, + filename: 'report.pdf', + content_type: 'application/pdf', + content_disposition: 'attachment', + download_url: 'https://example.com/att-1', + download_url_expires_at: '2026-01-15T10:30:00Z' + ) + end + end +end diff --git a/spec/mailtrap/inbound_folder_spec.rb b/spec/mailtrap/inbound_folder_spec.rb new file mode 100644 index 0000000..ec1090c --- /dev/null +++ b/spec/mailtrap/inbound_folder_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundFolder do + describe '#initialize' do + subject(:folder) { described_class.new(id: 1, name: 'Support') } + + it 'creates a folder with all attributes' do + expect(folder).to have_attributes(id: 1, name: 'Support') + end + end +end diff --git a/spec/mailtrap/inbound_folders_api_spec.rb b/spec/mailtrap/inbound_folders_api_spec.rb new file mode 100644 index 0000000..4bf298a --- /dev/null +++ b/spec/mailtrap/inbound_folders_api_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundFoldersAPI, :vcr do + subject(:folders_api) { described_class.new(client) } + + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } + + describe '#list' do + subject(:list) { folders_api.list } + + it 'maps response data to InboundFolder objects' do + expect(list).to all(be_a(Mailtrap::InboundFolder)) + end + end + + describe '#get' do + subject(:get) { folders_api.get(folder_id) } + + let(:folder_id) { 1 } + + it 'maps response data to an InboundFolder object' do + expect(get).to be_a(Mailtrap::InboundFolder) + expect(get).to have_attributes(id: folder_id, name: be_a(String)) + end + + context 'when the folder does not exist' do + let(:folder_id) { -1 } + + it 'raises an error' do + expect { get }.to raise_error(Mailtrap::Error) + end + end + end + + describe '#create' do + subject(:create) { folders_api.create(options) } + + let(:options) { { name: 'Support' } } + + it 'maps response data to an InboundFolder object' do + expect(create).to be_a(Mailtrap::InboundFolder) + expect(create).to have_attributes(name: 'Support') + end + + context 'when invalid options are provided' do + let(:options) { { unsupported: 'value' } } + + it 'raises ArgumentError' do + expect { create }.to raise_error(ArgumentError) + end + end + end + + describe '#update' do + subject(:update) { folders_api.update(folder_id, options) } + + let(:folder_id) { 1 } + let(:options) { { name: 'Customer Success' } } + + it 'maps response data to an InboundFolder object' do + expect(update).to be_a(Mailtrap::InboundFolder) + expect(update).to have_attributes(id: folder_id, name: 'Customer Success') + end + + context 'when invalid options are provided' do + let(:options) { { unsupported: 'value' } } + + it 'raises ArgumentError' do + expect { update }.to raise_error(ArgumentError) + end + end + end + + describe '#delete' do + subject(:delete) { folders_api.delete(folder_id) } + + let(:folder_id) { 1 } + + it 'returns nil' do + expect(delete).to be_nil + end + + context 'when the folder does not exist' do + let(:folder_id) { -1 } + + it 'raises an error' do + expect { delete }.to raise_error(Mailtrap::Error) + end + end + end +end diff --git a/spec/mailtrap/inbound_inbox_spec.rb b/spec/mailtrap/inbound_inbox_spec.rb new file mode 100644 index 0000000..ee9be58 --- /dev/null +++ b/spec/mailtrap/inbound_inbox_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundInbox do + describe '#initialize' do + subject(:inbox) do + described_class.new( + id: 42, + name: 'Tickets', + address: 'tickets-1a2b3c4d@inbound-mailtrap.io', + domain_id: 892 + ) + end + + it 'creates an inbox with all attributes' do + expect(inbox).to have_attributes( + id: 42, + name: 'Tickets', + address: 'tickets-1a2b3c4d@inbound-mailtrap.io', + domain_id: 892 + ) + end + end +end diff --git a/spec/mailtrap/inbound_inboxes_api_spec.rb b/spec/mailtrap/inbound_inboxes_api_spec.rb new file mode 100644 index 0000000..16cd831 --- /dev/null +++ b/spec/mailtrap/inbound_inboxes_api_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundInboxesAPI, :vcr do + subject(:inboxes_api) { described_class.new(client) } + + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } + let(:folder_id) { 1 } + + describe '#list' do + subject(:list) { inboxes_api.list(folder_id) } + + it 'maps response data to InboundInbox objects' do + expect(list).to all(be_a(Mailtrap::InboundInbox)) + end + end + + describe '#get' do + subject(:get) { inboxes_api.get(folder_id, inbox_id) } + + let(:inbox_id) { 42 } + + it 'maps response data to an InboundInbox object' do + expect(get).to be_a(Mailtrap::InboundInbox) + expect(get).to have_attributes(id: inbox_id, name: be_a(String), address: be_a(String)) + end + + context 'when the inbox does not exist' do + let(:inbox_id) { -1 } + + it 'raises an error' do + expect { get }.to raise_error(Mailtrap::Error) + end + end + end + + describe '#create' do + subject(:create) { inboxes_api.create(folder_id, options) } + + let(:options) { { name: 'Tickets' } } + + it 'maps response data to an InboundInbox object' do + expect(create).to be_a(Mailtrap::InboundInbox) + expect(create).to have_attributes(name: 'Tickets') + end + + context 'when invalid options are provided' do + let(:options) { { unsupported: 'value' } } + + it 'raises ArgumentError' do + expect { create }.to raise_error(ArgumentError) + end + end + end + + describe '#update' do + subject(:update) { inboxes_api.update(folder_id, inbox_id, options) } + + let(:inbox_id) { 42 } + let(:options) { { name: 'Tickets (renamed)' } } + + it 'maps response data to an InboundInbox object' do + expect(update).to be_a(Mailtrap::InboundInbox) + expect(update).to have_attributes(id: inbox_id, name: 'Tickets (renamed)') + end + + context 'when invalid options are provided' do + let(:options) { { unsupported: 'value' } } + + it 'raises ArgumentError' do + expect { update }.to raise_error(ArgumentError) + end + end + end + + describe '#delete' do + subject(:delete) { inboxes_api.delete(folder_id, inbox_id) } + + let(:inbox_id) { 42 } + + it 'returns nil' do + expect(delete).to be_nil + end + + context 'when the inbox does not exist' do + let(:inbox_id) { -1 } + + it 'raises an error' do + expect { delete }.to raise_error(Mailtrap::Error) + end + end + end +end diff --git a/spec/mailtrap/inbound_message_spec.rb b/spec/mailtrap/inbound_message_spec.rb new file mode 100644 index 0000000..9dc85db --- /dev/null +++ b/spec/mailtrap/inbound_message_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundMessage do + describe '#initialize' do + subject(:message) do + described_class.new( + id: '1700000000000123', + inbox_id: 42, + from: 'customer@example.com', + subject: 'Re: Question', + rfc_message_id: '', + in_reply_to: '', + references: [''], + received_at: '2026-01-15T10:30:00Z', + thread_id: '1700000000000124', + attachments: [] + ) + end + + it 'creates a message with all attributes' do + expect(message).to have_attributes( + id: '1700000000000123', + inbox_id: 42, + from: 'customer@example.com', + subject: 'Re: Question', + rfc_message_id: '', + in_reply_to: '', + references: [''], + received_at: '2026-01-15T10:30:00Z', + thread_id: '1700000000000124', + attachments: [] + ) + end + end +end diff --git a/spec/mailtrap/inbound_messages_api_spec.rb b/spec/mailtrap/inbound_messages_api_spec.rb new file mode 100644 index 0000000..1a983dc --- /dev/null +++ b/spec/mailtrap/inbound_messages_api_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundMessagesAPI, :vcr do + subject(:messages_api) { described_class.new(client) } + + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } + let(:inbox_id) { 42 } + let(:message_id) { '1700000000000123' } + + describe '#list' do + subject(:list) { messages_api.list(inbox_id) } + + it 'maps response data to an InboundMessagesListResponse' do + expect(list).to be_a(Mailtrap::InboundMessagesListResponse) + expect(list.data).to all(be_a(Mailtrap::InboundMessage)) + expect(list).to have_attributes(total_count: be_a(Integer)) + end + + context 'with pagination cursor' do + subject(:list) { messages_api.list(inbox_id, last_id: '1700000000000000') } + + it 'passes last_id and returns a page' do + expect(list).to be_a(Mailtrap::InboundMessagesListResponse) + end + end + end + + describe '#get' do + subject(:get) { messages_api.get(inbox_id, message_id) } + + it 'maps response data to an InboundMessage with attachments' do + expect(get).to be_a(Mailtrap::InboundMessage) + expect(get).to have_attributes(id: message_id, inbox_id: inbox_id) + expect(get.attachments).to all(be_a(Mailtrap::InboundAttachment)) + end + + context 'when the message does not exist' do + let(:message_id) { 'does-not-exist' } + + it 'raises an error' do + expect { get }.to raise_error(Mailtrap::Error) + end + end + end + + describe '#delete' do + subject(:delete) { messages_api.delete(inbox_id, message_id) } + + it 'returns nil' do + expect(delete).to be_nil + end + end + + describe '#reply' do + subject(:reply) { messages_api.reply(inbox_id, message_id, options) } + + let(:options) { { text: 'Thanks for reaching out.' } } + + it 'maps response data to an InboundSendResult' do + expect(reply).to be_a(Mailtrap::InboundSendResult) + expect(reply.message_ids).to all(be_a(String)) + end + + context 'when invalid options are provided' do + let(:options) { { unsupported: 'value' } } + + it 'raises ArgumentError' do + expect { reply }.to raise_error(ArgumentError) + end + end + end + + describe '#reply_all' do + subject(:reply_all) { messages_api.reply_all(inbox_id, message_id, { text: 'Looping everyone in.' }) } + + it 'maps response data to an InboundSendResult' do + expect(reply_all).to be_a(Mailtrap::InboundSendResult) + end + end + + describe '#forward' do + subject(:forward) { messages_api.forward(inbox_id, message_id, options) } + + let(:options) { { to: [{ email: 'colleague@example.com' }], text: 'Please take a look.' } } + + it 'maps response data to an InboundSendResult' do + expect(forward).to be_a(Mailtrap::InboundSendResult) + end + + context 'when no recipient is given' do + let(:options) { { text: 'Please take a look.' } } + + it 'raises ArgumentError' do + expect { forward }.to raise_error(ArgumentError, /to is required/) + end + end + end +end diff --git a/spec/mailtrap/inbound_messages_list_response_spec.rb b/spec/mailtrap/inbound_messages_list_response_spec.rb new file mode 100644 index 0000000..e15445e --- /dev/null +++ b/spec/mailtrap/inbound_messages_list_response_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundMessagesListResponse do + describe '#initialize' do + subject(:response) do + described_class.new(data: [], total_count: 0, last_id: nil) + end + + it 'creates a list response with all attributes' do + expect(response).to have_attributes(data: [], total_count: 0, last_id: nil) + end + end +end diff --git a/spec/mailtrap/inbound_send_result_spec.rb b/spec/mailtrap/inbound_send_result_spec.rb new file mode 100644 index 0000000..b99a54e --- /dev/null +++ b/spec/mailtrap/inbound_send_result_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundSendResult do + describe '#initialize' do + subject(:result) do + described_class.new(message_ids: ['1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d']) + end + + it 'creates a send result with all attributes' do + expect(result).to have_attributes( + message_ids: ['1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d'] + ) + end + end +end diff --git a/spec/mailtrap/inbound_thread_message_spec.rb b/spec/mailtrap/inbound_thread_message_spec.rb new file mode 100644 index 0000000..38c288a --- /dev/null +++ b/spec/mailtrap/inbound_thread_message_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundThreadMessage do + describe '#initialize' do + subject(:thread_message) do + described_class.new( + visibility_status: 'available', + direction: 'outbound', + id: '1700000000000125', + subject: 'Re: Question', + delivery_status: 'delivered', + delivered_at: '2026-01-15T10:31:00Z' + ) + end + + it 'creates a thread message with all attributes' do + expect(thread_message).to have_attributes( + visibility_status: 'available', + direction: 'outbound', + id: '1700000000000125', + subject: 'Re: Question', + delivery_status: 'delivered', + delivered_at: '2026-01-15T10:31:00Z' + ) + end + end +end diff --git a/spec/mailtrap/inbound_thread_spec.rb b/spec/mailtrap/inbound_thread_spec.rb new file mode 100644 index 0000000..154db68 --- /dev/null +++ b/spec/mailtrap/inbound_thread_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundThread do + describe '#initialize' do + subject(:thread) do + described_class.new( + id: '1700000000000124', + subject: 'Question', + message_count: 2, + size: 16_384, + first_message_at: '2026-01-15T10:00:00Z', + last_activity_at: '2026-01-15T10:31:00Z', + senders: ['customer@example.com'], + recipients: ['support@company.com'], + attachments: [], + messages: [] + ) + end + + it 'creates a thread with all attributes' do + expect(thread).to have_attributes( + id: '1700000000000124', + subject: 'Question', + message_count: 2, + size: 16_384, + first_message_at: '2026-01-15T10:00:00Z', + last_activity_at: '2026-01-15T10:31:00Z', + senders: ['customer@example.com'], + recipients: ['support@company.com'], + attachments: [], + messages: [] + ) + end + end +end diff --git a/spec/mailtrap/inbound_threads_api_spec.rb b/spec/mailtrap/inbound_threads_api_spec.rb new file mode 100644 index 0000000..fceba45 --- /dev/null +++ b/spec/mailtrap/inbound_threads_api_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundThreadsAPI, :vcr do + subject(:threads_api) { described_class.new(client) } + + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } + let(:inbox_id) { 42 } + let(:thread_id) { '1700000000000124' } + + describe '#list' do + subject(:list) { threads_api.list(inbox_id) } + + it 'maps response data to an InboundThreadsListResponse' do + expect(list).to be_a(Mailtrap::InboundThreadsListResponse) + expect(list.data).to all(be_a(Mailtrap::InboundThread)) + expect(list).to have_attributes(total_count: be_a(Integer)) + end + + context 'with pagination cursor' do + subject(:list) { threads_api.list(inbox_id, last_id: 'abc123') } + + it 'passes last_id and returns a page' do + expect(list).to be_a(Mailtrap::InboundThreadsListResponse) + end + end + end + + describe '#get' do + subject(:get) { threads_api.get(inbox_id, thread_id) } + + it 'maps response data to an InboundThread with messages' do + expect(get).to be_a(Mailtrap::InboundThread) + expect(get).to have_attributes(id: thread_id) + expect(get.messages).to all(be_a(Mailtrap::InboundThreadMessage)) + end + + context 'when the thread does not exist' do + let(:thread_id) { 'does-not-exist' } + + it 'raises an error' do + expect { get }.to raise_error(Mailtrap::Error) + end + end + end + + describe '#delete' do + subject(:delete) { threads_api.delete(inbox_id, thread_id) } + + it 'returns nil' do + expect(delete).to be_nil + end + + context 'when the thread does not exist' do + let(:thread_id) { 'does-not-exist' } + + it 'raises an error' do + expect { delete }.to raise_error(Mailtrap::Error) + end + end + end +end diff --git a/spec/mailtrap/inbound_threads_list_response_spec.rb b/spec/mailtrap/inbound_threads_list_response_spec.rb new file mode 100644 index 0000000..c69103b --- /dev/null +++ b/spec/mailtrap/inbound_threads_list_response_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::InboundThreadsListResponse do + describe '#initialize' do + subject(:response) do + described_class.new(data: [], total_count: 0, last_id: nil) + end + + it 'creates a list response with all attributes' do + expect(response).to have_attributes(data: [], total_count: 0, last_id: nil) + end + end +end From c28fbe20c28a0bbcb02550d2e6ab359b9e175bee Mon Sep 17 00:00:00 2001 From: Marcin Klocek Date: Fri, 24 Jul 2026 08:29:27 +0200 Subject: [PATCH 5/6] Record inbound API VCR cassettes Cassettes recorded against the live API for folders, inboxes, messages and threads, with the spec let IDs pointed at real recorded resources. Response bodies anonymised: the sending domain is replaced with example.com and the live inbox addresses with non-real slugs; the public inbound-mailtrap.io domain and resource IDs are kept, consistent with the existing cassettes. --- ...sponse_data_to_an_InboundFolder_object.yml | 73 +++++++++++++++++ .../_delete/returns_nil.yml | 65 +++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ...sponse_data_to_an_InboundFolder_object.yml | 73 +++++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ...response_data_to_InboundFolder_objects.yml | 73 +++++++++++++++++ ...sponse_data_to_an_InboundFolder_object.yml | 73 +++++++++++++++++ ...esponse_data_to_an_InboundInbox_object.yml | 73 +++++++++++++++++ .../_delete/returns_nil.yml | 65 +++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ...esponse_data_to_an_InboundInbox_object.yml | 73 +++++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ..._response_data_to_InboundInbox_objects.yml | 73 +++++++++++++++++ ...esponse_data_to_an_InboundInbox_object.yml | 73 +++++++++++++++++ .../_delete/returns_nil.yml | 65 +++++++++++++++ ..._response_data_to_an_InboundSendResult.yml | 73 +++++++++++++++++ ..._to_an_InboundMessage_with_attachments.yml | 82 +++++++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ...data_to_an_InboundMessagesListResponse.yml | 75 +++++++++++++++++ .../passes_last_id_and_returns_a_page.yml | 73 +++++++++++++++++ ..._response_data_to_an_InboundSendResult.yml | 73 +++++++++++++++++ ..._response_data_to_an_InboundSendResult.yml | 73 +++++++++++++++++ .../_delete/returns_nil.yml | 65 +++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ...data_to_an_InboundThread_with_messages.yml | 81 ++++++++++++++++++ .../raises_an_error.yml | 71 ++++++++++++++++ ..._data_to_an_InboundThreadsListResponse.yml | 74 +++++++++++++++++ .../passes_last_id_and_returns_a_page.yml | 74 +++++++++++++++++ spec/mailtrap/inbound_folders_api_spec.rb | 6 +- spec/mailtrap/inbound_inboxes_api_spec.rb | 8 +- spec/mailtrap/inbound_messages_api_spec.rb | 4 +- spec/mailtrap/inbound_threads_api_spec.rb | 4 +- 32 files changed, 2030 insertions(+), 11 deletions(-) create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_create/maps_response_data_to_an_InboundFolder_object.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/returns_nil.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/when_the_folder_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/maps_response_data_to_an_InboundFolder_object.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/when_the_folder_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_list/maps_response_data_to_InboundFolder_objects.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_update/maps_response_data_to_an_InboundFolder_object.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_create/maps_response_data_to_an_InboundInbox_object.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/returns_nil.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/when_the_inbox_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/maps_response_data_to_an_InboundInbox_object.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/when_the_inbox_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_list/maps_response_data_to_InboundInbox_objects.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_update/maps_response_data_to_an_InboundInbox_object.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_delete/returns_nil.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_forward/maps_response_data_to_an_InboundSendResult.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/maps_response_data_to_an_InboundMessage_with_attachments.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/when_the_message_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/maps_response_data_to_an_InboundMessagesListResponse.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply/maps_response_data_to_an_InboundSendResult.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply_all/maps_response_data_to_an_InboundSendResult.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/returns_nil.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/when_the_thread_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/maps_response_data_to_an_InboundThread_with_messages.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/when_the_thread_does_not_exist/raises_an_error.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/maps_response_data_to_an_InboundThreadsListResponse.yml create mode 100644 spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_create/maps_response_data_to_an_InboundFolder_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_create/maps_response_data_to_an_InboundFolder_object.yml new file mode 100644 index 0000000..4131311 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_create/maps_response_data_to_an_InboundFolder_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/inbound/folders + body: + encoding: UTF-8 + string: '{"name":"Support"}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 24 Jul 2026 05:19:26 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '26' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"f8de0d6b4c1c156d96e6426a71876780" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.043308' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"id":89,"name":"Support"}' + recorded_at: Fri, 24 Jul 2026 05:19:26 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/returns_nil.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/returns_nil.yml new file mode 100644 index 0000000..e64d5a0 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/returns_nil.yml @@ -0,0 +1,65 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/folders/89 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 204 + message: No Content + headers: + Date: + - Fri, 24 Jul 2026 05:20:41 GMT + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.038224' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Fri, 24 Jul 2026 05:20:41 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/when_the_folder_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/when_the_folder_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..08a0eb3 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_delete/when_the_folder_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/folders/-1 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 05:20:42 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '147' + Cache-Control: + - no-cache + X-Runtime: + - '0.013694' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 05:20:42 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/maps_response_data_to_an_InboundFolder_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/maps_response_data_to_an_InboundFolder_object.yml new file mode 100644 index 0000000..973755b --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/maps_response_data_to_an_InboundFolder_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/folders/77 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 05:19:18 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '35' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"58b984d2a0bc9ec2c624a8c3f5ec5a57" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.014208' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"id":77,"name":"Customer Success"}' + recorded_at: Fri, 24 Jul 2026 05:19:18 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/when_the_folder_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/when_the_folder_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..02644be --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_get/when_the_folder_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/folders/-1 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 05:19:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.013188' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 05:19:19 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_list/maps_response_data_to_InboundFolder_objects.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_list/maps_response_data_to_InboundFolder_objects.yml new file mode 100644 index 0000000..7bb2fa4 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_list/maps_response_data_to_InboundFolder_objects.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/folders + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 05:18:43 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '37' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"77f3703efdfde963cb90665cd76f7b50" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.018008' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '[{"id":77,"name":"Customer Success"}]' + recorded_at: Fri, 24 Jul 2026 05:18:43 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_update/maps_response_data_to_an_InboundFolder_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_update/maps_response_data_to_an_InboundFolder_object.yml new file mode 100644 index 0000000..3511c2d --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundFoldersAPI/_update/maps_response_data_to_an_InboundFolder_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: patch + uri: https://mailtrap.io/api/inbound/folders/77 + body: + encoding: UTF-8 + string: '{"name":"Customer Success"}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 05:20:41 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '35' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"58b984d2a0bc9ec2c624a8c3f5ec5a57" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.033271' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"id":77,"name":"Customer Success"}' + recorded_at: Fri, 24 Jul 2026 05:20:41 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_create/maps_response_data_to_an_InboundInbox_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_create/maps_response_data_to_an_InboundInbox_object.yml new file mode 100644 index 0000000..5f88f39 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_create/maps_response_data_to_an_InboundInbox_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/inbound/folders/77/inboxes + body: + encoding: UTF-8 + string: '{"name":"Tickets"}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 24 Jul 2026 05:25:22 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '96' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"a7531a9e954af07dd41a138ae21ef328" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.061474' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"id":513,"name":"Tickets","address":"tickets-1a2b3c4d@inbound-mailtrap.io","domain_id":1237003}' + recorded_at: Fri, 24 Jul 2026 05:25:22 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/returns_nil.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/returns_nil.yml new file mode 100644 index 0000000..3f550d2 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/returns_nil.yml @@ -0,0 +1,65 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/folders/77/inboxes/473 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 204 + message: No Content + headers: + Date: + - Fri, 24 Jul 2026 05:25:54 GMT + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Runtime: + - '0.032814' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Fri, 24 Jul 2026 05:25:54 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/when_the_inbox_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/when_the_inbox_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..398966f --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_delete/when_the_inbox_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/folders/77/inboxes/-1 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 05:25:54 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.018619' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 05:25:54 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/maps_response_data_to_an_InboundInbox_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/maps_response_data_to_an_InboundInbox_object.yml new file mode 100644 index 0000000..f017cda --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/maps_response_data_to_an_InboundInbox_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/folders/77/inboxes/473 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 05:25:00 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"da980a3ccef18672ce467709f90783fe" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.017995' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"id":473,"name":"Tickets (renamed)","address":"tickets-5e6f7a8b@inbound-mailtrap.io","domain_id":1237003}' + recorded_at: Fri, 24 Jul 2026 05:25:00 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/when_the_inbox_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/when_the_inbox_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..bf9a0e8 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_get/when_the_inbox_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/folders/77/inboxes/-1 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 05:25:00 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.014995' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 05:25:00 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_list/maps_response_data_to_InboundInbox_objects.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_list/maps_response_data_to_InboundInbox_objects.yml new file mode 100644 index 0000000..67a9797 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_list/maps_response_data_to_InboundInbox_objects.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/folders/77/inboxes + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 05:21:55 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"19de61481c01647d11a67e7bdb721d42" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.019062' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '[{"id":473,"name":"Tickets (renamed)","address":"tickets-5e6f7a8b@inbound-mailtrap.io","domain_id":1237003}]' + recorded_at: Fri, 24 Jul 2026 05:21:55 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_update/maps_response_data_to_an_InboundInbox_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_update/maps_response_data_to_an_InboundInbox_object.yml new file mode 100644 index 0000000..00cbc99 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundInboxesAPI/_update/maps_response_data_to_an_InboundInbox_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: patch + uri: https://mailtrap.io/api/inbound/folders/77/inboxes/513 + body: + encoding: UTF-8 + string: '{"name":"Tickets (renamed)"}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 05:26:00 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"a9fd658cef5554f16b6b7fe7ac3dd559" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.044583' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"id":513,"name":"Tickets (renamed)","address":"tickets-1a2b3c4d@inbound-mailtrap.io","domain_id":1237003}' + recorded_at: Fri, 24 Jul 2026 05:26:00 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_delete/returns_nil.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_delete/returns_nil.yml new file mode 100644 index 0000000..5911a79 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_delete/returns_nil.yml @@ -0,0 +1,65 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/inboxes/513/messages/1871574677877796928 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 204 + message: No Content + headers: + Date: + - Fri, 24 Jul 2026 06:02:06 GMT + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Runtime: + - '0.061123' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Fri, 24 Jul 2026 06:02:06 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_forward/maps_response_data_to_an_InboundSendResult.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_forward/maps_response_data_to_an_InboundSendResult.yml new file mode 100644 index 0000000..30b7f70 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_forward/maps_response_data_to_an_InboundSendResult.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/inbound/inboxes/513/messages/1871574677877796928/forward + body: + encoding: UTF-8 + string: '{"to":[{"email":"colleague@example.com"}],"text":"Please take a look."}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 24 Jul 2026 06:01:55 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '56' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"af166d86507a473c8d0bb2be30b6ddd3" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.177895' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"message_ids":["2ca1ffb0-8725-11f1-0000-f1b5e753a178"]}' + recorded_at: Fri, 24 Jul 2026 06:01:55 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/maps_response_data_to_an_InboundMessage_with_attachments.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/maps_response_data_to_an_InboundMessage_with_attachments.yml new file mode 100644 index 0000000..4bd4fa5 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/maps_response_data_to_an_InboundMessage_with_attachments.yml @@ -0,0 +1,82 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/messages/1871574677877796928 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 06:01:24 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + Vary: + - Accept + - Accept-Encoding + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"0e8fb328ca23c2bb76d0154d5dea76bf" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.182283' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"id":"1871574677877796928","inbox_id":513,"from":"Inbound Tester ","to":["tickets-1a2b3c4d@inbound-mailtrap.io"],"cc":[],"bcc":[],"reply_to":null,"subject":"Inbound + test 2026-07-24T05:58:20Z","headers":{"mime-version":"1.0","return-path":""},"size":4210,"html_size":968,"text_size":351,"received_at":"2026-07-24T05:58:21.528Z","rfc_message_id":"","in_reply_to":null,"references":[],"thread_id":"1871574677878845504","attachments":[],"raw_message_url":"https://mailsend-us-inbound-mail-bodies.s3.amazonaws.com/raw/c3b/292/1871574677877796928/raw.eml?X-Amz-Signature=REDACTED","raw_message_expires_at":"2026-07-24T07:01:23.851Z","html_body":"

Hello + from the inbound test script.

\r\n \r\n \r\n \r\n
\r\n Unsubscribe\r\n
\r\n\"\"","text_body":"Hello from the inbound + test script.\r\n\r\nUnsubscribe: https://t.mailtrap.net/out/REDACTED"}' + recorded_at: Fri, 24 Jul 2026 06:01:23 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/when_the_message_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/when_the_message_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..f8917a6 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_get/when_the_message_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/messages/does-not-exist + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 06:01:24 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.016128' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 06:01:24 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/maps_response_data_to_an_InboundMessagesListResponse.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/maps_response_data_to_an_InboundMessagesListResponse.yml new file mode 100644 index 0000000..6307a28 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/maps_response_data_to_an_InboundMessagesListResponse.yml @@ -0,0 +1,75 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/messages + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 06:00:18 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"837b7badb4225a3ac7deee001686574f" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.052864' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"data":[{"id":"1871574677877796928","inbox_id":513,"from":"Inbound + Tester ","to":["tickets-1a2b3c4d@inbound-mailtrap.io"],"cc":[],"bcc":[],"reply_to":null,"subject":"Inbound + test 2026-07-24T05:58:20Z","headers":{"mime-version":"1.0","return-path":""},"size":4210,"html_size":968,"text_size":351,"received_at":"2026-07-24T05:58:21.528Z","rfc_message_id":"","in_reply_to":null,"references":[],"thread_id":"1871574677878845504","attachments":[]}],"total_count":1,"last_id":null}' + recorded_at: Fri, 24 Jul 2026 06:00:18 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml new file mode 100644 index 0000000..7efce8b --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/messages?last_id=1700000000000000 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 06:00:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '42' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Etag: + - W/"298b2cdb802ff5b7c1d64b61a50a9c91" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.020621' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"data":[],"total_count":0,"last_id":null}' + recorded_at: Fri, 24 Jul 2026 06:00:19 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply/maps_response_data_to_an_InboundSendResult.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply/maps_response_data_to_an_InboundSendResult.yml new file mode 100644 index 0000000..c40d286 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply/maps_response_data_to_an_InboundSendResult.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/inbound/inboxes/513/messages/1871574677877796928/reply + body: + encoding: UTF-8 + string: '{"text":"Thanks for reaching out."}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 24 Jul 2026 06:01:40 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '56' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"b9b71a1818af1ac86182186d6cb07b41" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.140215' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"message_ids":["23594a30-8725-11f1-0000-f1b5e753a178"]}' + recorded_at: Fri, 24 Jul 2026 06:01:40 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply_all/maps_response_data_to_an_InboundSendResult.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply_all/maps_response_data_to_an_InboundSendResult.yml new file mode 100644 index 0000000..8b49609 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundMessagesAPI/_reply_all/maps_response_data_to_an_InboundSendResult.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/inbound/inboxes/513/messages/1871574677877796928/reply_all + body: + encoding: UTF-8 + string: '{"text":"Looping everyone in."}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 24 Jul 2026 06:01:47 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '56' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Etag: + - W/"978c1ede6c1cfef358d3de3b48f14202" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.101735' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"message_ids":["277b7ca0-8725-11f1-0000-f12f7dc93be2"]}' + recorded_at: Fri, 24 Jul 2026 06:01:47 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/returns_nil.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/returns_nil.yml new file mode 100644 index 0000000..638bd28 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/returns_nil.yml @@ -0,0 +1,65 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/inboxes/513/threads/1871574677878845504 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 204 + message: No Content + headers: + Date: + - Fri, 24 Jul 2026 06:04:04 GMT + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Runtime: + - '0.959129' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Fri, 24 Jul 2026 06:04:04 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/when_the_thread_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/when_the_thread_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..29a6883 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_delete/when_the_thread_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/inbound/inboxes/513/threads/does-not-exist + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 06:04:05 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.023884' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 06:04:04 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/maps_response_data_to_an_InboundThread_with_messages.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/maps_response_data_to_an_InboundThread_with_messages.yml new file mode 100644 index 0000000..357ba47 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/maps_response_data_to_an_InboundThread_with_messages.yml @@ -0,0 +1,81 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/threads/1871574677878845504 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 06:03:40 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + Vary: + - Accept + - Accept-Encoding + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"028d9f60732fae8f80c27a07ce63b63e" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.106065' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"id":"1871574677878845504","subject":"Inbound test 2026-07-24T05:58:20Z","message_count":2,"size":936,"first_message_at":"2026-07-24T05:58:21.528Z","last_received_at":"2026-07-24T05:58:21.528Z","last_sent_at":"2026-07-24T06:01:47.114Z","last_activity_at":"2026-07-24T06:01:47.114Z","last_message_id":"1871574893450792960","senders":["tickets-1a2b3c4d@inbound-mailtrap.io"],"recipients":["Inbound + Tester "],"attachments":[],"messages":[{"visibility_status":"available","direction":"outbound","id":"23594a30-8725-11f1-0000-f1b5e753a178","message_group_id":null,"subject":"Re: + Inbound test 2026-07-24T05:58:20Z","rfc_message_id":"<23594a30-8725-11f1-0000-f1b5e753a178@inbound-mailtrap.io>","in_reply_to":"","references":[""],"from":"tickets-1a2b3c4d@inbound-mailtrap.io","to":["Inbound + Tester "],"cc":[],"bcc":[],"reply_to":"","created_at":"2026-07-24T06:01:40.178Z","email_size":470,"text_body":"Thanks + for reaching out.","html_body":null,"attachments":[],"delivery_status":"enqueued","delivered_at":null,"bounced_at":null},{"visibility_status":"available","direction":"outbound","id":"277b7ca0-8725-11f1-0000-f12f7dc93be2","message_group_id":null,"subject":"Re: + Inbound test 2026-07-24T05:58:20Z","rfc_message_id":"<277b7ca0-8725-11f1-0000-f12f7dc93be2@inbound-mailtrap.io>","in_reply_to":"","references":[""],"from":"tickets-1a2b3c4d@inbound-mailtrap.io","to":["Inbound + Tester "],"cc":[],"bcc":[],"reply_to":"","created_at":"2026-07-24T06:01:47.114Z","email_size":466,"text_body":"Looping + everyone in.","html_body":null,"attachments":[],"delivery_status":"enqueued","delivered_at":null,"bounced_at":null}]}' + recorded_at: Fri, 24 Jul 2026 06:03:40 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/when_the_thread_does_not_exist/raises_an_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/when_the_thread_does_not_exist/raises_an_error.yml new file mode 100644 index 0000000..a152e0c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_get/when_the_thread_does_not_exist/raises_an_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/threads/does-not-exist + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Fri, 24 Jul 2026 06:03:41 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.024935' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Fri, 24 Jul 2026 06:03:41 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/maps_response_data_to_an_InboundThreadsListResponse.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/maps_response_data_to_an_InboundThreadsListResponse.yml new file mode 100644 index 0000000..fb31af6 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/maps_response_data_to_an_InboundThreadsListResponse.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/threads + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 06:03:11 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"56ee4b8d5ffec4d9010514ca73a839e9" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.138654' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"data":[{"id":"1871574677878845504","subject":"Inbound test 2026-07-24T05:58:20Z","message_count":2,"size":936,"first_message_at":"2026-07-24T05:58:21.528Z","last_received_at":"2026-07-24T05:58:21.528Z","last_sent_at":"2026-07-24T06:01:47.114Z","last_activity_at":"2026-07-24T06:01:47.114Z","last_message_id":"1871574893450792960","senders":["tickets-1a2b3c4d@inbound-mailtrap.io"],"recipients":["Inbound + Tester "],"attachments":[]}],"total_count":1,"last_id":null}' + recorded_at: Fri, 24 Jul 2026 06:03:11 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml new file mode 100644 index 0000000..e05d86d --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_InboundThreadsAPI/_list/with_pagination_cursor/passes_last_id_and_returns_a_page.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/inbound/inboxes/513/threads?last_id=abc123 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 24 Jul 2026 06:03:12 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Etag: + - W/"56ee4b8d5ffec4d9010514ca73a839e9" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.040010' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"data":[{"id":"1871574677878845504","subject":"Inbound test 2026-07-24T05:58:20Z","message_count":2,"size":936,"first_message_at":"2026-07-24T05:58:21.528Z","last_received_at":"2026-07-24T05:58:21.528Z","last_sent_at":"2026-07-24T06:01:47.114Z","last_activity_at":"2026-07-24T06:01:47.114Z","last_message_id":"1871574893450792960","senders":["tickets-1a2b3c4d@inbound-mailtrap.io"],"recipients":["Inbound + Tester "],"attachments":[]}],"total_count":1,"last_id":null}' + recorded_at: Fri, 24 Jul 2026 06:03:12 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/mailtrap/inbound_folders_api_spec.rb b/spec/mailtrap/inbound_folders_api_spec.rb index 4bf298a..884a96d 100644 --- a/spec/mailtrap/inbound_folders_api_spec.rb +++ b/spec/mailtrap/inbound_folders_api_spec.rb @@ -16,7 +16,7 @@ describe '#get' do subject(:get) { folders_api.get(folder_id) } - let(:folder_id) { 1 } + let(:folder_id) { 77 } it 'maps response data to an InboundFolder object' do expect(get).to be_a(Mailtrap::InboundFolder) @@ -54,7 +54,7 @@ describe '#update' do subject(:update) { folders_api.update(folder_id, options) } - let(:folder_id) { 1 } + let(:folder_id) { 77 } let(:options) { { name: 'Customer Success' } } it 'maps response data to an InboundFolder object' do @@ -74,7 +74,7 @@ describe '#delete' do subject(:delete) { folders_api.delete(folder_id) } - let(:folder_id) { 1 } + let(:folder_id) { 89 } it 'returns nil' do expect(delete).to be_nil diff --git a/spec/mailtrap/inbound_inboxes_api_spec.rb b/spec/mailtrap/inbound_inboxes_api_spec.rb index 16cd831..8ea18ac 100644 --- a/spec/mailtrap/inbound_inboxes_api_spec.rb +++ b/spec/mailtrap/inbound_inboxes_api_spec.rb @@ -4,7 +4,7 @@ subject(:inboxes_api) { described_class.new(client) } let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } - let(:folder_id) { 1 } + let(:folder_id) { 77 } describe '#list' do subject(:list) { inboxes_api.list(folder_id) } @@ -17,7 +17,7 @@ describe '#get' do subject(:get) { inboxes_api.get(folder_id, inbox_id) } - let(:inbox_id) { 42 } + let(:inbox_id) { 473 } it 'maps response data to an InboundInbox object' do expect(get).to be_a(Mailtrap::InboundInbox) @@ -55,7 +55,7 @@ describe '#update' do subject(:update) { inboxes_api.update(folder_id, inbox_id, options) } - let(:inbox_id) { 42 } + let(:inbox_id) { 513 } let(:options) { { name: 'Tickets (renamed)' } } it 'maps response data to an InboundInbox object' do @@ -75,7 +75,7 @@ describe '#delete' do subject(:delete) { inboxes_api.delete(folder_id, inbox_id) } - let(:inbox_id) { 42 } + let(:inbox_id) { 473 } it 'returns nil' do expect(delete).to be_nil diff --git a/spec/mailtrap/inbound_messages_api_spec.rb b/spec/mailtrap/inbound_messages_api_spec.rb index 1a983dc..2e78253 100644 --- a/spec/mailtrap/inbound_messages_api_spec.rb +++ b/spec/mailtrap/inbound_messages_api_spec.rb @@ -4,8 +4,8 @@ subject(:messages_api) { described_class.new(client) } let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } - let(:inbox_id) { 42 } - let(:message_id) { '1700000000000123' } + let(:inbox_id) { 513 } + let(:message_id) { '1871574677877796928' } describe '#list' do subject(:list) { messages_api.list(inbox_id) } diff --git a/spec/mailtrap/inbound_threads_api_spec.rb b/spec/mailtrap/inbound_threads_api_spec.rb index fceba45..28e5965 100644 --- a/spec/mailtrap/inbound_threads_api_spec.rb +++ b/spec/mailtrap/inbound_threads_api_spec.rb @@ -4,8 +4,8 @@ subject(:threads_api) { described_class.new(client) } let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } - let(:inbox_id) { 42 } - let(:thread_id) { '1700000000000124' } + let(:inbox_id) { 513 } + let(:thread_id) { '1871574677878845504' } describe '#list' do subject(:list) { threads_api.list(inbox_id) } From e60835842a2aacf40371336eff11ccc93c0b7fdb Mon Sep 17 00:00:00 2001 From: Marcin Klocek Date: Fri, 24 Jul 2026 08:44:50 +0200 Subject: [PATCH 6/6] Add inbound v2 examples and README entries Per-resource example scripts for inbound folders, inboxes, messages (incl. reply/reply_all/forward) and threads, linked from a new Inbound Email section in the README. --- README.md | 7 +++++++ examples/inbound_folders_api.rb | 24 ++++++++++++++++++++++++ examples/inbound_inboxes_api.rb | 25 +++++++++++++++++++++++++ examples/inbound_messages_api.rb | 32 ++++++++++++++++++++++++++++++++ examples/inbound_threads_api.rb | 20 ++++++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 examples/inbound_folders_api.rb create mode 100644 examples/inbound_inboxes_api.rb create mode 100644 examples/inbound_messages_api.rb create mode 100644 examples/inbound_threads_api.rb diff --git a/README.md b/README.md index b7a9318..1b66997 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,13 @@ Email Sandbox (Testing): - Sandbox Messages CRUD - [`sandbox_messages_api.rb`](examples/sandbox_messages_api.rb) - Sandbox Attachments API - [`sandbox_attachments_api.rb`](examples/sandbox_attachments_api.rb) +Inbound Email: + +- Inbound Folders API – [`inbound_folders_api.rb`](examples/inbound_folders_api.rb) +- Inbound Inboxes API – [`inbound_inboxes_api.rb`](examples/inbound_inboxes_api.rb) +- Inbound Messages API (list, get, delete, reply, reply_all, forward) – [`inbound_messages_api.rb`](examples/inbound_messages_api.rb) +- Inbound Threads API – [`inbound_threads_api.rb`](examples/inbound_threads_api.rb) + Contact management: - Contacts CRUD & Listing – [`contacts_api.rb`](examples/contacts_api.rb) diff --git a/examples/inbound_folders_api.rb b/examples/inbound_folders_api.rb new file mode 100644 index 0000000..b8371f4 --- /dev/null +++ b/examples/inbound_folders_api.rb @@ -0,0 +1,24 @@ +require 'mailtrap' + +client = Mailtrap::Client.new(api_key: 'your-api-key') +folders = Mailtrap::InboundFoldersAPI.new(client) + +# Create a folder +folder = folders.create(name: 'Support') +# => # + +# List all folders +folders.list +# => [#] + +# Get a folder +folders.get(folder.id) +# => # + +# Update a folder +folders.update(folder.id, name: 'Customer Success') +# => # + +# Delete a folder (and all of its inboxes) +folders.delete(folder.id) +# => nil diff --git a/examples/inbound_inboxes_api.rb b/examples/inbound_inboxes_api.rb new file mode 100644 index 0000000..20e3fb3 --- /dev/null +++ b/examples/inbound_inboxes_api.rb @@ -0,0 +1,25 @@ +require 'mailtrap' + +client = Mailtrap::Client.new(api_key: 'your-api-key') +inboxes = Mailtrap::InboundInboxesAPI.new(client) +folder_id = 1 + +# Create an inbox. Omit domain_id for a Mailtrap-hosted inbox; pass it for a custom-domain inbox. +inbox = inboxes.create(folder_id, name: 'Tickets') +# => # + +# List inboxes in a folder +inboxes.list(folder_id) +# => [#] + +# Get an inbox +inboxes.get(folder_id, inbox.id) +# => # + +# Update an inbox +inboxes.update(folder_id, inbox.id, name: 'Tickets (renamed)') +# => # + +# Delete an inbox +inboxes.delete(folder_id, inbox.id) +# => nil diff --git a/examples/inbound_messages_api.rb b/examples/inbound_messages_api.rb new file mode 100644 index 0000000..e4992e3 --- /dev/null +++ b/examples/inbound_messages_api.rb @@ -0,0 +1,32 @@ +require 'mailtrap' + +client = Mailtrap::Client.new(api_key: 'your-api-key') +messages = Mailtrap::InboundMessagesAPI.new(client) +inbox_id = 42 + +# List messages in an inbox (cursor-paginated) +page = messages.list(inbox_id) +# => # + +# Fetch the next page with the returned cursor +messages.list(inbox_id, last_id: page.last_id) if page.last_id + +# Get a single message with its body and attachment download URLs +message = messages.get(inbox_id, page.data.first.id) +# => # + +# Reply to the original sender +messages.reply(inbox_id, message.id, text: 'Thanks for reaching out.') +# => # + +# Reply to everyone on the original message +messages.reply_all(inbox_id, message.id, text: 'Looping everyone in.') +# => # + +# Forward to new recipients (at least one `to` is required) +messages.forward(inbox_id, message.id, to: [{ email: 'colleague@example.com' }], text: 'Please take a look.') +# => # + +# Delete a message +messages.delete(inbox_id, message.id) +# => nil diff --git a/examples/inbound_threads_api.rb b/examples/inbound_threads_api.rb new file mode 100644 index 0000000..765f737 --- /dev/null +++ b/examples/inbound_threads_api.rb @@ -0,0 +1,20 @@ +require 'mailtrap' + +client = Mailtrap::Client.new(api_key: 'your-api-key') +threads = Mailtrap::InboundThreadsAPI.new(client) +inbox_id = 42 + +# List threads in an inbox (cursor-paginated) +page = threads.list(inbox_id) +# => # + +# Fetch the next page with the returned cursor +threads.list(inbox_id, last_id: page.last_id) if page.last_id + +# Get a single thread with its messages embedded (oldest first) +threads.get(inbox_id, page.data.first.id) +# => # + +# Delete a thread +threads.delete(inbox_id, page.data.first.id) +# => nil