Send Spring Boot email without confusing async with durable. Accept quickly, retry deliberately, trace delivery.
Put the API call in a focused service, choose @Async or a durable job based on the failure model, and retain the accepted message ID through delivery.
✓Inject the key through externalized configuration; never ship it in an application artifact.
One request, complete trace
Request
POST /api/v1/emails
Authorization: Bearer aem_…
Idempotency-Key: welcome-8f21
{ "to": "[email protected]", "template": "welcome" }
202 Accepted
{ "id": "msg_7ca31", "status": "queued" }
[ 01 / 03 ]Integration shape
Give each layer one responsibility.
Use services, events, and scheduled jobs
A service owns the call; @Async moves it off the request thread, while a durable worker or batch job survives restarts.
Publish product events
Publish lifecycle events after the transaction commits, so flows never act on state that Spring later rolls back.
Verify the webhook
A @RestController taking the body as String so the HMAC is computed over the exact bytes.
[ 02 / 03 ]Server quickstart
Queue one Spring Boot email with a stable request key.
The accepted message ID gives your service a trace; the idempotency key keeps a retried job from creating another send.
Run the client with explicit timeouts and keep the returned message ID.
var request = HttpRequest.newBuilder(URI.create(EMAIL_URL))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.header("Idempotency-Key", "welcome-" + user.id())
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = client.send(request, BodyHandlers.ofString());Accepted response
{ "id": "msg_7ca31", "status": "queued" }Questions people ask us
Make Spring Boot email retry-safe and observable.
Queue one message from a service and follow its accepted ID through delivery.