Send FastAPI email without blocking the endpoint. Know when BackgroundTasks is enough.
Queue an email with an async HTTP call, keep its accepted message ID, and move to a durable worker only when the surrounding application work requires one.
✓Use httpx directly or BackgroundTasks for short in-process work; use a durable worker for guaranteed local execution.
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
Choose the right async boundary for the work.
Use typed endpoints and background tasks
Call with httpx when the accepted ID belongs in the response; use BackgroundTasks only for work that may stay in the web process.
Publish product events
Publish typed product events after the state change succeeds, so lifecycle flows respond to application truth.
Verify the webhook
An endpoint typed to take Request so you can await request.body() before validation.
[ 02 / 03 ]Server quickstart
Queue one email from FastAPI with a stable retry key.
The API accepts the message and returns its ID; a stable idempotency key protects task or client retries.
Use an async HTTP client when this runs inside an async framework.
response = requests.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"}, timeout=10,
)
response.raise_for_status()Accepted response
{ "id": "msg_7ca31", "status": "queued" }[ 03 / 03 ]Related
Keep building with FastAPI
Questions people ask us
Give FastAPI email an async path you can trace.
Queue one message, keep its accepted ID, and handle delivery separately.