Skip to main content
v3 is a breaking release. It uses one package, a concrete *Client, ctx as the first argument everywhere and resource groups, and it fixes every known v2 bug. It covers all 67 operations of the current API.

At a glance

Construction and options

Per-request options:

Method by method

api is the v2 rest.Rest, c the v3 *squarecloud.Client.

Types

Errors

rest.APIError (StatusCode, Code, Message) becomes squarecloud.APIError (Status, Code, Message, Method, Path): rename StatusCode to Status. rest.ErrorCode(err) and rest.IsRateLimit(err) were removed: use errors.As and check Code or Status == 429.
  • Network failures are now *APIError with Status 0, Code NETWORK_ERROR or TIMEOUT and the cause’s text as Message, and they unwrap to the cause (errors.Is(err, context.Canceled) works).
  • So are local checks (Status 0: INVALID_ID, FILE_TOO_LARGE, INVALID_API_KEY) and a 2xx body that is not JSON (UNKNOWN_ERROR, Invalid JSON in HTTP <status> response).
  • A 2xx body that says "status": "error" is an error now (v2 reported it as success). The cluster refusals of app and database start/stop arrive as 409 CONTAINER_ALREADY_STARTED, CONTAINER_ALREADY_STOPPED, CONTAINER_TEMPORARILY_SUSPENDED, CONTAINER_NOT_FOUND, CONTAINER_INSUFFICIENT_DISK_SPACE, CONTAINER_NETWORK_CONFLICT or ACTION_FAILED, with no message. The SDK returns an “already” answer as an error: treat it as success yourself if you need to.
  • A response without a code has Code UNKNOWN_ERROR.
  • An expired API key is 401 ACCESS_DENIED, like an unknown one.
  • There is a Code* constant for every code the API documents. The API now sends 429 RATE_LIMITED (CodeRateLimited) where it sent RATE_LIMIT and RATE_LIMIT_EXCEEDED; CodeRateLimit and CodeRateLimitExceeded remain, deprecated.
  • Every AI.Chat error is OpenAI-shaped with a lowercase code (access_denied, rate_limit_exceeded, server_overloaded, …), which Code carries verbatim.
  • Error() renders squarecloud: <METHOD> <path>: HTTP <status> <CODE>: <message> (v2: squarecloud: <message> (<CODE>, HTTP <status>)). Match on fields, not on the text.
See Errors for the full reference.

Behavior changes

  • Empty API key: New("") (or a whitespace-only key) still returns a client (it cannot return an error), but every call except Service.Status fails locally with INVALID_API_KEY.
  • Snapshot 202: v2 returned an *APIError with StatusCode 202. v3 returns a SnapshotCreated with Pending: true and a nil error.
  • Realtime: Next now returns a RealtimeEvent. Switch on ev.Event (system, status, logs, error, message). For logs, print ev.Line (the \u0001/\u0002 byte is stripped; ev.Data stays the raw frame) and use ev.Stream for stdout/stderr. For status, use ev.Status: never nil on a status event, shallow-merged across frames and reconnections. After REALTIME_DISCONNECTED, Next returns io.EOF. The stream no longer dies after 30 s, reconnections wait at least 5.5 s after the previous open, and the open is bounded by the client timeout until the headers arrive. See Realtime.
  • Timeouts: v2 used a fixed 30 s http.Client timeout for everything. v3 applies a default deadline only when ctx has none: the client timeout (WithTimeout, 30 s) for most calls; at least 2 minutes for start/stop/restart, database create, snapshot create/restore and AI.Chat; none for uploads, file writes over 1 MiB of content and snapshot downloads. WithTimeout(0) disables all of them.
  • Empty network windows: Analytics, Errors and Performance return nil pointers when the window has no traffic.
  • Headers: every API request sends Accept: application/json (text/event-stream for realtime). The default User-Agent changed from Square GO to squarecloud-sdk-go/3.0.0 (WithUserAgent still overrides it).
  • Ids: every id is now percent-encoded as one path segment (v2 pasted it into the path as is), and an empty, . or .. id fails locally with INVALID_ID.
  • File writes: v2 always sent the content as a string, which corrupted binary files, and could not write an empty file. v3 always sends the content base64-encoded, so every byte round-trips, empty content writes an empty file, and content over 10 MB fails locally with FILE_TOO_LARGE. The API answers 400 INVALID_CONTENT for content it cannot decode.
  • File reads: v3 always requests base64 and decodes it, instead of the JSON byte array v2 read (which the API has deprecated). A file over 10 MB is 413 FILE_TOO_LARGE.
  • File listing: listing a directory that does not exist is 404 FILE_NOT_FOUND; it used to be an empty list.
  • Snapshots: list entries carry VersionID and URL from the API; nothing is parsed out of Key.
  • Retries: new. GET network errors, 503 UPLOAD_BUSY/ANALYTICS_BUSY and 503 DATABASE_UNAVAILABLE on GET are retried twice by default; WithMaxRetries(0) restores the v2 behavior. DATABASE_UNAVAILABLE can arrive after a mutation has started, so the SDK never retries it on other methods; retry an idempotent mutation yourself if you want to. See Retries.
  • Go version: the minimum dropped from Go 1.24 to Go 1.22.