Go SDK

Send events from any Go server using a project API key. Pure stdlib, no external dependencies. Works with net/http, gin, echo, chi, and any other Go service.

clamp-sh/analytics-goMIT

Paste to your agent

Wire Clamp into a Go service
Adds the clamp Go module, initializes it on startup, and tracks the project's primary conversion event from the relevant handler.

Install

terminal
go get github.com/clamp-sh/analytics-go

Go 1.21+ supported. The package depends only on the standard library.

Use an API key that starts with sk_proj_ from Settings → API Keys. Keys are scoped to one project. Read it from the environment; never commit it.

Quick start

main.go
package main

import (
    "context"
    "log"
    "os"

    clamp "github.com/clamp-sh/analytics-go"
)

func main() {
    clamp.Init("proj_xxx", os.Getenv("CLAMP_API_KEY"))

    err := clamp.Track(context.Background(), "signup", clamp.Properties{
        "plan":   "pro",
        "method": "email",
    })
    if err != nil {
        log.Printf("clamp: %v", err)
    }
}

API

Init(projectID, apiKey string, opts ...InitOption)

Configures the SDK. Call once at process startup. State is held at the package level behind a mutex, so it's safe to use from concurrent goroutines.

Available options:

Track(ctx, name, properties, opts...) error

Sends a server event. Returns nil on success.

Optional via TrackOption:

Returns:

Money

purchase.go
clamp.Track(ctx, "purchase", clamp.Properties{
    "plan":  "pro",
    "total": clamp.Money{Amount: 29.00, Currency: "USD"},
    "tax":   clamp.Money{Amount: 4.35, Currency: "USD"},
})

Amount is in major units. Currency is an ISO 4217 code (uppercase, three letters).

Framework integrations

net/http

signup_handler.go
func signupHandler(w http.ResponseWriter, r *http.Request) {
    user, err := createUser(r)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    anonID, _ := r.Cookie("clamp_aid")
    _ = clamp.Track(r.Context(), "signup", clamp.Properties{
        "plan":   user.Plan,
        "method": "email",
    }, clamp.WithAnonymousID(anonID.Value))

    http.Redirect(w, r, "/welcome", http.StatusSeeOther)
}

gin

checkout.go
func checkoutHandler(c *gin.Context) {
    var payload CheckoutPayload
    if err := c.BindJSON(&payload); err != nil {
        c.AbortWithStatus(http.StatusBadRequest)
        return
    }

    session := createStripeSession(payload)
    _ = clamp.Track(c.Request.Context(), "checkout_started", clamp.Properties{
        "plan": payload.Plan,
    }, clamp.WithAnonymousID(payload.AnonID))

    c.JSON(http.StatusOK, gin.H{"url": session.URL})
}

echo

featureusedhandler.go
func featureUsed(c echo.Context) error {
    user := c.Get("user").(*User)
    _ = clamp.Track(c.Request().Context(), "feature_used", clamp.Properties{
        "feature": c.Param("feature"),
        "plan":    user.Plan,
    })
    return c.NoContent(http.StatusNoContent)
}

Capturing exceptions

Wrap unexpected errors and send them as $error events on the same pipeline as the rest of your tracking, queryable via the MCP errors.* tools.

webhook.go
if err := processWebhook(ctx, payload); err != nil {
    _ = clamp.CaptureError(ctx, err, clamp.WithErrorContext(clamp.Properties{
        "webhook": "stripe",
        "event":   payload.Type,
    }))
    return err
}

See Error capture for the wire shape, length caps, and the MCP read tools.

Errors and retries

The SDK is synchronous and returns errors on failure. There are no automatic retries. Either ignore the error (fire-and-forget), log it, or hand off to a queue:

background.go
go func() {
    if err := clamp.Track(ctx, "subscription_started", props); err != nil {
        log.Printf("clamp: %v", err)
    }
}()

Linking browser and server events

Pass the anonymous ID from the browser to your API, then use clamp.WithAnonymousID() on server-side calls. See the linking pattern on the server-side docs page; the same pattern applies in Go.