Send emails from Go
Transactional and marketing email from any Go service. One net/http request, and a message ID you can follow to the inbox.
✓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 Go?
Three steps from no account to a queued email. The standard library is enough.
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. Your code reads it with os.Getenv("AEM_KEY").
Terminalexport AEM_KEY="aem_..."Send the email
POST the recipient and a published template as JSON. Anything but a 2xx returns an error, and a retry with the same Idempotency-Key won't send a second email.
send_welcome.gobody, _ := json.Marshal(map[string]string{"to": user.Email, "template": "welcome"}) req, err := http.NewRequest(http.MethodPost, "https://sendandretain.com/api/v1/emails", bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Authorization", "Bearer "+os.Getenv("AEM_KEY")) req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "welcome-"+user.ID) res, err := http.DefaultClient.Do(req) if err != nil { return err } defer res.Body.Close() if res.StatusCode >= 300 { return fmt.Errorf("send failed: %s", res.Status) }Keep the message ID
A 202 means queued, not delivered. Store the id, and read delivery, bounce, and complaint events from your signed webhook.
202 Accepted{ "id": "msg_7ca31", "status": "queued" }
[ 02 / 04 ]In production
What does a production Go integration need?
Deadlines on every request
Pass the caller's context and set a timeout. Struct tags map cleanly onto the JSON payload.
Retries with the same key
Retry transport errors and 5xx responses with the same Idempotency-Key. Don't retry validation or auth errors; fix the request instead.
Webhooks checked on the raw body
Read the body with io.ReadAll before decoding, 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 Go?
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 small Send & Retain email client in Go with net/http, a context deadline, and typed errors for transport failures versus API errors.”
Retries
“Add retries with backoff to our email sends. Reuse the same Idempotency-Key and never retry 4xx responses.”
Webhooks
“Add an HTTP handler 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 ]Related
More ways to send email from Go
Questions people ask us
Send your first email from Go.
Create a key, make one call, and follow the message ID to the inbox.