Send email with Java
Send transactional email from Java through PostStack.
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 — see the Spring Boot guide.
Quickstart#
Install the SDK
Nothing to install:
java.net.http.HttpClientis part of the JDK since Java 11, and the examples use text blocks (Java 15+). Check your version:bashjava -version # 15 or newerInitialize the client
javaimport 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");Send an email
javaimport 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()); } }Handle errors
javaimport 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 { // One key for every attempt, so a retry never delivers a second copy. String idempotencyKey = java.util.UUID.randomUUID().toString(); 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") .header("Idempotency-Key", idempotencyKey) .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} and properties.mail.smtp.starttls.enable: true (STARTTLS on 587). For port 465 set properties.mail.smtp.ssl.enable: true instead. 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.
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.
Ready to send from Java?
Create a free account and an API key — no card needed.