---
title: "Go SDK Quickstart"
canonical: "https://test.abhinandan.one/go-sdk-quickstart-81405d38"
markdown_url: "https://test.abhinandan.one/go-sdk-quickstart-81405d38.md"
publisher: "Primitive SDKs"
kind: "quickstart"
content_type: "reference"
category: "Go SDK"
description: "Run go get github.com/primitivedotdev/sdks/sdk-go, verify a webhook with primitive.Receive, and reply with client.Reply in one Go program."
keywords: ["go get github.com/primitivedotdev/sdks/sdk-go", "primitive.Receive", "primitive.NewClient", "client.Reply", "PRIMITIVE_WEBHOOK_SECRET", "Go SDK quickstart"]
last_modified: "2026-08-11T18:54:50.48073+00:00"
published_at: "2026-08-11T18:54:49.896145+00:00"
source_files:
  - "sdk-go/README.md"
sections:
  - {anchor: "step-install-the-sdk", title: "Install the SDK"}
  - {anchor: "step-set-your-api-key-and-webhook-secret", title: "Set your API key and webhook secret"}
  - {anchor: "step-verify-a-webhook-and-reply-to-it", title: "Verify a webhook and reply to it"}
  - {anchor: "step-verify-it-worked", title: "Verify it worked"}
  - {anchor: "send-a-new-email-instead-of-replying", title: "Send a new email instead of replying"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Go SDK Quickstart

Install github.com/primitivedotdev/sdks/sdk-go, verify an inbound webhook into a normalized email, and send a reply, all in a single Go program.

Get from zero to a verified inbound email and a sent reply with the Go SDK, `github.com/primitivedotdev/sdks/sdk-go`, in one program.

> **Note:** Requires Go **1.25 or newer**. You'll also need a Primitive API key (`prim_test` in the examples below) and a webhook signing secret (`whsec_...`) from your dashboard.

### 1. Install the SDK

```bash
go get github.com/primitivedotdev/sdks/sdk-go@latest
```

This pulls in the root `primitive` package (the high-level email client, x402 payments, and webhook helpers) plus the sibling `github.com/primitivedotdev/sdks/sdk-go/api` package, the generated HTTP client for the full API surface.

### 2. Set your API key and webhook secret

Export both as environment variables so they never land in source control:

```bash
export PRIMITIVE_API_KEY=prim_test
export PRIMITIVE_WEBHOOK_SECRET=whsec_...
```

`PRIMITIVE_API_KEY` authenticates outbound calls (`client.Send`, `client.Reply`). `PRIMITIVE_WEBHOOK_SECRET` verifies that inbound webhook deliveries actually came from Primitive.

### 3. Verify a webhook and reply to it

`primitive.Receive` verifies the `Primitive-Signature` header, parses the raw body, and returns a normalized `ReceivedEmail`. Pass that email straight into `client.Reply`, which derives the recipient, subject (`Re: <parent>`), and threading headers server-side.

```go
// main.go
package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	primitive "github.com/primitivedotdev/sdks/sdk-go"
)

var client *primitive.Client

func handler(w http.ResponseWriter, r *http.Request) {
	// Verification runs over the exact request bytes, so read the raw body
	// before any JSON decoding.
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "could not read body", http.StatusBadRequest)
		return
	}

	headers := map[string]string{
		"Primitive-Signature": r.Header.Get("Primitive-Signature"),
		"X-Webhook-Event":     r.Header.Get("X-Webhook-Event"),
	}

	email, err := primitive.Receive(primitive.HandleWebhookOptions{
		Body:    body,
		Headers: headers,
		Secret:  os.Getenv("PRIMITIVE_WEBHOOK_SECRET"),
	})
	if err != nil {
		log.Printf("invalid webhook: %v", err)
		http.Error(w, "invalid webhook", http.StatusBadRequest)
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
	defer cancel()

	if _, err := client.Reply(ctx, email, primitive.ReplyParams{
		BodyText: "Thank you for your email.",
	}); err != nil {
		log.Printf("reply failed: %v", err)
		http.Error(w, "reply failed", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func main() {
	var err error
	client, err = primitive.NewClient(os.Getenv("PRIMITIVE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/webhooks/email", handler)
	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
```

### 4. Verify it worked

Send a test webhook delivery to `/webhooks/email` from your Primitive dashboard. On success:

- The handler returns HTTP `200`.
- Your logs show no `invalid webhook` or `reply failed` line.
- The sender receives a reply with the body "Thank you for your email." threaded under the original message.

A `400` response means signature verification failed: check that `PRIMITIVE_WEBHOOK_SECRET` matches the secret shown in your dashboard and that you passed the raw body bytes. A `500` means the reply call failed; inspect the logged error, which is a `*primitive.APIError` carrying `StatusCode`, `Code`, and `Message`.

## Send a new email instead of replying

If you're sending outbound mail rather than reacting to an inbound webhook, call `client.Send` directly.

```go
// send.go
package main

import (
	"context"
	"log"
	"os"
	"time"

	primitive "github.com/primitivedotdev/sdks/sdk-go"
)

func main() {
	client, err := primitive.NewClient(os.Getenv("PRIMITIVE_API_KEY"))
	if err != nil {
		log.Fatal(err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	result, err := client.Send(ctx, primitive.SendParams{
		From:     "Support <support@example.com>",
		To:       "alice@example.com",
		Subject:  "Hello",
		BodyText: "Hi there",
	})
	if err != nil {
		log.Fatal(err)
	}

	log.Println(result.ID, result.Status, result.Accepted)
}
```

A successful run logs the sent email's id, its status, and the accepted recipients (here, `alice@example.com`).

`Send` and `Reply` return as soon as Primitive accepts the message by default. Set `Wait` to a pointer to `true` (with `WaitTimeoutMs`, default 30000) when you need the first downstream SMTP outcome instead. See the [inbound and outbound email model](https://test.abhinandan.one/email-model.md) for the `DeliveryStatus` values wait mode reports.

> **Tip:** Building in Node.js or Python instead? See the [Node.js SDK Quickstart](https://test.abhinandan.one/node-sdk-quickstart.md) or the [Python SDK quickstart](https://test.abhinandan.one/python-sdk-quickstart.md). All three SDKs implement the identical inbound/outbound model, so pick by language.
