# Go

Send your first email from Go in three steps: install the SDK, write a handler that sends, and run it. This quickstart uses the standard library's `net/http`, no framework required.

## 1. Install

```bash
go get github.com/messagebird/bird-sdk-go
```

The SDK requires Go 1.24+. Grab an API key from **Developers → API keys** in the dashboard and export it; the SDK infers the region from the key's `bk_us1_` / `bk_eu1_` prefix, so nothing else needs configuring:

```bash
export BIRD_API_KEY="bk_us1_..."
```

## 2. Send

Create `main.go` with an HTTP handler that sends through Bird's shared onboarding domain, so no domain verification is needed:

<!-- bird:snippet quickstart-email -->

```go
package main

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"os"

	bird "github.com/messagebird/bird-sdk-go"
	"github.com/messagebird/bird-sdk-go/option"
)

func main() {
	client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("POST /send", func(w http.ResponseWriter, r *http.Request) {
		msg, err := client.Email.Send(r.Context(), bird.EmailSendParams{
			From:    "onboarding@messagebird.dev",
			To:      []string{"delivered@messagebird.dev"},
			Subject: "Hello from Bird",
			HTML:    "<p>My first Bird email.</p>",
		})
		if err != nil {
			var apiErr *bird.APIError
			if errors.As(err, &apiErr) {
				http.Error(w, apiErr.Error(), apiErr.StatusCode)
				return
			}
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
		_ = json.NewEncoder(w).Encode(msg)
	})

	log.Fatal(http.ListenAndServe(":3000", nil))
}
```

Every `Send` is automatically idempotent: the SDK generates one idempotency key per call and reuses it across retries, so transient failures (timeouts, 429s, 5xx) are retried without ever double-delivering.

## 3. Try it

```bash
go run . &
curl -X POST localhost:3000/send
```

Bird responds with `202`, accepting the email for asynchronous delivery, and the handler writes the message back out. The `*_count` fields track your recipients through the delivery states; right now one recipient is accepted and none are delivered:

```json
{
  "id": "em_01ky7ma8y2es1s2akzk53tmjn0",
  "status": "accepted",
  "category": "marketing",
  "from": { "email": "onboarding@messagebird.dev" },
  "to": [{ "email": "delivered@messagebird.dev" }],
  "subject": "Hello from Bird",
  "accepted_count": 1,
  "processed_count": 0,
  "delivered_count": 0,
  "deferred_count": 0,
  "bounced_count": 0,
  "complained_count": 0,
  "rejected_count": 0,
  "open_count": 0,
  "click_count": 0,
  "track_opens": true,
  "track_clicks": true,
  "created_at": "2026-07-23T13:58:20.866Z"
}
```

Because the recipient is the [`delivered@messagebird.dev` sandbox address](/docs/guides/email/testing-sandbox), delivery is guaranteed. Fetch the message by its `em_` ID with `client.Email.Get` and watch the status move to `delivered`.

## Next steps

- [Go SDK reference](/docs/sdks/go): every method, param, and error type.
- [Send your first email](/docs/get-started/send-your-first-email): the same flow with curl and the dashboard.
- [Sending domains](/docs/guides/email/sending-domains): verify your own domain for production sending.
- [Email API reference](/docs/api/reference/create-email-message): the full request and response schema.