Every dashboard action is an MCP tool your agent can call.Read the docs

Send emails from Java

Transactional and marketing email from any JVM service. One request with Java's HttpClient, and a message ID you can follow to the inbox.

✓Free to start. Unlimited contacts on every tier.

Server quickstart
Java

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" }

RuntimeServer process
RequestPOST /api/v1/emails
Result202 · queued
Credentials stay server-side

[ 01 / 04 ]Quickstart

How do I send email from Java?

Three steps from no account to a queued email. Java 17's HttpClient is enough.

  1. Create an API key

    In the dashboard, open Settings → API & MCP and create a key with the Send + manage permission (the default). It starts with aem_ and is shown once.

  2. Store it as an environment variable

    Keep the key on your server. Your code reads it with System.getenv("AEM_KEY").

    Terminal
    export AEM_KEY="aem_..."
  3. Send the email

    POST the recipient and a published template as JSON (built here with Jackson). Anything but a 2xx throws, and a retry with the same Idempotency-Key won't send a second email.

    EmailService.java
    var client = HttpClient.newHttpClient(); // create once and reuse it
    var payload = new ObjectMapper()
        .writeValueAsString(Map.of("to", user.email(), "template", "welcome"));
    var request = HttpRequest.newBuilder(URI.create("https://sendandretain.com/api/v1/emails"))
        .header("Authorization", "Bearer " + System.getenv("AEM_KEY"))
        .header("Content-Type", "application/json")
        .header("Idempotency-Key", "welcome-" + user.id())
        .POST(HttpRequest.BodyPublishers.ofString(payload))
        .build();
    var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    if (response.statusCode() / 100 != 2) {
      throw new IllegalStateException("Send failed: " + response.statusCode() + " " + response.body());
    }
  4. Keep the message ID

    A 202 means queued, not delivered. Store the id; delivery, bounce, and complaint events arrive later on your signed webhook.

    202 Accepted
    { "id": "msg_7ca31", "status": "queued" }

[ 02 / 04 ]In production

What does a production Java integration need?

One reused HttpClient

Set connection and request timeouts once. Wrap it in Spring or another framework only when that helps.

Retries with the same key

Send from a service or worker after the transaction commits. Retry timeouts and 5xx errors with the same Idempotency-Key.

Webhooks checked on the raw body

Take the body as a String in your controller so the signature covers the exact bytes.

Events for marketing email

Send product events like signup or trial started. Automations you've turned on decide which email goes out.

[ 03 / 04 ]Example prompts

What can you ask your coding agent to do in Java?

Paste these into Claude Code, Cursor, or Codex. Connect the MCP server and it can build the templates and automations too.

  • Email service

    “Write an EmailService that sends through the Send & Retain API with one shared HttpClient, explicit timeouts, and the user ID as the idempotency key.”

  • After commit

    “Send our order confirmation from Send & Retain only after the order transaction commits, not from an entity lifecycle hook.”

  • Webhooks

    “Add a Spring controller that verifies Send & Retain webhook signatures on the raw body and flags bounced addresses on our users.”

  • Trial automation

    “Send a trial_started event to Send & Retain when a trial begins, then build a paused trial automation for it.”

Questions people ask us

Send your first email from Java.

Create a key, make one call, and follow the message ID to the inbox.