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

# Golang SDK

<Card title="GitHub">
  [https://github.com/bronlabs/bron-sdk-go](https://github.com/bronlabs/bron-sdk-go)
</Card>

## Install

```bash theme={"system"}
go get github.com/bronlabs/bron-sdk-go
```

## Authenticate

<Note>
  Need a JWK keypair? Install the [Bron CLI](/sdk/cli) and run `bron config init --name default --workspace <workspaceId> --key-file ~/.config/bron/keys/me.jwk --generate-key` — it generates the JWK, stores it at `0600`, prints the public half to paste into the Bron UI, and registers the profile. Same key file works directly with the SDK via `BRON_API_KEY_FILE`.
</Note>

```sh theme={"system"}
export BRON_API_KEY='{"kty":"EC","x":"VqW0Rzw4At***ADF2iFCzxc","y":"9AylQ7HHI0vRT0C***PqWuf2yT8","crv":"P-256","d":"DCQ0jrmYw8***9i64igNKuP0","kid":"cmdos3lj50000sayo6pl45zly"}'
export BRON_WORKSPACE_ID='htotobpkg7xqjfxenjid3n1o'
```

The SDK generates an ES256 JWT per request from your private JWK and sends it in `Authorization: ApiKey <jwt>`. No token caching, no revocation flow.

## Quickstart

```go theme={"system"}
package main

import (
	"log"
	"os"

	"github.com/bronlabs/bron-sdk-go/sdk"
	"github.com/bronlabs/bron-sdk-go/sdk/types"
	"github.com/google/uuid"
	"github.com/joho/godotenv"
)

func main() {
	godotenv.Load()

	client := sdk.NewBronClient(bron.BronClientConfig{
		APIKey:      os.Getenv("BRON_API_KEY"),
		WorkspaceID: os.Getenv("BRON_WORKSPACE_ID"),
	})

	// Get workspace
	workspace, err := client.Workspaces.GetWorkspaceById()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Workspace: %s", workspace.Name)

	// Get accounts
	accounts, err := client.Accounts.GetAccounts(nil)
	if err != nil {
		log.Fatal(err)
	}

	// Get balances for first account
	if len(accounts.Accounts) > 0 {
        account := accounts.Accounts[0]
        accountIds := []string{account.AccountId}

        balances, err := client.Balances.GetBalances(&types.BalancesQuery{
            AccountIds: &accountIds,
        })

        if err != nil {
            log.Fatal(err)
        }

        for _, balance := range balances.Balances {
            log.Printf("Balance %s (%s): %s", balance.AssetId, balance.Symbol, balance.TotalBalance)
        }

        // Create a USDC withdrawal on Ethereum to a saved address-book record.
        tx, err := client.Transactions.CreateTransaction(types.CreateTransaction{
            AccountId:       account.AccountId,
            ExternalId:      uuid.New().String(),
            TransactionType: "withdrawal",
            Params: map[string]interface{}{
                "amount":                  "100",
                "assetId":                 "5000",
                "networkId":               "ETH",
                "toAddressBookRecordId":   "<recordId>",
            },
        })

        if err != nil {
            log.Fatal(err)
        }

        log.Printf("Created transaction '%s' (%s): sent %s", tx.TransactionId, tx.Status, tx.Params["amount"])
	}
}
```

## Subscriptions (WebSocket)

A subscription is "GET extended" — the same query you would send to the matching list endpoint, but the connection stays open. The server replays the historical match as the first frame, then keeps pushing live updates as additional frames of the same response shape (a list with one element per change in steady state).

```go theme={"system"}
package main

import (
	"context"
	"log"
	"os"

	"github.com/bronlabs/bron-sdk-go/sdk"
	"github.com/bronlabs/bron-sdk-go/sdk/types"
)

func main() {
	client := sdk.NewBronClient(sdk.BronClientConfig{
		APIKey:      os.Getenv("BRON_API_KEY"),
		WorkspaceID: os.Getenv("BRON_WORKSPACE_ID"),
	})

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	stream, err := client.Transactions.Subscribe(ctx, &types.TransactionsQuery{
		TransactionStatuses: &[]types.TransactionStatus{
			types.TransactionStatus_SIGNING_REQUIRED,
			types.TransactionStatus_WAITING_APPROVAL,
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer stream.Close()

	// First batch is the historical match; subsequent batches are live updates.
	for batch := range stream.Updates() {
		for _, tx := range batch.Transactions {
			log.Printf("tx %s → %s", tx.TransactionId, tx.Status)
		}
	}

	if err := stream.Err(); err != nil {
		log.Printf("stream ended: %v", err)
	}
}
```

* **First frame** = historical match, exactly as `GetTransactions(query)` would return.
* **Subsequent frames** = live updates, each typically a single-element list. Filters apply to both phases.
* **Skip the initial replay** — pass `Limit: ptr("0")` (or `SubscribeWithFilter(ctx, map[string]interface{}{"limit": 0, ...})` when the typed `*string` field doesn't carry through; the backend wants an integer for `limit`).
* **Always `defer stream.Close()`** — sends `UNSUBSCRIBE` and tears down the WebSocket cleanly. The channel closes when the context is cancelled, `Close` is called, or the connection drops; `stream.Err()` returns the cause.
* **Proxy** is honored automatically via `BronClientConfig.Proxy` (or `HTTP_PROXY` / `HTTPS_PROXY` env vars).

Today only `client.Transactions.Subscribe` is exposed. Subscriptions for balances, approvals, and intents follow the same pattern and will land in subsequent SDK releases.

## Errors

Methods return Go errors mirroring the API envelope. The error implements an interface with `Code()`, `Trace()`, `Details()` so callers can branch without parsing the message:

```go theme={"system"}
if _, err := client.Transactions.CreateTransaction(body); err != nil {
    var apiErr *sdk.APIError
    if errors.As(err, &apiErr) {
        log.Printf("api error code=%s trace=%s", apiErr.Code(), apiErr.Trace())
    }
    return err
}
```

Branch on `Code()` (stable identifier) — never on the human message. Quote `Trace()` in any user-facing report.

## Configuration

`BronClientConfig` accepts:

* `APIKey` — private JWK as a JSON string (required).
* `WorkspaceID` — workspace ID (required).
* `BaseURL` — API base URL (defaults to `https://api.bron.org`).
* `Proxy` — `http://[user:pass@]host:port` for outbound requests through a corporate proxy. Standard `HTTPS_PROXY` / `HTTP_PROXY` env vars are honored too.

## Where to next

<CardGroup cols={2}>
  <Card title="API reference" icon="book-open" href="/api-reference/getting-started">
    Endpoints, request/response shapes, OpenAPI spec.
  </Card>

  <Card title="CLI overview" icon="terminal" href="/sdk/cli">
    `bron` binary — same auth, same data path, no client code.
  </Card>

  <Card title="Live updates (`tx subscribe`)" icon="satellite-dish" href="/sdk/cli/subscribe">
    Same WebSocket transport, JSONL on stdout — useful for shell pipelines.
  </Card>

  <Card title="Authentication & profiles" icon="key" href="/sdk/cli/auth">
    Keypair lifecycle, env-var overrides, proxy setup, rotation.
  </Card>
</CardGroup>
