{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/go-sdk-quickstart-81405d38","markdown_url":"https://test.abhinandan.one/go-sdk-quickstart-81405d38.md","article":{"id":"9c67d00c-c10a-4ee1-bede-5f277f95f7ca","article_slug":"go-sdk-quickstart-81405d38","parent_article_slug":null,"parent_article_title":null,"kind":"quickstart","published_at":"2026-08-11T18:54:49.896145+00:00","keywords":["go get github.com/primitivedotdev/sdks/sdk-go","primitive.Receive","primitive.NewClient","client.Reply","PRIMITIVE_WEBHOOK_SECRET","Go SDK quickstart"],"meta_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.","og_image_url":null,"source_file_paths":["sdk-go/README.md"],"recording_id":null,"replayable":false,"task_name":"Go SDK Quickstart","category":"Go SDK","summary":null,"description":"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.","content_kind":"repo_page","content_markdown":"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.\n\n<Note>\n\nRequires 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.\n\n</Note>\n\n<Steps>\n\n<Step title=\"Install the SDK\">\n\n```bash\ngo get github.com/primitivedotdev/sdks/sdk-go@latest\n```\n\nThis 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.\n\n</Step>\n\n<Step title=\"Set your API key and webhook secret\">\n\nExport both as environment variables so they never land in source control:\n\n```bash\nexport PRIMITIVE_API_KEY=prim_test\nexport PRIMITIVE_WEBHOOK_SECRET=whsec_...\n```\n\n`PRIMITIVE_API_KEY` authenticates outbound calls (`client.Send`, `client.Reply`). `PRIMITIVE_WEBHOOK_SECRET` verifies that inbound webhook deliveries actually came from Primitive.\n\n</Step>\n\n<Step title=\"Verify a webhook and reply to it\">\n\n`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.\n\n```go\n// main.go\npackage main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n)\n\nvar client *primitive.Client\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\t// Verification runs over the exact request bytes, so read the raw body\n\t// before any JSON decoding.\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\thttp.Error(w, \"could not read body\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\theaders := map[string]string{\n\t\t\"Primitive-Signature\": r.Header.Get(\"Primitive-Signature\"),\n\t\t\"X-Webhook-Event\":     r.Header.Get(\"X-Webhook-Event\"),\n\t}\n\n\temail, err := primitive.Receive(primitive.HandleWebhookOptions{\n\t\tBody:    body,\n\t\tHeaders: headers,\n\t\tSecret:  os.Getenv(\"PRIMITIVE_WEBHOOK_SECRET\"),\n\t})\n\tif err != nil {\n\t\tlog.Printf(\"invalid webhook: %v\", err)\n\t\thttp.Error(w, \"invalid webhook\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)\n\tdefer cancel()\n\n\tif _, err := client.Reply(ctx, email, primitive.ReplyParams{\n\t\tBodyText: \"Thank you for your email.\",\n\t}); err != nil {\n\t\tlog.Printf(\"reply failed: %v\", err)\n\t\thttp.Error(w, \"reply failed\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusOK)\n}\n\nfunc main() {\n\tvar err error\n\tclient, err = primitive.NewClient(os.Getenv(\"PRIMITIVE_API_KEY\"))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\thttp.HandleFunc(\"/webhooks/email\", handler)\n\tlog.Println(\"listening on :8080\")\n\tlog.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n```\n\n</Step>\n\n<Step title=\"Verify it worked\">\n\nSend a test webhook delivery to `/webhooks/email` from your Primitive dashboard. On success:\n\n- The handler returns HTTP `200`.\n- Your logs show no `invalid webhook` or `reply failed` line.\n- The sender receives a reply with the body \"Thank you for your email.\" threaded under the original message.\n\nA `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`.\n\n</Step>\n\n</Steps>\n\n## Send a new email instead of replying\n\nIf you're sending outbound mail rather than reacting to an inbound webhook, call `client.Send` directly.\n\n```go\n// send.go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\tprimitive \"github.com/primitivedotdev/sdks/sdk-go\"\n)\n\nfunc main() {\n\tclient, err := primitive.NewClient(os.Getenv(\"PRIMITIVE_API_KEY\"))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\n\tdefer cancel()\n\n\tresult, err := client.Send(ctx, primitive.SendParams{\n\t\tFrom:     \"Support <support@example.com>\",\n\t\tTo:       \"alice@example.com\",\n\t\tSubject:  \"Hello\",\n\t\tBodyText: \"Hi there\",\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlog.Println(result.ID, result.Status, result.Accepted)\n}\n```\n\nA successful run logs the sent email's id, its status, and the accepted recipients (here, `alice@example.com`).\n\n`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](email-model) for the `DeliveryStatus` values wait mode reports.\n\n<Tip>\n\nBuilding in Node.js or Python instead? See the [Node.js SDK Quickstart](node-sdk-quickstart) or the [Python SDK quickstart](python-sdk-quickstart). All three SDKs implement the identical inbound/outbound model, so pick by language.\n\n</Tip>\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Client and Configuration\" href=\"go-client-configuration\">\n\nUnderstand the dual-host client NewClient constructs and when to override base URLs.\n\n</Card>\n\n<Card title=\"Receiving and Verifying Webhooks\" href=\"go-receiving-webhooks\">\n\nGo deeper on primitive.Receive and ReceiveFromHTTPRequest for real HTTP frameworks.\n\n</Card>\n\n<Card title=\"Replying to Emails\" href=\"go-replying-to-emails\">\n\nControl From overrides, attachments, and threading behavior on Client.Reply.\n\n</Card>\n\n<Card title=\"Inbound and Outbound Email Model\" href=\"email-model\">\n\nLearn the normalized email object and wait-mode delivery statuses shared across every SDK.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-go/README.md","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Go+SDK+Quickstart&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fgo-sdk-quickstart-81405d38","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}