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

Send emails with FastAPI

Transactional and marketing email from a FastAPI endpoint. One HTTP call returns an id you can follow to the inbox.

✓Free to start. Unlimited contacts on every tier.

Server quickstart
Fastapi

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 / 05 ]Quickstart

How do I send email from FastAPI?

Create a key, keep it on the server, and call one endpoint from a FastAPI endpoint.

  1. Get an API key

    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 a server-side environment variable

    Set AEM_KEY in the environment of every process that sends, web and workers alike. The code reads it with os.environ.

    Environment
    AEM_KEY=aem_…
  3. Send the email

    httpx.AsyncClient posts to the email API from your async code, and raise_for_status() raises on a 4xx or 5xx. A retry with the same Idempotency-Key won't send a second email.

    send_welcome.py
    import os
    import httpx
    
    async def send_welcome(user) -> dict:
        async with httpx.AsyncClient(timeout=10) as client:
            response = await client.post(
                "https://sendandretain.com/api/v1/emails",
                headers={"Authorization": f"Bearer {os.environ['AEM_KEY']}",
                         "Idempotency-Key": f"welcome-{user.id}"},
                json={"to": user.email, "template": "welcome"},
            )
        response.raise_for_status()
        return response.json()
  4. Check the response

    A 202 means queued, not delivered. Keep the id, then follow delivery in the message log or through a signed webhook.

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

[ 02 / 05 ]Where it goes

Where should email code live in your FastAPI app?

Endpoints and background tasks

Call with httpx when the id belongs in the response. BackgroundTasks run in the web process, so use them only for short work.

Typed product events

Send typed events after the state change succeeds, so automations follow what really happened.

A raw body for webhooks

Type the endpoint to take Request and await request.body() before validation.

[ 03 / 05 ]Marketing email

Can I send marketing emails from FastAPI too?

Yes. Send an event from the same endpoint when a user signs up, starts a trial, or goes quiet, and an automation sends the right email. Automations start paused, and every email goes to one person: no newsletter blasts.

  • Transactional and marketing email share one contact record and one unsubscribe list.
  • Unsubscribes, bounces, and complaints are suppressed for you before every send.
  • Templates are react.email components; published versions never change under you.

[ 04 / 05 ]Example prompts

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

Claude Code, Cursor, and Codex can do these over the Send & Retain MCP server, with your repo open. Automations they build stay paused until you turn them on.

  • Welcome email

    “Add a welcome email to our FastAPI signup endpoint using Send & Retain and httpx, with an idempotency key.”

  • Magic link

    “Add a sign-in link email to our FastAPI auth router, render it in our brand, and send me a test.”

  • Webhook

    “Add a FastAPI endpoint that verifies Send & Retain delivery webhooks and marks bounced users.”

  • Trial automation

    “Send a trial_started event from our FastAPI service and build a paused trial-conversion automation for it.”

Questions people ask us

Send your first email from FastAPI today.

Create a key, paste the code into a FastAPI endpoint, and watch the email land in the message log.