Send emails with Java
Learn how to send transactional emails using PostStack and Java.
Java 11 introduced `java.net.http.HttpClient` as a first-class HTTP client in the stdlib, removing the need for Apache HttpClient, OkHttp, or RestTemplate for simple REST integrations. PostStack accepts a small JSON POST with Bearer auth, so the stdlib client is plenty. For Spring Boot apps, integrating via `JavaMailSender` and SMTP is usually cleaner so `@Async` and `@Retryable` annotations Just Work.
1. Install the SDK
// No external dependencies — uses Java 11+ HttpClient2. Initialize the client
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
String apiKey = System.getenv("POSTSTACK_API_KEY");3. Send an email
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class SendEmail {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("POSTSTACK_API_KEY");
String json = """
{
"from": "hello@yourdomain.com",
"to": ["user@example.com"],
"subject": "Hello from Java!",
"html": "<h1>Welcome!</h1>"
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.poststack.dev/emails"))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}4. Handle errors
Java idioms for error handling, retries, and structured logging when calling the PostStack API.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class PoststackClient {
private static final HttpClient HTTP = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
public static String send(String json) throws Exception {
for (int attempt = 0; attempt < 3; attempt++) {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.poststack.dev/emails"))
.header("Authorization", "Bearer " + System.getenv("POSTSTACK_API_KEY"))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(10))
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> res = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
if (res.statusCode() >= 200 && res.statusCode() < 300) {
return res.body();
}
if (res.statusCode() == 429 || res.statusCode() >= 500) {
Thread.sleep((long) Math.pow(2, attempt) * 1000);
continue;
}
throw new RuntimeException("PostStack " + res.statusCode() + ": " + res.body());
}
throw new RuntimeException("PostStack: retries exhausted");
}
}Framework integrations
Spring Boot — JavaMailSender + SMTP
In `application.yml`, set `spring.mail.host: smtp.poststack.dev`, `port: 587`, `username: poststack`, `password: ${POSTSTACK_API_KEY}`, `properties.mail.smtp.starttls.enable: true`. Inject `JavaMailSender` into your services and use `@Async` to make sends non-blocking.
Spring Boot — WebClient
For non-blocking REST calls, use Spring WebFlux’s `WebClient`. Configure a `@Bean` with the API key on construction, then `webClient.post().uri("/emails").bodyValue(payload).retrieve().bodyToMono(EmailResponse.class)`. Pair with `Resilience4j` for retry policies.
Quarkus / Micronaut
Both frameworks have first-class declarative HTTP clients. Define a `@RegisterRestClient` (Quarkus) or `@Client` (Micronaut) interface with the PostStack endpoint, then inject and call. CDI handles the singleton lifetime.
Plain JVM apps
For non-Spring apps, build an `HttpClient` singleton at startup and share it across threads. The Java stdlib `HttpClient` is thread-safe and pools connections internally.
Common pitfalls
Creating a new HttpClient per request
`HttpClient.newHttpClient()` spins up an internal executor and connection pool — recreating it per request leaks threads. Cache a single instance at class level or via DI.
Forgetting `.timeout(...)`
Without `.connectTimeout()` and `.timeout()`, a stuck connection blocks the calling thread indefinitely. Always set both — 5 seconds connect, 10 seconds total is a reasonable default.
Mixing `send()` and `sendAsync()`
`HttpClient.send` blocks; `sendAsync` returns `CompletableFuture`. Don’t call `.get()` on the future inside a blocking handler — it just adds context-switch overhead. Pick one model and stay consistent.
Notes
- Uses the Java 11+ built-in HttpClient — no Maven/Gradle dependencies needed
FAQ
Do I need Apache HttpClient or OkHttp?
No. Java 11+ has a stdlib HttpClient that is more than enough for PostStack’s REST API. Skip the extra dependency.
How do I integrate with Spring Boot?
For SMTP, configure JavaMailSender via `application.yml`. For REST, define a `WebClient` bean or use the stdlib `HttpClient` directly. Both are detailed in the integrations section.
Is `HttpClient` thread-safe?
Yes. A single `HttpClient` instance is thread-safe and pools connections internally. Share one instance across your application.
Related guides
Ready to send emails with Java?
Create a free account and get your API key in under a minute.