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

# Go SDK: quickstart and client

> Install the Square Cloud Go SDK (sdk-api-go v3), create a client with your API key and follow its conventions: context, ids, workspace apps, dates and timeouts.

<Info>
  This page documents **`github.com/squarecloudofc/sdk-api-go/v3`** (v3.0.0), a rewrite of the SDK. Coming from v2? Read the [v2 → v3 migration guide](/en/sdks/go/migrating_to_v3).
</Info>

## Requirements

* **Go 1.22** or newer.
* An API key (see [API key and scopes](#api-key-and-scopes)).

The module has **no dependencies** besides the Go standard library and is licensed under **MIT** (v2 was AGPL-3.0). Its package name is `squarecloud`.

## Installation

```bash theme={"system"}
go get github.com/squarecloudofc/sdk-api-go/v3
```

```go theme={"system"}
import "github.com/squarecloudofc/sdk-api-go/v3" // package squarecloud
```

## API key and scopes

Create a key at [squarecloud.app/account/security](https://squarecloud.app/en/account/security). The SDK sends it as-is in the `Authorization` header (no `Bearer` prefix).

A key can be limited to **scopes** (`apps:read`, `apps:deploy`, `apps:control`, `ai:chat`, ...) and to specific apps or databases:

* A call outside those limits returns an [`*APIError`](/en/sdks/go/errors) with 403 `MISSING_SCOPE` or `RESOURCE_NOT_ALLOWED`.
* List methods (`Account.Me`, `Apps.StatusAll`, ...) only return the resources the key can see.
* An unknown, revoked or expired key is 401 `ACCESS_DENIED`.

<Warning>
  Keep the key out of your source code and out of binaries you distribute. Read it from the environment or a secret store.
</Warning>

The examples read the key from the `SQUARECLOUD_API_KEY` environment variable. Set it in the terminal where you run them:

<Tabs>
  <Tab title="macOS / Linux">
    ```bash theme={"system"}
    export SQUARECLOUD_API_KEY="your-api-key"
    ```
  </Tab>

  <Tab title="Windows (PowerShell)">
    ```powershell theme={"system"}
    $env:SQUARECLOUD_API_KEY = "your-api-key"
    ```
  </Tab>
</Tabs>

## Creating the client

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

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/squarecloudofc/sdk-api-go/v3"
)

func main() {
	ctx := context.Background()
	c := squarecloud.New(os.Getenv("SQUARECLOUD_API_KEY"))

	me, err := c.Account.Me(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Hi %s, you have %d apps\n", me.User.Name, len(me.Applications))
}
```

Save it as `main.go` in a module (`go mod init example.com/hello`, then the `go get` above) and run `go run .`. It prints your account name and the number of apps the key can see:

```text theme={"system"}
Hi Jane, you have 3 apps
```

`squarecloud.New(apiKey, opts...)` returns a `*Client` and **never an error**. A `*Client` is safe for concurrent use: build it once and share it between goroutines.

Because `New` cannot fail, an empty or whitespace-only key is not rejected there. Instead, every call except `Service.Status` fails locally with `INVALID_API_KEY` (status `0`) before any request. This code exists only in the Go SDK.

### Options

Pass options to `New` after the key:

```go theme={"system"}
c := squarecloud.New(os.Getenv("SQUARECLOUD_API_KEY"),
	squarecloud.WithBaseURL(squarecloud.DefaultBaseURL), // "https://api.squarecloud.app/v2" (default)
	squarecloud.WithTimeout(30*time.Second),
	squarecloud.WithMaxRetries(2),
	squarecloud.WithUserAgent("my-bot/1.0"),
	squarecloud.WithHTTPClient(&http.Client{ // proxy, tracing, tests
		Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
	}),
)
```

| Option | Default | Description |
| - | - | - |
| `WithBaseURL(u)` | `https://api.squarecloud.app/v2` | API base URL. A trailing `/` is stripped. Also exported as `DefaultBaseURL`. |
| `WithTimeout(d)` | `30*time.Second` | Default deadline of a call whose `ctx` has none. It covers the whole call, retries included. See [Timeouts](#timeouts). |
| `WithMaxRetries(n)` | `2` | Retries of safe failures only (up to 3 attempts). A negative value counts as `0`. See [Retries](/en/sdks/go/errors#retries). |
| `WithUserAgent(ua)` | `squarecloud-sdk-go/<version>` | Replaces the whole `User-Agent` header. |
| `WithHTTPClient(hc)` | `&http.Client{}` | A custom `*http.Client` for proxies, tracing or tests. `nil` keeps the default. |

<Warning>
  Do not set `Timeout` on the `*http.Client` you pass to `WithHTTPClient`. That timeout also covers reading the body, so it would cut [realtime](/en/sdks/go/realtime) streams and snapshot downloads. Use contexts, `WithTimeout` and `http.Transport` timeouts instead.
</Warning>

The SDK never logs. To trace requests, wrap the `http.RoundTripper` of the client you pass to `WithHTTPClient`.

<Warning>
  The API key is stored in an unexported field of the client. `fmt` prints unexported fields, so don't print the client with `%v` or `%+v`.
</Warning>

## Running the examples

The snippets on the Go SDK pages are fragments. Each one runs on its own inside this program, which declares the `ctx`, `c` and `appID` they use:

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

import (
	"context"
	"os"

	"github.com/squarecloudofc/sdk-api-go/v3"
)

var (
	ctx   = context.Background()
	c     = squarecloud.New(os.Getenv("SQUARECLOUD_API_KEY"))
	appID = "abc123def456abc123def456" // one of your apps: see Account below
)

func main() {
	// Paste one snippet here, then run goimports.
}
```

Paste **one snippet at a time** into `main`, then run `goimports -w .` to add the imports it needs (`fmt`, `log`, `time`, ...). Install it with `go install golang.org/x/tools/cmd/goimports@latest`, or let your editor's Go extension (gopls) add imports on save. Pages that need other ids, such as a database or a workspace, start with their own version of this program.

## Modules

| Field | Covers | Page |
| - | - | - |
| `c.Account` | `Me`, `Snapshots(ctx, scope)` | [Account](#account) and [Snapshots](/en/sdks/go/snapshots) |
| `c.Service` | `Status` | [Platform status](#platform-status) |
| `c.Apps` | Get, delete, status, lifecycle, logs, metrics, domains, load balancers | [Managing applications](/en/sdks/go/managing_application) |
| `c.Apps.Create` / `Commit` | Uploads | [Commit and upload](/en/sdks/go/commit_and_upload) |
| `c.Apps.Files` | File manager | [Managing files](/en/sdks/go/managing_files) |
| `c.Apps.Envs` | Environment variables | [Environment variables](/en/sdks/go/environment_variables) |
| `c.Apps.Deploys` | Webhook and GitHub App deploys | [Deploys](/en/sdks/go/deploys) |
| `c.Apps.Network` | Analytics, logs, DNS, custom domain, cache | [Network](/en/sdks/go/network) |
| `c.Apps.Realtime` | Live logs and status | [Realtime](/en/sdks/go/realtime) |
| `c.Apps.Snapshots` / `c.Databases.Snapshots` / `c.DownloadSnapshot` | Backups | [Snapshots](/en/sdks/go/snapshots) |
| `c.Databases` | Managed databases | [Databases](/en/sdks/go/databases) |
| `c.Workspaces` | Workspaces, members and shared apps | [Workspaces](/en/sdks/go/workspaces) |
| `c.AI` | `Chat` | [AI](/en/sdks/go/ai) |

Besides `New` and the `With*` options, the package exports the `DefaultBaseURL` and `Version` constants, the [`APIError`](/en/sdks/go/errors) type with one `Code*` constant per error code, typed constants for enumerated inputs (`DatabaseRedis`, `GroupView`, `ResetPassword`, `SnapshotScopeDatabases`, ...) and one struct per API shape, which you can use in your own code:

```go theme={"system"}
func appNames(apps []squarecloud.AppSummary) []string {
	names := make([]string, 0, len(apps))
	for _, app := range apps {
		names = append(names, app.Name)
	}
	return names
}
```

## Conventions

### Context and ids first, typed data back

Every method takes a `context.Context` first and the resource id next, and returns **plain structs** (no methods, no cache). The `json` tags are the API's own field names (`created_at`, `version_id`, `lastModified`, `joinedAt`, `netIO`, ...), so the [API reference](/en/api-reference/authentication) applies as-is: `CreatedAt` is `created_at`, `VersionID` is `version_id`. Fields the API adds later are ignored, so they never break decoding.

* Mutations return only an `error`, unless the API returns data (`Envs.*`, `Deploys.SetWebhook`, `Deploys.LinkGithubApp`, `Databases.ResetCredentials` and the `Create` methods).
* A string result is `""` when the API sends none.
* Fields the API may send as `null` are **pointers**: check them for `nil` before use.
* Counters and byte sizes are `int64`.
* Lists come complete in one call: there is no pagination.

```go theme={"system"}
app, err := c.Apps.Get(ctx, appID)
if err != nil {
	log.Fatal(err)
}
if app.Domain != nil { // *string: nil for apps without a web domain
	fmt.Println("https://" + *app.Domain)
}
```

Resource groups are plain struct fields, so you can keep method values:

```go theme={"system"}
restart, logs := c.Apps.Restart, c.Apps.Logs

if err := restart(ctx, appID); err != nil {
	log.Fatal(err)
}
out, err := logs(ctx, appID)
if err != nil {
	log.Fatal(err)
}
fmt.Println(out)
```

### Workspace apps

Every `appID` also accepts the composite form `<appId>-<workspaceId>` to act on an app shared with you through a [workspace](/en/sdks/go/workspaces). `Workspaces.Get` and `Workspaces.List` return raw ids; you build the composite id yourself:

```go theme={"system"}
workspaceID := "0123456789abcdef0123456789abcdef"

if err := c.Apps.Restart(ctx, appID+"-"+workspaceID); err != nil {
	log.Fatal(err)
}
```

### Ids are encoded

Ids in the URL path are percent-encoded. An id that is empty, `.` or `..` would reach a different route, so it fails locally with `INVALID_ID` (status `0`) before anything is sent. Workspace routes send their ids in the body instead: there, `INVALID_ID` (400) comes from the server.

### Dates

`start` and `end` arguments (see [Network](/en/sdks/go/network)) are `time.Time` values, sent as RFC 3339 in UTC (whole seconds). In responses, the API's ISO 8601 strings decode to `time.Time`, and the fields the API sends as Unix milliseconds stay numbers (`Plan.Duration` and `Uptime` as `*int64`, `FileEntry.LastModified` as `*float64`): convert them with `time.UnixMilli`.

## Timeouts

| Call | Default deadline |
| - | - |
| Every other call | `WithTimeout` (30 s) |
| Calls the server holds open: `Start`/`Stop`/`Restart` of apps, `Start`/`Stop` of databases, `Databases.Create`, snapshot `Create`/`Restore`, `AI.Chat` | At least **2 minutes** (a larger `WithTimeout` wins) |
| Uploads (`Apps.Create`, `Apps.Commit`), `Files.Write` above 1 MiB, `DownloadSnapshot` | None: cancel `ctx` |
| `Apps.Realtime` | Only until the response headers arrive |

* The default deadline applies **only when `ctx` has no deadline**. A deadline on `ctx` always wins, shorter or longer than the default, floors included.
* One deadline covers the **whole call**: every attempt and the waits between [retries](/en/sdks/go/errors#retries).
* `WithTimeout(0)` (or any `d <= 0`) disables **every** default deadline, the 2-minute floors included.

Every method takes a `ctx`, so you can bound or cancel any call:

```go theme={"system"}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()

f, err := os.Open("build.zip")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

if err := c.Apps.Commit(ctx, appID, f, "", ""); err != nil {
	log.Fatal(err)
}
```

A call whose `ctx` expires returns an `*APIError` with `TIMEOUT`, and one whose `ctx` is canceled returns `NETWORK_ERROR`, both with status `0`. They unwrap to the context's error, so `errors.Is(err, context.DeadlineExceeded)` and `errors.Is(err, context.Canceled)` work. A [realtime](/en/sdks/go/realtime) loop returns the bare `ctx.Err()` instead.

## Account

`c.Account.Me(ctx)` returns the authenticated user plus the apps and databases the key can see.

```go theme={"system"}
me, err := c.Account.Me(ctx)
if err != nil {
	log.Fatal(err)
}

user := me.User
fmt.Println(user.ID, user.Name, user.Email, user.Plan.Name)
fmt.Println(user.Plan.Memory.Limit, user.Plan.Memory.Available, user.Plan.Memory.Used) // MB
if user.Plan.Duration != nil {
	fmt.Println("expires", time.UnixMilli(*user.Plan.Duration))
} else {
	fmt.Println("never expires")
}
```

| Field | Description |
| - | - |
| `User` (`user`) | `ID`, `Name`, `Email`, `Locale`, `CreatedAt` and `Plan` (`Name`, `Memory`, `Duration`: the Unix ms at which the plan **expires**, `nil` if it never does) |
| `Applications` (`applications`) | `[]AppSummary`: `ID`, `Name`, `Description` (`desc`), `RAM`, `Lang`, `Domain` (`*string`), `Custom` (`*string`), `Cluster`, `CreatedAt` |
| `Databases` (`databases`) | `[]DatabaseSummary`: `ID`, `Name`, `RAM`, `Type`, `Cluster`, `CreatedAt` |

`c.Account.Snapshots(ctx, scope)` lists every snapshot of the account: see [Snapshots](/en/sdks/go/snapshots#account-snapshots).

## Platform status

`c.Service.Status(ctx)` returns the public platform status. The route needs no key, and it is the only method that also works on a client built with an empty key.

```go theme={"system"}
status, err := c.Service.Status(ctx)
if err != nil {
	log.Fatal(err)
}

fmt.Println(status.Status) // "online" | "degraded" | "unknown"
fmt.Println(status.Message)

for name, service := range status.Services {
	fmt.Println(name, service.Status) // "operational" | "degraded" | "outage" | "maintenance" | "unknown"
}
```

`unknown` means the check itself could not run: it is not evidence of an outage.

## Next steps

<CardGroup cols={2}>
  <Card title="Managing applications" icon="server" href="/en/sdks/go/managing_application">
    Status, lifecycle, logs and metrics.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/en/sdks/go/errors">
    Error class, retries and rate limits.
  </Card>

  <Card title="API introduction" icon="code" href="/en/api-reference/introduction">
    Base URL, authentication and a first request.
  </Card>

  <Card title="CLI quickstart" icon="terminal" href="/en/cli-reference/quickstart">
    Deploy and manage apps from the terminal.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.