Send emails from any language
One REST API for transactional and marketing email. Pick your language for a copy-paste example, or call it from anything that speaks HTTP.
✓Free to start. Unlimited contacts on every tier.
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 / 04 ]Quickstart
How do I send email from my server?
Every language guide follows the same three steps. Here they are with curl.
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.
Store it as an environment variable
Keep the key on your server, never in browser or mobile code.
Terminalexport AEM_KEY="aem_..."Send the email
POST the recipient and the slug of a published template. A retry with the same Idempotency-Key won't send a second email.
Terminalcurl -X POST https://sendandretain.com/api/v1/emails \ -H "Authorization: Bearer $AEM_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: welcome-123" \ -d '{"to": "[email protected]", "template": "welcome"}'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 every email integration need?
The language changes. These four habits don't.
Send from the server
Call the API after the action commits, with the key read from server config.
Retry with the same key
Retry timeouts and 5xx errors with the same Idempotency-Key. Don't retry validation or auth errors.
Verify webhooks on the raw body
Check the signature before any JSON parsing, then handle repeats safely.
Send events for marketing email
Post product events like signup. Automations you've turned on decide what goes out.
Built into every step
[ 03 / 04 ]Example prompts
What can you ask your coding agent to do?
Paste these into Claude Code, Cursor, or Codex. Connect the MCP server and it can build the templates and automations too.
First send
“Find where we create users and send a welcome email with the Send & Retain API. Read the key from AEM_KEY and use the user ID as the idempotency key.”
Retries
“Add retries to our email sends. Reuse the same Idempotency-Key and never retry 4xx errors.”
Webhooks
“Add an endpoint that verifies Send & Retain webhook signatures on the raw 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.”
[ 04 / 04 ]Languages
Which languages have a guide?
Each guide has a copy-paste send call and the production habits for that language.
Node.js
Send email with fetch from TypeScript or JavaScript.
Python
Send email with requests or httpx, sync or async.
PHP
Send email with Laravel's client, Guzzle, or PSR-18.
Ruby
Send email with Net::HTTP from a service or job.
Go
Send email with net/http and context deadlines.
Java
Send email with Java 17's HttpClient.
C# and .NET
Send email with HttpClient and IHttpClientFactory.
Rust
Send email with reqwest and serde.
Elixir
Send email with Req from a context or Oban job.
Questions people ask us
Send your first email today.
Create a key, make one call, and follow the message ID to the inbox.