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

Send emails from Elixir

Transactional and marketing email from Elixir apps and Oban jobs. One Req call, and a message ID you can follow to the inbox.

✓Free to start. Unlimited contacts on every tier.

Server quickstart
Elixir

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 Elixir?

Three steps from no account to a queued email. No Hex package needed.

  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.fetch_env!("AEM_KEY").

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

    POST the recipient and a published template. Req doesn't raise on a 4xx or 5xx, so match on the status. A retry with the same Idempotency-Key won't send a second email.

    lib/app/email.ex
    response =
      Req.post!("https://sendandretain.com/api/v1/emails",
        headers: [
          authorization: "Bearer #{System.fetch_env!("AEM_KEY")}",
          idempotency_key: "welcome-#{user.id}"
        ],
        json: %{to: user.email, template: "welcome"}
      )
    
    case response do
      %{status: status, body: body} when status in 200..299 -> {:ok, body["id"]}
      %{status: status, body: body} -> {:error, {status, body}}
    end
  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 Elixir integration need?

A context around the call

Wrap the request in one context with the endpoint, key, timeout, and error handling. Req and Finch both work.

Oban retries with the same key

Derive one Idempotency-Key per email and reuse it on every attempt, so a retried job never sends twice.

Webhooks checked on the raw body

Cache the raw body in a custom Plug body reader; Plug.Parsers consumes it otherwise.

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 Elixir?

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

  • Welcome email

    “Add an Emails context that sends a welcome email through the Send & Retain API with Req, using the user ID as the idempotency key.”

  • Oban

    “Move our email sends into an Oban worker that reuses the same Idempotency-Key on every attempt and discards 4xx errors instead of retrying.”

  • Webhooks

    “Add a Phoenix endpoint that caches the raw body, verifies Send & Retain webhook signatures, 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 Elixir.

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