> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sendbaze.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send emails with Go

> Learn how to send your first email from Go with the Sendbaze REST API and net/http.

## Prerequisites

* A Sendbaze [API key](https://docs.sendbaze.com/get-started/create-api-key.md)
* A [verified domain](https://docs.sendbaze.com/get-started/add-domain.md). A test key works without one, and so does the [shared test address](https://docs.sendbaze.com/get-started/test-without-a-domain.md) for emails to yourself.
* Go 1.21 or later

## Guide

**1. Set your API key**

```bash theme={"dark"}
export SENDBAZE_API_KEY=sk_live_xxxxxxxx
```

**2. Send an email**

```go main.go theme={"dark"}
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type sendResponse struct {
	ID     string `json:"id"`
	Status string `json:"status"`
}

type apiError struct {
	Error struct {
		Code    string              `json:"code"`
		Message string              `json:"message"`
		Fields  map[string][]string `json:"fields"`
	} `json:"error"`
}

func main() {
	body, _ := json.Marshal(map[string]any{
		"from":    "Yourshop <orders@yourshop.co.za>",
		"to":      "thandi@example.com",
		"subject": "Your order #1042 has shipped",
		"html":    "<p>Tracking: <b>CG123456789ZA</b></p>",
	})

	req, _ := http.NewRequest(http.MethodPost, "https://api.sendbaze.com/v1/emails", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("SENDBAZE_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	if res.StatusCode >= 400 {
		var e apiError
		json.NewDecoder(res.Body).Decode(&e)
		fmt.Println(e.Error.Code, e.Error.Message, e.Error.Fields)
		return
	}

	var out sendResponse
	json.NewDecoder(res.Body).Decode(&out)
	fmt.Println(out.ID) // em_01j9...
}
```

**3. Try it**

`go run main.go`, then open the dashboard **Logs**.

## Examples

### A small client

```go sendbaze/client.go theme={"dark"}
package sendbaze

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

type Client struct {
	APIKey  string
	BaseURL string
	HTTP    *http.Client
}

type Error struct {
	Status  int
	Code    string
	Message string
	Fields  map[string][]string
}

func (e *Error) Error() string { return fmt.Sprintf("sendbaze: %s (%d): %s", e.Code, e.Status, e.Message) }

func New(apiKey string) *Client {
	return &Client{APIKey: apiKey, BaseURL: "https://api.sendbaze.com", HTTP: &http.Client{Timeout: 30 * time.Second}}
}

func (c *Client) Do(method, path string, in, out any, idempotencyKey string) error {
	var buf bytes.Buffer
	if in != nil {
		if err := json.NewEncoder(&buf).Encode(in); err != nil {
			return err
		}
	}
	req, err := http.NewRequest(method, c.BaseURL+path, &buf)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bearer "+c.APIKey)
	req.Header.Set("Content-Type", "application/json")
	if idempotencyKey != "" {
		req.Header.Set("Idempotency-Key", idempotencyKey)
	}
	res, err := c.HTTP.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode >= 400 {
		var body struct {
			Error struct {
				Code    string              `json:"code"`
				Message string              `json:"message"`
				Fields  map[string][]string `json:"fields"`
			} `json:"error"`
		}
		json.NewDecoder(res.Body).Decode(&body)
		return &Error{Status: res.StatusCode, Code: body.Error.Code, Message: body.Error.Message, Fields: body.Error.Fields}
	}
	if out != nil {
		return json.NewDecoder(res.Body).Decode(out)
	}
	return nil
}
```

### Send with a template and an idempotency key

```go theme={"dark"}
var out struct{ ID string `json:"id"` }
err := client.Do(http.MethodPost, "/v1/emails", map[string]any{
	"from":       "orders@yourshop.co.za",
	"to":         "thandi@example.com",
	"templateId": "tpl_01j9x7k2r4m8q3v5n6b7c8d9e0",
	"variables":  map[string]any{"name": "Thandi", "order_id": 1042, "eta": "Friday"},
}, &out, "order-1042-shipped")
```

### Verify a webhook

```go theme={"dark"}
func verify(rawBody []byte, header, secret string) bool {
	parts := map[string]string{}
	for _, kv := range strings.Split(header, ",") {
		if k, v, ok := strings.Cut(kv, "="); ok {
			parts[k] = v
		}
	}
	ts, err := strconv.ParseInt(parts["t"], 10, 64)
	if err != nil || math.Abs(float64(time.Now().Unix()-ts)) > 300 {
		return false
	}
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(parts["t"] + "."))
	mac.Write(rawBody)
	expected := hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(expected), []byte(parts["v1"]))
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.