Send Phoenix email with retries that do not become duplicates. Oban runs the job; the message key keeps it safe.
Queue transactional messages from a context or Oban worker, reuse one logical key across retries, and trace each accepted ID through delivery.
✓Use it beside or instead of Swoosh when product email needs lifecycle flows and delivery history.
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
Match BEAM reliability with an idempotent send boundary.
Use controllers, contexts, and Oban jobs
Oban owns durable local execution; a stable idempotency key keeps repeated jobs tied to one logical message.
Publish product events
Publish product events from contexts after the data change succeeds, so lifecycle behavior stays outside controllers.
Verify the webhook
A Plug with a custom body reader that caches the raw body for signature verification.
[ 02 / 03 ]Server quickstart
Queue one email from a Phoenix context or worker.
The request returns an accepted message ID while Oban remains responsible for your application's local retry policy.
Call from a context or supervised job and retain the accepted message ID.
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"}
)Accepted response
{ "id": "msg_7ca31", "status": "queued" }[ 03 / 03 ]Related
Keep building with Phoenix
Questions people ask us
Give your next Phoenix email a retry-safe trail.
Queue it from a context or Oban worker and follow its accepted ID to delivery.