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

Send emails from Rust

Transactional and marketing email from async Rust services. One reqwest call, and a message ID you can follow to the inbox.

✓Free to start. Unlimited contacts on every tier.

Server quickstart
Rust

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

Three steps from no account to a queued email. No crate of ours 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 std::env::var("AEM_KEY").

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

    POST the recipient and a published template with reqwest (json feature on). error_for_status() turns a 4xx or 5xx into an error, and a retry with the same Idempotency-Key won't send a second email.

    send_welcome.rs
    let client = reqwest::Client::new(); // build once and reuse it
    let response = client
        .post("https://sendandretain.com/api/v1/emails")
        .bearer_auth(std::env::var("AEM_KEY")?)
        .header("Idempotency-Key", format!("welcome-{}", user.id))
        .json(&serde_json::json!({ "to": user.email, "template": "welcome" }))
        .send()
        .await?
        .error_for_status()?;
  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 Rust integration need?

One reused async client

Build the reqwest client once with a timeout, and model the request and response with your own serde types.

Retries with the same key

Keep transport errors, API errors, and accepted sends distinct. Retry only the temporary ones, with the same Idempotency-Key.

Webhooks checked on the raw body

Take the body as Bytes before deserialising, 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 Rust?

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

  • Email client

    “Write a Send & Retain email client with reqwest and serde, one shared client, and an error enum that separates transport, API, and accepted results.”

  • Retries

    “Add retries with backoff to our email sends. Reuse the same Idempotency-Key and never retry 4xx responses.”

  • Webhooks

    “Add an axum handler that verifies Send & Retain webhook signatures on the raw Bytes body and flags bounced addresses in our database.”

  • 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 Rust.

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