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

# Go SDK quickstart

> Install the ZeroClick Go seller SDK and guard a net/http route with one middleware: verify the signature, check the allowance, and settle usage.

The Go seller SDK ([`cdn.zeroclick.io/sdks/sellers-go`](https://pkg.go.dev/cdn.zeroclick.io/sdks/sellers-go)) implements the ZeroClick billing guard for Go backends. It verifies that a request really came from ZeroClick and checks that the buyer can pay before your handler runs. It returns the refusals ZeroClick expects and reports what was used. The usual integration is one middleware on each billable route.

This page takes a `net/http` service from `go get` to guarded. The [quickstart](/quickstart) covers the full setup, including the dashboard side, and [integrate your API](/integrate/overview) describes the contract the SDK implements.

## Install

```sh theme={null}
go get cdn.zeroclick.io/sdks/sellers-go
```

Requires Go 1.24 or newer. The core package imports only the standard library. The SDK's one third-party dependency (`go-jose`) belongs to the optional `jwe` subpackage for encrypted request bodies. Unless you import that package, Go's module graph pruning keeps it out of your `go.sum` and your binary.

## What you need first

Two credentials from your [dashboard](https://dashboard.zeroclick.io):

* A **signing secret**: a secret value (`zcsec_…`) and its key id (`hsec_…`), called the `kid`. The SDK uses it to verify the `zc-signature` header on every forwarded request.
* An **API key** (`zc_…`) with the `usage:read` and `usage:write` scopes. The read scope covers allowance checks; the write scope covers usage reporting.

`APIKey` carries both scopes. To follow least privilege, pass two scoped keys instead and omit `APIKey`: `UsageReadKey` for allowance checks and `UsageWriteKey` for usage reporting. The split is handy when a separate worker reports usage. A scoped key falls back to `APIKey` when it is not set, so either form works. See [keys and secrets](/integrate/keys-and-secrets) for scopes and rotation.

The dashboard shows each secret once, when you create it. `SecretsFromEnv()` reads signing secrets from one environment variable of `<kid>:<secret>` pairs, comma-separated:

```sh theme={null}
export ZEROCLICK_SIGNING_SECRETS="hsec_k5nq0v7m3d8p:zcsec_…"
export ZEROCLICK_API_KEY="zc_…"
```

## The whole integration

This example guards `/v1/product-watch` for a service `product-watch` with a meter `requests`, and leaves `/health` open. Substitute your own slugs.

```go main.go theme={null}
package main

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

	sellers "cdn.zeroclick.io/sdks/sellers-go"
)

func main() {
	secrets, err := sellers.SecretsFromEnv() // ZEROCLICK_SIGNING_SECRETS
	if err != nil {
		log.Fatal(err)
	}

	seller, err := sellers.New(sellers.Config{
		APIKey:         os.Getenv("ZEROCLICK_API_KEY"),
		ServiceSlug:    "product-watch",
		SigningSecrets: secrets,
		Logger:         log.Default(),
	})
	if err != nil {
		log.Fatal(err)
	}

	mux := http.NewServeMux()

	// Unguarded: the platform's health probe is not a buyer.
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`{"status":"ok"}`))
	})

	// Billable. One fixed charge per request.
	mux.Handle("/v1/product-watch", seller.Meter(sellers.PerRequest("requests", 1))(
		http.HandlerFunc(productWatch)))

	log.Fatal(http.ListenAndServe(":8080", mux))
}

func productWatch(w http.ResponseWriter, r *http.Request) {
	zc, _ := sellers.FromContext(r.Context()) // zc.AgentID, zc.RequestID
	body, _ := io.ReadAll(r.Body)             // still readable

	log.Printf("watching %d bytes for %s", len(body), zc.AgentID)

	w.Header().Set("content-type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"productCount": 3})
}
```

On each request, `Meter` verifies the `zc-signature` header over the raw bytes, checks that the buyer's allowance covers one `requests` unit, and only then calls your handler. When a request fails either step, the middleware refuses it before your code runs, with the exact response ZeroClick expects: a `401` for a bad signature, a priced `402` for a business denial. When the handler responds 2xx, the middleware attaches the `zc-usage` header so the request settles. A 4xx or 5xx bills nothing.

Inside the handler, `r.Body` reads normally: verification consumed it, and the middleware put it back. `sellers.FromContext` returns the verified buyer: `AgentID` (`agt_…`) and `RequestID` (`zcreq_…`), the id that correlates this request across ZeroClick's logs and your own.

Test the guard locally: it must refuse a plain request with no ZeroClick headers.

```sh theme={null}
curl -i localhost:8080/v1/product-watch -d '{}'
```

```http theme={null}
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{"error":"invalid_zeroclick_signature"}
```

Only signed requests from ZeroClick reach your handler. To see paid traffic end to end (the `402` challenge, the payment, the forwarded request), deploy the route where ZeroClick can reach it and follow the verification step in the [quickstart](/quickstart).

## Framework fit

`Meter` (and its free counterpart `Identify`) returns a plain `func(http.Handler) http.Handler`, the standard middleware shape, so it composes with any router built on `net/http`:

| Framework   | Wire-up                                                              |
| ----------- | -------------------------------------------------------------------- |
| `net/http`  | `mux.Handle("/v1/product-watch", seller.Meter(…)(handler))`          |
| chi         | `r.Use(seller.Meter(…))`                                             |
| gorilla/mux | `r.Use(seller.Meter(…))`                                             |
| Echo        | `echo.WrapMiddleware(seller.Meter(…))`                               |
| Gin         | Wrap the guarded handler with `gin.WrapH` inside a `gin.HandlerFunc` |
| Fiber       | Not supported. Fiber runs on fasthttp, which has no `http.Handler`   |

Apply the middleware per billable route, not globally: health probes and other free endpoints should stay unguarded, and each metered route declares its own charge.

For a backend not built on `net/http` at all, every decision also flows through the SDK's framework-neutral `Request` and `Response` types. See the [API reference](/sdks/go/api).

## Next steps

<Columns cols={2}>
  <Card title="Middleware" icon="layers" href="/sdks/go/middleware">
    What Meter and Identify do on every request, streaming support, and variable usage.
  </Card>

  <Card title="Configuration" icon="settings" href="/sdks/go/configuration">
    Every Config field: keys, signing secrets, body limits, and the outage policy.
  </Card>

  <Card title="API reference" icon="code" href="/sdks/go/api">
    The full public surface, including Guard and the framework-neutral types.
  </Card>

  <Card title="Errors" icon="triangle-alert" href="/sdks/go/errors">
    Decisions versus errors, error codes, and outage classification.
  </Card>
</Columns>
