*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
*APIErrorwithStatus0,CodeNETWORK_ERRORorTIMEOUTand the cause’s text asMessage, and they unwrap to the cause (errors.Is(err, context.Canceled)works). - So are local checks (
Status0: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 409CONTAINER_ALREADY_STARTED,CONTAINER_ALREADY_STOPPED,CONTAINER_TEMPORARILY_SUSPENDED,CONTAINER_NOT_FOUND,CONTAINER_INSUFFICIENT_DISK_SPACE,CONTAINER_NETWORK_CONFLICTorACTION_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
CodeUNKNOWN_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 429RATE_LIMITED(CodeRateLimited) where it sentRATE_LIMITandRATE_LIMIT_EXCEEDED;CodeRateLimitandCodeRateLimitExceededremain, deprecated. - Every
AI.Chaterror is OpenAI-shaped with a lowercase code (access_denied,rate_limit_exceeded,server_overloaded, …), whichCodecarries verbatim. Error()renderssquarecloud: <METHOD> <path>: HTTP <status> <CODE>: <message>(v2:squarecloud: <message> (<CODE>, HTTP <status>)). Match on fields, not on the text.
Behavior changes
- Empty API key:
New("")(or a whitespace-only key) still returns a client (it cannot return an error), but every call exceptService.Statusfails locally withINVALID_API_KEY. - Snapshot 202: v2 returned an
*APIErrorwithStatusCode202. v3 returns aSnapshotCreatedwithPending: trueand anilerror. - Realtime:
Nextnow returns aRealtimeEvent. Switch onev.Event(system,status,logs,error,message). For logs, printev.Line(the\u0001/\u0002byte is stripped;ev.Datastays the raw frame) and useev.Streamfor stdout/stderr. For status, useev.Status: nevernilon a status event, shallow-merged across frames and reconnections. AfterREALTIME_DISCONNECTED,Nextreturnsio.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.Clienttimeout for everything. v3 applies a default deadline only whenctxhas none: the client timeout (WithTimeout, 30 s) for most calls; at least 2 minutes for start/stop/restart, database create, snapshot create/restore andAI.Chat; none for uploads, file writes over 1 MiB of content and snapshot downloads.WithTimeout(0)disables all of them. - Empty network windows:
Analytics,ErrorsandPerformancereturnnilpointers when the window has no traffic. - Headers: every API request sends
Accept: application/json(text/event-streamfor realtime). The defaultUser-Agentchanged fromSquare GOtosquarecloud-sdk-go/3.0.0(WithUserAgentstill 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 withINVALID_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 400INVALID_CONTENTfor 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
VersionIDandURLfrom the API; nothing is parsed out ofKey. - Retries: new. GET network errors, 503
UPLOAD_BUSY/ANALYTICS_BUSYand 503DATABASE_UNAVAILABLEon GET are retried twice by default;WithMaxRetries(0)restores the v2 behavior.DATABASE_UNAVAILABLEcan 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.

