diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f8e737..5ef48e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +### Unreleased +* Documented `tracking_options.domain_name` hash support for custom link and open tracking hostnames. See the [message tracking documentation](https://developer.nylas.com/docs/v3/email/message-tracking/) + ### [6.8.1] * Documented `tentative_as_busy` support in the `Calendars#get_free_busy` request body, consistent with other Nylas SDKs diff --git a/examples/README.md b/examples/README.md index 717964da..6a9b5f78 100644 --- a/examples/README.md +++ b/examples/README.md @@ -92,6 +92,103 @@ Before running any example, make sure to: export NYLAS_TEST_EMAIL="test@example.com" # Email address to send test messages to ``` +#### Custom tracking hostnames + +Set `tracking_options.domain_name` to an active custom hostname owned by your organization. Enable +link tracking, open tracking, or both in the same hash. If `domain_name` is omitted, tracked messages +use the default Nylas tracking hostname. See the +[message tracking documentation](https://developer.nylas.com/docs/v3/email/message-tracking/) for +registration, validation, and failure behavior. + +##### Regular Send + +```ruby +nylas.messages.send( + identifier: grant_id, + request_body: { + to: [{ email: "recipient@example.com" }], + subject: "Your update", + body: 'View update', + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } +) +``` + +##### Transactional Send + +The sender domain belongs in the Transactional Send route. The distinct `tracking_options.domain_name` +value belongs in the request body and is used for recipient-visible links and open pixels. + +```ruby +sender_domain = "sender.example.com" +transactional_path = "/v3/domains/#{sender_domain}/messages/send" +transactional_request_body = { + from: [{ email: "billing@sender.example.com" }], + to: [{ email: "recipient@example.com" }], + subject: "Your receipt", + body: 'View receipt', + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } +} + +# POST transactional_path with transactional_request_body. +``` + +##### Drafts + +```ruby +draft, = nylas.drafts.create( + identifier: grant_id, + request_body: { + to: [{ email: "recipient@example.com" }], + subject: "Draft update", + body: 'View update', + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } +) + +nylas.drafts.update( + identifier: grant_id, + draft_id: draft[:id], + request_body: { + subject: "Updated draft subject" + # Omit tracking_options to preserve the draft's existing tracking settings. + } +) +``` + +##### Scheduled Send + +Custom tracking hostnames are validated when the message is scheduled and revalidated before delivery. + +```ruby +nylas.messages.send( + identifier: grant_id, + request_body: { + to: [{ email: "recipient@example.com" }], + subject: "Scheduled update", + body: 'View update', + send_at: Time.now.to_i + 3600, + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } +) +``` + ### Notetaker - `notetaker/notetaker_example.rb`: Shows basic Notetaker functionality, including: - Inviting a Notetaker to a meeting @@ -123,4 +220,4 @@ When adding new examples: If you encounter any issues or have questions about these examples, please: 1. Check the [Nylas documentation](https://developer.nylas.com) 2. Visit our [GitHub repository](https://github.com/nylas/nylas-ruby) -3. Contact [Nylas support](https://support.nylas.com) \ No newline at end of file +3. Contact [Nylas support](https://support.nylas.com) diff --git a/lib/nylas/resources/drafts.rb b/lib/nylas/resources/drafts.rb index 6dbf8fb2..df555d2b 100644 --- a/lib/nylas/resources/drafts.rb +++ b/lib/nylas/resources/drafts.rb @@ -39,6 +39,9 @@ def find(identifier:, draft_id:) # # @param identifier [String] Grant ID or email account in which to create the draft. # @param request_body [Hash] The values to create the message with. + # Use `:tracking_options` with `:links` or `:opens` enabled to track the draft. To use a custom + # tracking hostname, set `:domain_name` inside `:tracking_options` to an active hostname owned + # by your organization. # If you're attaching files, you must pass an array of [File] objects, or # you can pass in base64 encoded strings if the total attachment size is less than 3mb. # You can also use {FileUtils::attach_file_request_builder} to build each object attach. @@ -61,6 +64,9 @@ def create(identifier:, request_body:) # @param identifier [String] Grant ID or email account in which to update the draft. # @param draft_id [String] The id of the draft to update. # @param request_body [Hash] The values to create the message with. + # To replace the draft's custom tracking hostname, set `:tracking_options` with `:links` or + # `:opens` enabled and a `:domain_name`. Omit `:tracking_options` to preserve the draft's + # existing tracking settings. # If you're attaching files, you must pass an array of [File] objects, or # you can pass in base64 encoded strings if the total attachment size is less than 3mb. # You can also use {FileUtils::attach_file_request_builder} to build each object attach. diff --git a/lib/nylas/resources/messages.rb b/lib/nylas/resources/messages.rb index 2a83c4ad..b6f63842 100644 --- a/lib/nylas/resources/messages.rb +++ b/lib/nylas/resources/messages.rb @@ -114,6 +114,11 @@ def clean_messages(identifier:, request_body:) # # @param identifier [String] Grant ID or email account from which to delete an object. # @param request_body [Hash] The values to create the message with. + # Use `:tracking_options` with `:links` or `:opens` enabled to track a message. To use a custom + # tracking hostname, set `:domain_name` inside `:tracking_options` to an active hostname owned + # by your organization. Omit `:domain_name` to use the default Nylas tracking hostname. + # Set `:send_at` to a Unix timestamp to schedule the message; custom tracking hostnames are + # validated when the send is scheduled and revalidated before delivery. # If you're attaching files, you must pass an array of [File] objects, or # you can pass in base64 encoded strings if the total attachment size is less than 3mb. # You can also use {FileUtils::attach_file_request_builder} to build each object attach. diff --git a/spec/nylas/handler/http_client_spec.rb b/spec/nylas/handler/http_client_spec.rb index c0e905aa..f4f77314 100644 --- a/spec/nylas/handler/http_client_spec.rb +++ b/spec/nylas/handler/http_client_spec.rb @@ -175,6 +175,26 @@ class TestHttpClient ) end + it "serializes a custom tracking hostname using the documented JSON shape" do + payload = { + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } + request = http_client.send(:build_request, method: :post, path: "https://test.api.nylas.com/foo", + payload: payload, api_key: "fake-key") + + expect(JSON.parse(request[:payload])).to eq( + "tracking_options" => { + "links" => true, + "opens" => true, + "domain_name" => "links.example.com" + } + ) + end + it "returns the correct request with a multipart flag (string key)" do payload = { "multipart" => true } request = http_client.send(:build_request, method: :post, path: "https://test.api.nylas.com/foo", diff --git a/spec/nylas/resources/drafts_spec.rb b/spec/nylas/resources/drafts_spec.rb index bc01b57f..402f2bd0 100644 --- a/spec/nylas/resources/drafts_spec.rb +++ b/spec/nylas/resources/drafts_spec.rb @@ -97,6 +97,29 @@ expect(draft_response).to eq(response) end + it "forwards a custom tracking hostname in the request body" do + identifier = "abc-123-grant-id" + request_body = { + subject: "Tracked draft", + to: [{ email: "recipient@example.com" }], + body: 'View update', + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } + path = "#{api_uri}/v3/grants/#{identifier}/drafts" + + allow(drafts).to receive(:post) + .with(path: path, request_body: request_body) + .and_return(response) + + draft_response = drafts.create(identifier: identifier, request_body: request_body) + + expect(draft_response).to eq(response) + end + it "calls the post method with the correct parameters for small attachments" do identifier = "abc-123-grant-id" mock_file = instance_double("file") @@ -181,6 +204,28 @@ expect(draft_response).to eq(response) end + it "forwards a replacement custom tracking hostname in the request body" do + identifier = "abc-123-grant-id" + draft_id = "5d3qmne77v32r8l4phyuksl2x" + request_body = { + tracking_options: { + links: true, + opens: true, + domain_name: "replacement-links.example.com" + } + } + path = "#{api_uri}/v3/grants/#{identifier}/drafts/#{draft_id}" + + allow(drafts).to receive(:put) + .with(path: path, request_body: request_body) + .and_return(response) + + draft_response = drafts.update(identifier: identifier, draft_id: draft_id, + request_body: request_body) + + expect(draft_response).to eq(response) + end + it "calls the put method with the correct parameters and attachments" do identifier = "abc-123-grant-id" draft_id = "5d3qmne77v32r8l4phyuksl2x" diff --git a/spec/nylas/resources/messages_spec.rb b/spec/nylas/resources/messages_spec.rb index 32af54dc..1284781e 100644 --- a/spec/nylas/resources/messages_spec.rb +++ b/spec/nylas/resources/messages_spec.rb @@ -277,6 +277,53 @@ expect(message_response).to eq(response) end + it "forwards a custom tracking hostname in the request body" do + identifier = "abc-123-grant-id" + request_body = { + subject: "Tracked message", + to: [{ email: "recipient@example.com" }], + body: 'View update', + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } + path = "#{api_uri}/v3/grants/#{identifier}/messages/send" + + allow(messages).to receive(:post) + .with(path: path, request_body: request_body) + .and_return(response) + + message_response = messages.send(identifier: identifier, request_body: request_body) + + expect(message_response).to eq(response) + end + + it "forwards a custom tracking hostname for a scheduled send" do + identifier = "abc-123-grant-id" + request_body = { + subject: "Scheduled tracked message", + to: [{ email: "recipient@example.com" }], + body: 'View update', + send_at: 1_893_456_000, + tracking_options: { + links: true, + opens: true, + domain_name: "links.example.com" + } + } + path = "#{api_uri}/v3/grants/#{identifier}/messages/send" + + allow(messages).to receive(:post) + .with(path: path, request_body: request_body) + .and_return(response) + + message_response = messages.send(identifier: identifier, request_body: request_body) + + expect(message_response).to eq(response) + end + it "calls the post method with the correct parameters and attachments" do identifier = "abc-123-grant-id" mock_file = instance_double("file")