Send email from Go with an email API
Send email from Go with net/http and encoding/json: one POST to the API, an id back, and delivery events on a signed webhook.
// net/http and encoding/json are all you need.package main
import ( "bytes" "encoding/json" "fmt" "net/http" "os")
const url = "https://api.rasket.com/emails"
func main() { payload, err := json.Marshal(map[string]any{ "from": "Acme <orders@send.acme.example>", "to": []string{"ronald.williams@example.com"}, "subject": "Your order has shipped", "html": "<p>Order 1042 shipped today.</p>", }) if err != nil { panic(err) }
body := bytes.NewReader(payload) req, err := http.NewRequest("POST", url, body) if err != nil { panic(err) }
key := os.Getenv("RASKET_API_KEY") req.Header.Set("Authorization", "Bearer "+key) // Required on every request. req.Header.Set("User-Agent", "acme-billing/1.0") req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "order-1042")
res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close()
var email struct { ID string `json:"id"` } err = json.NewDecoder(res.Body).Decode(&email) if err != nil { panic(err) }
fmt.Println(email.ID)}What sending from Go actually involves
To send email from Go you need net/http, encoding/json and a key — everything else the standard library already gives you. There is no Go SDK, and the sample below shows why one is not urgent: the whole send is a marshalled map, four headers and a decoded response. net/smtp is the alternative, and it means running or renting a relay.
Last updated 2026-09-16. Every field, header and limit below is the one the emails reference carries.
What you get with the key
Five things that arrive with the first request rather than with a later plan.
Idempotent sends
Put an Idempotency-Key on a send and repeat it as often as you like. For 24 hours the same key and payload return the first response instead of a second email, and a different payload under that key is refused rather than sent.
Signed webhooks you can replay
Delivery, bounce, complaint, open and click posted to your endpoint with a timestamp and a signature, retried on failure and replayable from the dashboard.
Inbound mail on your own domain
Receive at your domain on every plan. Each message arrives on a signed webhook with its headers, text and HTML, and its attachments behind signed links.
Templates with typed variables
Publish a template, then send it by ID or alias with the values it declares. Every email records the exact version it was rendered from.
Broadcasts and automations
Contacts with typed properties, topics people subscribe to, and per-contact workflows that wait, branch and send — through the same pipeline as the rest of your mail.
How to send email from Go
Six steps, in this order. Everything before the fifth is done once, and only the fifth is about the language you write in.
- 1
Create an API key
Open API keys in the dashboard and create one. It is shown once and stored hashed, so copy it then. Put it in your environment as RASKET_API_KEY and read it from there; a key committed to a repository is a key you have to rotate.
- 2
Add a sending domain
POST /domains with a domain you control. A subdomain such as send.acme.example is the usual choice, because it keeps this mail's reputation separate from the address your people write from. The response carries a records array.
- 3
Publish the DNS records
Every row of records goes into your DNS: the DKIM TXT record at rasket._domainkey, and the two records that give the domain its own return path. Nothing sends from the domain until they resolve.
- 4
Verify the domain
POST /domains/{domain_id}/verify. The domain comes back with a status on each record, and once it reads verified you can send from any address on it. An unverified sender is refused rather than quietly dropped.
- 5
Send your first email from Go
Post the message with the sample above. Carry an Idempotency-Key — yours or your queue's retry cannot then turn into a second email — and a User-Agent, which every request has to have. A 200 with an id means we have taken responsibility for the message.
- 6
Subscribe a webhook and check its signature
POST /webhooks with an https endpoint and the events you care about. Delivery, bounce, complaint, open and click arrive there with svix-id, svix-timestamp and svix-signature; verify against the raw body before anything parses it.
Verify webhooks in Go
io.ReadAll(r.Body) before anything decodes it: json.NewDecoder(r.Body) consumes the reader and the bytes are gone. Hand the slice and r.Header to the svix-webhooks module's Verify, and decode only after it returns nil. Keep a copy of the bytes if you need both the event and the raw source.
The five steps a verifier performs, in seven languages, are on the webhooks reference.
Read the long version
Sending email from Go: the full guide covers attachments, scheduling, suppressions and what to do when a message bounces.
Questions about Go
Do I need an SMTP server to send email from Go?
No. net/smtp needs a relay to talk to, which means running one or paying for one and keeping its credentials. An HTTPS API needs a key and a verified domain, and the send is the twenty lines above. Rasket offers no SMTP relay, so net/smtp is not an option against it.
Is there a Go SDK?
Not today. The API is plain JSON over HTTPS and the sample above is the whole of a send, so a client is a convenience rather than a requirement. The OpenAPI document is published, if you would rather generate one than write it.
Where do I keep the API key in a Go service?
In the environment, read once at startup with os.Getenv and checked for empty before the first request, so a misconfigured deployment fails immediately rather than collecting 401s. Build the http.Client once and reuse it; the default one is fine.
How do I stop a retry from sending the same email twice?
Set the Idempotency-Key header from whatever the mail is about rather than from a random value, and reuse it when you retry. For 24 hours the same key with the same payload returns the first response instead of sending again, which is what makes an at-least-once queue safe here.
How do I verify a Rasket webhook in Go?
go get github.com/svix/svix-webhooks/go, read the body with io.ReadAll before anything decodes it, and call Verify with those bytes and r.Header. It checks the three svix headers and the timestamp window; decode the JSON only once it has returned without an error.
Keep reading
Make your first send
Create a key, verify a domain, and post your first message from Go. The free plan does not expire.