Skip to content

Commit 7a179ce

Browse files
authored
TT-4385: add CRUDs for transactional send (#311)
1 parent 87d7c9b commit 7a179ce

8 files changed

Lines changed: 1048 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Nylas Java SDK Changelog
22

3+
## [Unreleased]
4+
5+
### Added
6+
* Transactional email support via `Domains.sendTransactionalEmail()`
7+
- `SendTransactionalEmailRequest` model (and fluent `Builder`) for composing transactional messages from a verified domain — supports `to`, `from`, `cc`, `bcc`, `reply_to`, `subject`, `body`, `send_at`, `reply_to_message_id`, `tracking_options`, `use_draft`, `custom_headers`, and `is_plaintext`
8+
- `NylasClient.domains()` accessor returning the new `Domains` resource
9+
- Automatic multipart/form-data upload when the total attachment size exceeds the JSON limit
10+
- Examples: `TransactionalEmailExample.java` and `KotlinTransactionalEmailExample.kt`
11+
312
## [v2.16.1] - Release 2026-05-21
413

514
### Fixed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package com.nylas.examples;
2+
3+
import com.nylas.NylasClient;
4+
import com.nylas.models.*;
5+
import okhttp3.OkHttpClient;
6+
7+
import java.io.File;
8+
import java.io.IOException;
9+
import java.nio.file.Files;
10+
import java.util.Arrays;
11+
import java.util.Collections;
12+
import java.util.HashMap;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.stream.Collectors;
16+
17+
/**
18+
* Example demonstrating how to send transactional emails using the Nylas Java SDK.
19+
*
20+
* <p>Transactional send lets you send individual emails from a verified domain without
21+
* a connected end-user grant. Common use cases include:
22+
* <ul>
23+
* <li>Receipts and order confirmations</li>
24+
* <li>Password-reset and account-verification emails</li>
25+
* <li>Notification and alert emails</li>
26+
* </ul>
27+
*
28+
* <p>Prerequisites:
29+
* <ul>
30+
* <li>A Nylas application with at least one verified domain</li>
31+
* <li>{@code NYLAS_API_KEY} — your Nylas API key</li>
32+
* <li>{@code NYLAS_DOMAIN} — the verified domain to send from (e.g. "acme.com")</li>
33+
* <li>{@code NYLAS_SENDER_EMAIL} — a sender address on that domain (e.g. "noreply@acme.com")</li>
34+
* <li>{@code NYLAS_RECIPIENT_EMAIL} — the address that will receive the test email</li>
35+
* </ul>
36+
*/
37+
public class TransactionalEmailExample {
38+
39+
public static void main(String[] args) {
40+
try {
41+
Map<String, String> config = loadConfig();
42+
43+
NylasClient nylas = new NylasClient(
44+
config.get("NYLAS_API_KEY"),
45+
new OkHttpClient.Builder(),
46+
config.getOrDefault("NYLAS_API_URI", "https://api.us.nylas.com")
47+
);
48+
49+
runTransactionalEmailExample(nylas, config);
50+
System.exit(0);
51+
52+
} catch (NylasApiError e) {
53+
System.out.println("\n❌ Nylas API Error: " + e.getMessage());
54+
System.out.println(" Status code: " + e.getStatusCode());
55+
System.out.println(" Request ID: " + e.getRequestId());
56+
System.exit(1);
57+
} catch (IllegalArgumentException e) {
58+
System.out.println("\n❌ Configuration Error: " + e.getMessage());
59+
System.exit(1);
60+
} catch (Exception e) {
61+
System.out.println("\n❌ Unexpected Error: " + e.getMessage());
62+
e.printStackTrace();
63+
System.exit(1);
64+
}
65+
}
66+
67+
// -------------------------------------------------------------------------
68+
// Config loader
69+
// -------------------------------------------------------------------------
70+
71+
private static Map<String, String> loadConfig() {
72+
Map<String, String> config = new HashMap<>();
73+
74+
System.getenv().entrySet().stream()
75+
.filter(entry -> entry.getKey().startsWith("NYLAS_"))
76+
.forEach(entry -> config.put(entry.getKey(), entry.getValue()));
77+
78+
List<String> envPaths = Arrays.asList("examples/.env", ".env");
79+
for (String path : envPaths) {
80+
File envFile = new File(path);
81+
if (envFile.exists()) {
82+
System.out.println("📝 Loading configuration from " + envFile.getAbsolutePath());
83+
try {
84+
Files.lines(envFile.toPath())
85+
.filter(line -> !line.trim().isEmpty() && !line.startsWith("#"))
86+
.forEach(line -> {
87+
String[] parts = line.split("=", 2);
88+
if (parts.length == 2) {
89+
config.putIfAbsent(parts[0].trim(), parts[1].trim());
90+
}
91+
});
92+
} catch (IOException e) {
93+
System.out.println("Warning: Failed to load .env file: " + e.getMessage());
94+
}
95+
}
96+
}
97+
98+
List<String> requiredKeys = Arrays.asList(
99+
"NYLAS_API_KEY", "NYLAS_DOMAIN", "NYLAS_SENDER_EMAIL", "NYLAS_RECIPIENT_EMAIL");
100+
List<String> missingKeys = requiredKeys.stream()
101+
.filter(key -> !config.containsKey(key))
102+
.collect(Collectors.toList());
103+
104+
if (!missingKeys.isEmpty()) {
105+
throw new IllegalArgumentException(
106+
"Missing required configuration: " + String.join(", ", missingKeys) + "\n" +
107+
"Please set these in examples/.env or as environment variables.");
108+
}
109+
110+
return config;
111+
}
112+
113+
// -------------------------------------------------------------------------
114+
// Examples
115+
// -------------------------------------------------------------------------
116+
117+
private static void runTransactionalEmailExample(NylasClient nylas, Map<String, String> config)
118+
throws NylasApiError, NylasSdkTimeoutError {
119+
String domainName = config.get("NYLAS_DOMAIN");
120+
String senderEmail = config.get("NYLAS_SENDER_EMAIL");
121+
String recipientEmail = config.get("NYLAS_RECIPIENT_EMAIL");
122+
123+
System.out.println("📧 Nylas Transactional Email Examples\n");
124+
125+
sendSimpleEmail(nylas, domainName, senderEmail, recipientEmail);
126+
sendPlaintextEmail(nylas, domainName, senderEmail, recipientEmail);
127+
sendEmailWithTracking(nylas, domainName, senderEmail, recipientEmail);
128+
sendScheduledEmail(nylas, domainName, senderEmail, recipientEmail);
129+
sendEmailWithExtras(nylas, domainName, senderEmail, recipientEmail);
130+
}
131+
132+
/**
133+
* 1. Send a simple HTML email from a verified domain.
134+
*/
135+
private static void sendSimpleEmail(
136+
NylasClient nylas, String domainName, String senderEmail, String recipientEmail)
137+
throws NylasApiError, NylasSdkTimeoutError {
138+
System.out.println("1️⃣ Sending a simple HTML email...");
139+
140+
SendTransactionalEmailRequest request = new SendTransactionalEmailRequest.Builder(
141+
Collections.singletonList(new EmailName(recipientEmail, "Recipient")),
142+
new EmailName(senderEmail, "Sender")
143+
)
144+
.subject("Hello from Nylas Transactional Send!")
145+
.body("<html><body><h1>Hello!</h1>" +
146+
"<p>This is a transactional email sent via the <b>Nylas Java SDK</b>.</p>" +
147+
"</body></html>")
148+
.build();
149+
150+
Response<Message> response = nylas.domains().sendTransactionalEmail(domainName, request);
151+
System.out.println(" ✅ Sent! Message ID: " + response.getData().getId() + "\n");
152+
}
153+
154+
/**
155+
* 2. Send a plain-text-only email (no HTML MIME part).
156+
*/
157+
private static void sendPlaintextEmail(
158+
NylasClient nylas, String domainName, String senderEmail, String recipientEmail)
159+
throws NylasApiError, NylasSdkTimeoutError {
160+
System.out.println("2️⃣ Sending a plain-text email...");
161+
162+
SendTransactionalEmailRequest request = new SendTransactionalEmailRequest.Builder(
163+
Collections.singletonList(new EmailName(recipientEmail, "Recipient")),
164+
new EmailName(senderEmail, "Sender")
165+
)
166+
.subject("Plain-text transactional email")
167+
.body("Hello!\n\nThis message contains no HTML.\n\nRegards,\nNylas SDK")
168+
.isPlaintext(true)
169+
.build();
170+
171+
Response<Message> response = nylas.domains().sendTransactionalEmail(domainName, request);
172+
System.out.println(" ✅ Sent! Message ID: " + response.getData().getId() + "\n");
173+
}
174+
175+
/**
176+
* 3. Send an email with open/link tracking enabled.
177+
*/
178+
private static void sendEmailWithTracking(
179+
NylasClient nylas, String domainName, String senderEmail, String recipientEmail)
180+
throws NylasApiError, NylasSdkTimeoutError {
181+
System.out.println("3️⃣ Sending an email with tracking options...");
182+
183+
TrackingOptions tracking = new TrackingOptions("transactional-demo", true, true, true);
184+
185+
SendTransactionalEmailRequest request = new SendTransactionalEmailRequest.Builder(
186+
Collections.singletonList(new EmailName(recipientEmail, "Recipient")),
187+
new EmailName(senderEmail, "Sender")
188+
)
189+
.subject("Tracked transactional email")
190+
.body("<p>This email has open and link tracking enabled.</p>")
191+
.trackingOptions(tracking)
192+
.build();
193+
194+
Response<Message> response = nylas.domains().sendTransactionalEmail(domainName, request);
195+
System.out.println(" ✅ Sent! Message ID: " + response.getData().getId() + "\n");
196+
}
197+
198+
/**
199+
* 4. Schedule an email to be delivered in the future.
200+
*/
201+
private static void sendScheduledEmail(
202+
NylasClient nylas, String domainName, String senderEmail, String recipientEmail)
203+
throws NylasApiError, NylasSdkTimeoutError {
204+
System.out.println("4️⃣ Scheduling an email to send in 10 minutes...");
205+
206+
// Unix timestamp 10 minutes from now
207+
long sendAt = (System.currentTimeMillis() / 1000) + 600;
208+
209+
SendTransactionalEmailRequest request = new SendTransactionalEmailRequest.Builder(
210+
Collections.singletonList(new EmailName(recipientEmail, "Recipient")),
211+
new EmailName(senderEmail, "Sender")
212+
)
213+
.subject("Scheduled transactional email")
214+
.body("<p>This email was scheduled via <code>send_at</code>.</p>")
215+
.sendAt(sendAt)
216+
.build();
217+
218+
Response<Message> response = nylas.domains().sendTransactionalEmail(domainName, request);
219+
System.out.println(" ✅ Scheduled! Message ID: " + response.getData().getId() + "\n");
220+
}
221+
222+
/**
223+
* 5. Send an email with CC, BCC, reply-to, and custom headers.
224+
*/
225+
private static void sendEmailWithExtras(
226+
NylasClient nylas, String domainName, String senderEmail, String recipientEmail)
227+
throws NylasApiError, NylasSdkTimeoutError {
228+
System.out.println("5️⃣ Sending an email with CC, BCC, reply-to, and custom headers...");
229+
230+
String domainPart = senderEmail.contains("@") ? senderEmail.substring(senderEmail.indexOf('@') + 1) : domainName;
231+
232+
SendTransactionalEmailRequest request = new SendTransactionalEmailRequest.Builder(
233+
Collections.singletonList(new EmailName(recipientEmail, "Primary Recipient")),
234+
new EmailName(senderEmail, "Sender")
235+
)
236+
.subject("Transactional email with extras")
237+
.body("<p>This email includes CC, BCC, reply-to, and custom headers.</p>")
238+
.cc(Collections.singletonList(new EmailName(recipientEmail, "CC Recipient")))
239+
.bcc(Collections.singletonList(new EmailName(recipientEmail, "BCC Recipient")))
240+
.replyTo(Collections.singletonList(new EmailName("support@" + domainPart, "Support")))
241+
.customHeaders(Collections.singletonList(new CustomHeader("X-Campaign-ID", "transactional-demo-001")))
242+
.build();
243+
244+
Response<Message> response = nylas.domains().sendTransactionalEmail(domainName, request);
245+
System.out.println(" ✅ Sent! Message ID: " + response.getData().getId() + "\n");
246+
}
247+
}

0 commit comments

Comments
 (0)