Canonical page: https://docs.singlebase.io/sdk-go

# Go SDK

A small client you can copy into your backend. This is a documented implementation, not a separately published package. It sends the same API envelope for every service through one `call` method.

Go 1.18+; standard library only. Put the client in a `singlebase` subdirectory of your Go module. Replace `example.com/app` in the example with your module path.

## Response contract

| Field | Meaning |
|---|---|
| `data` | Operation result; absent on API errors |
| `error` | API error object, or null/nil on success; `message` is the stable code |
| `meta` | Response metadata, including pagination when returned |
| `exec_time` | Gateway execution time in seconds, when returned |

API errors return in `Result.error`. Network, invalid JSON, and transport failures raise an exception or return a Go error. These small clients do not manage sessions, auto-refresh tokens, or retry requests. Pass a current user bearer token to the constructor/client only when acting as that user; omit it for trusted server/agent calls. Keep keys on the server.

## Client implementation

Save as `singlebase.go`. [Download the source](/examples/singlebase.go).

```go
package singlebase

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

type Result struct {
	Data     json.RawMessage `json:"data"`
	Error    map[string]any  `json:"error"`
	Meta     map[string]any  `json:"meta"`
	ExecTime *float64        `json:"exec_time"`
}

type Client struct {
	APIURL string
	APIKey string
	Bearer string
}

func (s Client) Call(ctx context.Context, operation string, payload map[string]any, options map[string]any) (Result, error) {
	body := map[string]any{"operation": operation, "payload": payload}
	if options != nil {
		body["options"] = options
	}
	raw, err := json.Marshal(body)
	if err != nil {
		return Result{}, err
	}
	req, err := http.NewRequestWithContext(ctx, "POST", s.APIURL, bytes.NewReader(raw))
	if err != nil {
		return Result{}, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-API-KEY", s.APIKey)
	if s.Bearer != "" {
		req.Header.Set("Authorization", "Bearer "+s.Bearer)
	}
	httpClient := &http.Client{Timeout: 30 * time.Second}
	res, err := httpClient.Do(req)
	if err != nil {
		return Result{}, err
	}
	defer res.Body.Close()
	var result Result
	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
		return Result{}, err
	}
	if res.StatusCode >= 400 && result.Error == nil {
		return result, fmt.Errorf("HTTP %d without an API error envelope", res.StatusCode)
	}
	return result, nil
}
```

## Example

Set `SINGLEBASE_API_URL` to the complete console endpoint and `SINGLEBASE_API_KEY` to your server or appropriately scoped agent key. The `notes` collection must exist.

```go
package main

import (
    "context"
    "fmt"
    "os"
    "example.com/app/singlebase"
)

func main() {
    sbc := singlebase.Client{APIURL: os.Getenv("SINGLEBASE_API_URL"), APIKey: os.Getenv("SINGLEBASE_API_KEY")}
    resp, err := sbc.Call(context.Background(), "data.query", map[string]any{"collection": "notes", "limit": 5}, map[string]any{})
    if err != nil { panic(err) }
    if resp.Error != nil { panic(resp.Error["message"]) }
    fmt.Println(string(resp.Data), resp.Meta, resp.ExecTime)
}
```

Use the same method with `llm.summarize` for generation or `chat.ask` for an answer, with the operation's verified payload. The `options` argument goes in the request envelope, separate from `payload`. An empty options object is valid; omit it when unused. See [API overview](/api.md) for request access and errors, and [API reference](/api-reference.md) for the operation groups.
