Send Email with Java
Send transactional email from Java using the built-in HTTP client or SMTP relay through Jakarta Mail.
TL;DR
Use Java's HTTP client for new transactional email code and Jakarta Mail SMTP for apps that already use JavaMail-style configuration. Keep provider calls behind one adapter and update final state from webhooks.
What you will learn
- Send email from Java through a REST API
- Configure SMTP relay for Java frameworks
- Avoid duplicate sends during retries
REST API example
Java 11 and newer include java.net.http.HttpClient.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
var json = """
{
"from": "Acme <alerts@example.com>",
"to": ["user@example.com"],
"subject": "Welcome",
"html_body": "<p>Hello from Acme</p>",
"text_body": "Hello from Acme",
"tags": ["onboarding"]
}
""";
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.postscale.io/v1/send"))
.header("Authorization", "Bearer " + System.getenv("POSTSCALE_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
var response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new IllegalStateException("Email send failed: " + response.body());
}
In production, build JSON with your normal serializer rather than string concatenation.
SMTP relay with Jakarta Mail
Use SMTP relay for older applications and frameworks that already use mail sessions.
mail.smtp.host=smtp.postscale.io
mail.smtp.port=587
mail.smtp.auth=true
mail.smtp.starttls.enable=true
Store SMTP credentials in secrets management. Do not reuse a dashboard password.
Service design
Create one EmailSender interface:
public interface EmailSender {
void sendTransactional(EmailMessage message);
}
Your controllers and jobs should not know whether the current implementation uses Postscale, SendGrid, Mailgun, SES, SMTP, or a test fake.
Retry and idempotency
- Retry temporary transport failures with backoff.
- Do not retry validation errors.
- Store an application event ID for each send.
- Process webhooks idempotently by event ID.
- Keep final delivery state separate from initial accepted state.
Read SMTP errors and retries before moving high-volume Java services.
References
Frequently asked questions
- Should Java services send with REST or SMTP?
- Use REST for new service code. Use SMTP when the existing app or framework already uses JavaMail or Jakarta Mail settings.
- Does API accepted mean delivered?
- No. Accepted means the provider queued or accepted the message. Use webhooks for delivered, bounced, deferred, and complained state.
Related guides
Put the guide into production
Postscale brings sending, inbound processing, DMARC reporting, and masked addresses behind one API so the operational pieces stay connected.