> ## 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.

# Migrazione a v5

> Cosa è cambiato tra squarecloud-api v4 e v5: un client sincrono con una facciata await, dict semplici invece di dataclass, metodi raggruppati per risorsa, un'unica classe di eccezione. Una tabella metodo per metodo.

La v5 è una riscrittura. L'SDK ora è sincrono per impostazione predefinita (con una facciata `await`), non ha dipendenze, raggruppa i metodi per risorsa, restituisce dict semplici (`TypedDict`) e solleva un unico tipo di eccezione. Copre tutte le 67 operazioni dell'API di Square Cloud.

## In sintesi

| v4                                                                                                     | v5                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `await client.x(...)`, solo asincrono                                                                  | `client.group.x(...)`, oppure `await async_client.group.x(...)`                                                                                            |
| Dataclass frozen (`status.ram`)                                                                        | `TypedDict`, un dict semplice (`status['ram']`); i campi sconosciuti vengono mantenuti invece di sollevare `TypeError`                                     |
| Oggetti `Application` (`app.start()`, `app.cache`)                                                     | Dati semplici più id: `client.apps.start(app_id)`. Non c'è cache                                                                                           |
| \~25 classi di eccezione (`NotFoundError`, `TooManyRequests`, `FewMemory`, ...)                        | `SquareCloudAPIError` con `.status`, `.code`, `.message`, `.method`, `.path` (`'/v2/...'`), `.cause`                                                       |
| `squarecloud.File(path)`                                                                               | Passa direttamente un percorso, `bytes` o un file object binario                                                                                           |
| Argomenti facoltativi per posizione                                                                    | I modificatori facoltativi sono solo keyword (`status(id, raw=True)`, `commit(id, file, path=...)`, `databases.create(name, type=, version=, memory=)`)    |
| Listener delle richieste (`@client.on_request`), listener di cattura, `avoid_listener`, `update_cache` | Rimossi. Usa il [logger](/it/sdks/py/client#logging) `squarecloud` (DEBUG) o un [`transport=`](/it/sdks/py/client#transport-personalizzato) personalizzato |
| Dipendenze `aiohttp` e `typing-extensions`                                                             | Nessuna                                                                                                                                                    |
| Python `>=3.13,<3.15`                                                                                  | Python `>=3.11`, senza limite superiore                                                                                                                    |

## Costruzione e opzioni

| v4                                                                                  | v5                                                                                                                                                                 |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `squarecloud.Client(api_key, log_level=...)`                                        | `SquareCloud(api_key, *, base_url=BASE_URL, timeout=30.0, max_retries=2, transport=None, user_agent=...)`, oppure `AsyncSquareCloud(...)` con gli stessi argomenti |
| `log_level=` e un handler colorato collegato all'import                             | `logging.getLogger('squarecloud')` con un `NullHandler`; configuralo tu                                                                                            |
| Valori predefiniti di `aiohttp` (5 min per richiesta; 90 s tra le letture realtime) | `timeout` in secondi per operazione sul socket, con una soglia minima di 120 s per le chiamate tenute aperte e quelle AI; `timeout <= 0` disattiva tutti i timeout |
| Nessun retry                                                                        | `max_retries` (2) solo per i fallimenti sicuri; il 429 non viene mai ripetuto                                                                                      |
| `User-Agent` fisso nel codice                                                       | `user_agent=` sostituisce l'header (predefinito `squarecloud-sdk-py/<version>`)                                                                                    |
| Una nuova sessione per richiesta                                                    | `close()` o `with` / `async with` chiude le connessioni keep-alive del pool                                                                                        |

## Metodo per metodo

| vecchio (v4 `Client`)                                                                             | nuovo (v5 `SquareCloud`)                                                             | note                                                                                                                                                                                                                                                                                                             |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user()`                                                                                          | `account.me()['user']`                                                               | `me()` restituisce anche `applications` e `databases`                                                                                                                                                                                                                                                            |
| `all_apps()`                                                                                      | `account.me()['applications']`                                                       |                                                                                                                                                                                                                                                                                                                  |
| `app(app_id)`                                                                                     | `apps.get(app_id)`                                                                   | Ora `GET /apps/{id}`: funziona con gli id dei workspace `'<appId>-<workspaceId>'`                                                                                                                                                                                                                                |
| `user_snapshots(scope)`                                                                           | `account.snapshots(*, scope=None)`                                                   | `scope` è solo keyword. Gli elementi contengono il `version_id` e l'`url` firmato dell'API                                                                                                                                                                                                                       |
| `service_status()`                                                                                | `service.status()`                                                                   |                                                                                                                                                                                                                                                                                                                  |
| `upload_app(File(path))`                                                                          | `apps.create(path)`                                                                  | Streaming dal disco; restituisce `AppCreated` (`domain` è l'host completo di un sito; non c'è `subdomain`)                                                                                                                                                                                                       |
| `delete_app(id)`                                                                                  | `apps.delete(id)`                                                                    | Restituisce `None`                                                                                                                                                                                                                                                                                               |
| `all_apps_status()`                                                                               | `apps.status_all(*, workspace_id=None)`                                              | Le app arrestate non vengono più scartate                                                                                                                                                                                                                                                                        |
| `app_status(id)`                                                                                  | `apps.status(id, *, raw=False)`                                                      | `raw=True` (solo keyword) restituisce numeri                                                                                                                                                                                                                                                                     |
| `start_app` / `stop_app` / `restart_app`                                                          | `apps.start` / `apps.stop` / `apps.restart`                                          | Restituiscono `None`                                                                                                                                                                                                                                                                                             |
| `get_logs(id)`                                                                                    | `apps.logs(id)`                                                                      | Restituisce la `str`                                                                                                                                                                                                                                                                                             |
| `app_metrics(id)`                                                                                 | `apps.metrics(id)`                                                                   |                                                                                                                                                                                                                                                                                                                  |
| `realtime(id)` (generatore asincrono di ogni riga `data:`, decodificata da JSON quando possibile) | `apps.realtime(id)`                                                                  | Iteratore di `{'event', 'data', 'id'}` con il testo `data` grezzo, più `stream`/`line` nei log e lo `status` unito negli eventi di stato. Si riconnette da solo (vedi Cambiamenti di comportamento); il flusso non ha timeout di lettura (la v4 si arrendeva dopo 90 s senza dati), quindi fermalo con `close()` |
| `all_domains()`                                                                                   | `apps.domains()`                                                                     |                                                                                                                                                                                                                                                                                                                  |
| `load_balancers()`                                                                                | `apps.load_balancers()`                                                              |                                                                                                                                                                                                                                                                                                                  |
| `commit(id, File(path))`                                                                          | `apps.commit(id, file, *, path=None, filename=None)`                                 | `path` solo keyword = directory di destinazione; `filename` dà il nome a un file non zip (i `bytes` usano `commit.zip` come predefinito)                                                                                                                                                                         |
| `github_integration(id, access_token)`                                                            | `apps.deploys.set_webhook(id, access_token)`                                         |                                                                                                                                                                                                                                                                                                                  |
| `last_deploys(id)`                                                                                | `apps.deploys.list(id)`                                                              | Gli eventi includono `source`, `branch`, `files`; un deploy fallito termina con `state='error'` più `code` e `message`                                                                                                                                                                                           |
| `current_app_integration(id)`                                                                     | `apps.deploys.current(id)`                                                           | Restituisce `{'app'?, 'webhook'?}` invece della stringa del webhook                                                                                                                                                                                                                                              |
| `get_app_envs` / `set_app_envs` / `overwrite_app_envs` / `delete_app_envs`                        | `apps.envs.get` / `set` / `replace` / `delete`                                       |                                                                                                                                                                                                                                                                                                                  |
| `clear_app_envs(id)`                                                                              | `apps.envs.replace(id, {})`                                                          |                                                                                                                                                                                                                                                                                                                  |
| `app_files_list(id, path)`                                                                        | `apps.files.list(id, path=None)`                                                     | Le voci sono il `FileEntry` dell'API (senza `path`/`app_id` calcolati). Una directory mancante solleva 404 `FILE_NOT_FOUND` invece di restituire `[]`; una bloccata produce 403 `BLOCKED_PATH`                                                                                                                   |
| `read_app_file(id, path)` → `BytesIO`                                                             | `apps.files.read(id, path)` → `bytes`                                                | Scaricato codificato in base64 (`?encoding=base64`) e decodificato; oltre 10 MB produce 413 `FILE_TOO_LARGE`                                                                                                                                                                                                     |
| `create_app_file(id, File(...), path)`                                                            | `apps.files.write(id, path, content)`                                                | Una `str` viene inviata come testo, i `bytes` codificati in base64 (sicuro per i binari); percorso inviato così com'è (nessuna `/` aggiuntiva); un contenuto vuoto (`''` o `b''`) crea un file vuoto                                                                                                             |
| `move_app_file(id, origin, dest)`                                                                 | `apps.files.move(id, path, to)`                                                      |                                                                                                                                                                                                                                                                                                                  |
| `delete_app_file(id, path)`                                                                       | `apps.files.delete(id, path)`                                                        |                                                                                                                                                                                                                                                                                                                  |
| `snapshot(id)` → `Snapshot`                                                                       | `apps.snapshots.create(id)`                                                          | `{'pending': True}` su 202 invece di sollevare un'eccezione; poi interroga `list`, non ricreare mai                                                                                                                                                                                                              |
| `all_app_snapshots(id)`                                                                           | `apps.snapshots.list(id)`                                                            | Gli elementi contengono il `version_id` e l'`url` di download firmato dell'API, passati così come vengono inviati                                                                                                                                                                                                |
| `restore_snapshot('app', id, snapshot_id, version_id)`                                            | `apps.snapshots.restore(id, name, version_id)`                                       | `name` e `version_id` di un elemento di `list`                                                                                                                                                                                                                                                                   |
| `restore_snapshot('database', id, ...)`                                                           | `databases.snapshots.restore(id, name, version_id)`                                  | `name` e `version_id` di un elemento di `list`                                                                                                                                                                                                                                                                   |
| `Snapshot.download(path)`                                                                         | `client.download_snapshot(url, dest)`                                                | Trasmette in streaming lo zip stesso (la v4 lo racchiudeva in un altro zip)                                                                                                                                                                                                                                      |
| `domain_analytics(id, start=, end=, ...)`                                                         | `apps.network.analytics(id, start, end, *, country=, ...)`                           | `start`/`end` sono obbligatori per l'API; i filtri sono solo keyword                                                                                                                                                                                                                                             |
| `network_errors(id, start, end, include_4xx)` / `network_logs` / `network_performance`            | `apps.network.errors(id, start, end, *, include_4xx=False)` / `logs` / `performance` | `analytics`, `errors` e `performance` restituiscono `None` per una finestra senza dati                                                                                                                                                                                                                           |
| `dns_records(id)`                                                                                 | `apps.network.dns(id)`                                                               |                                                                                                                                                                                                                                                                                                                  |
| `set_custom_domain(id, custom_domain)`                                                            | `apps.network.set_domain(id, domain)`                                                | La v4 non inviava mai il body                                                                                                                                                                                                                                                                                    |
| `purge_cache(id)`                                                                                 | `apps.network.purge_cache(id)`                                                       |                                                                                                                                                                                                                                                                                                                  |
| `link_github_app(id, repository_name, repository_branch)`                                         | `apps.deploys.link_github_app(id, repository, branch)`                               | Ora funziona con una chiave API (scope `apps:deploy`); restituisce il `LinkedRepository` (`{id, full_name, branch}`) invece del dict grezzo; 403 `GITHUB_NOT_CONNECTED` senza un'installazione della GitHub App                                                                                                  |
| `unlink_github_app(id)`                                                                           | `apps.deploys.unlink_github_app(id)`                                                 | Restituisce `None`; 400 `GIT_NOT_CONFIGURED` quando non c'è nulla di collegato                                                                                                                                                                                                                                   |
| `create_database(name, memory, type, version=None)`                                               | `databases.create(name, type=, version=, memory=)`                                   | Solo keyword; `version` è obbligatorio (va bene una major come `'8'`)                                                                                                                                                                                                                                            |
| `get_database_info(id)`                                                                           | `databases.get(id)`                                                                  |                                                                                                                                                                                                                                                                                                                  |
| `edit_database(id, name, memory)`                                                                 | `databases.update(id, name=, ram=)`                                                  | La v4 inviava `memory`, che l'API ignorava                                                                                                                                                                                                                                                                       |
| `delete_database` / `start_database` / `stop_database`                                            | `databases.delete` / `start` / `stop`                                                |                                                                                                                                                                                                                                                                                                                  |
| `get_database_status(id)`                                                                         | `databases.status(id, *, raw=False)`                                                 |                                                                                                                                                                                                                                                                                                                  |
| `all_databases_status()`                                                                          | `databases.status_all()`                                                             |                                                                                                                                                                                                                                                                                                                  |
| `database_metrics(id)`                                                                            | `databases.metrics(id)`                                                              |                                                                                                                                                                                                                                                                                                                  |
| `get_database_certificate(id)` → `Certificate`                                                    | `databases.certificate(id)` → `str` in base64                                        | `base64.b64decode(...)` restituisce il PEM                                                                                                                                                                                                                                                                       |
| `reset_database_password(id)`                                                                     | `databases.reset_credentials(id, 'password')`                                        | Restituisce la nuova password                                                                                                                                                                                                                                                                                    |
| `reset_database_certificate(id)`                                                                  | `databases.reset_credentials(id, 'certificate')`                                     | Restituisce `''`                                                                                                                                                                                                                                                                                                 |
| `all_database_snapshots(id)` / `database_snapshot(id)`                                            | `databases.snapshots.list(id)` / `create(id)`                                        | Gli elementi contengono `version_id` e `url`, come per le app                                                                                                                                                                                                                                                    |
| `create_workspace(name)`                                                                          | `workspaces.create(name)`                                                            | Restituisce `{id, name}` in una sola richiesta                                                                                                                                                                                                                                                                   |
| `all_workspaces()` / `get_workspace(id)`                                                          | `workspaces.list()` / `workspaces.get(id)`                                           | La v4 riscriveva l'`id` di ogni app nel composto `'<appId>-<workspaceId>'`; la v5 restituisce l'id grezzo dell'API, quindi costruiscilo tu: `f"{app['id']}-{workspace['id']}"`                                                                                                                                   |
| `delete_workspace` / `leave_workspace`                                                            | `workspaces.delete` / `workspaces.leave`                                             |                                                                                                                                                                                                                                                                                                                  |
| `add_member_to_workspace(ws, invite_code, permissions)`                                           | `workspaces.members.add(ws, code, group)`                                            |                                                                                                                                                                                                                                                                                                                  |
| `modify_member_permissions(ws, user_id, permissions)`                                             | `workspaces.members.update(ws, member_id, group)`                                    |                                                                                                                                                                                                                                                                                                                  |
| `remove_member_from_workspace(ws, user_id)`                                                       | `workspaces.members.remove(ws, member_id)`                                           |                                                                                                                                                                                                                                                                                                                  |
| `get_invite_code()`                                                                               | `workspaces.members.invite_code()`                                                   |                                                                                                                                                                                                                                                                                                                  |
| `add_app_to_workspace` / `remove_app_from_workspace`                                              | `workspaces.apps.add` / `workspaces.apps.remove`                                     |                                                                                                                                                                                                                                                                                                                  |
| —                                                                                                 | `ai.chat(request)`                                                                   | Nuovo; `request` è il body in stile OpenAI (`{'messages': [...], 'model': ...}`)                                                                                                                                                                                                                                 |
| `client.api_key`                                                                                  | Rimosso                                                                              | Conserva tu un riferimento alla chiave                                                                                                                                                                                                                                                                           |
| `on_request(endpoint)`, `Application.capture(endpoint)`                                           | Rimossi                                                                              | Vedi la riga dei listener in [In sintesi](#in-sintesi)                                                                                                                                                                                                                                                           |
| `squarecloud.utils.ConfigFile`                                                                    | Rimosso                                                                              | Scrivi tu il file `squarecloud.app` (righe `KEY=value`)                                                                                                                                                                                                                                                          |

I metodi di `Application` corrispondono alle stesse chiamate con l'id: `app.logs()` → `client.apps.logs(app.id)`, `app.files_list(path)` → `client.apps.files.list(app.id, path)`, e così via.

## Tipi

Le risposte sono `TypedDict` in `squarecloud.types`, con gli stessi nomi degli SDK JS e Go: `Account`, `User`, `Plan`, `AppSummary`, `DatabaseSummary`, `App`, `AppCreated`, `StatusListItem`, `RuntimeStats`, `MetricPoint`, `AppDomain`, `LoadBalancers`, `DeployEvent`, `DeployCurrent`, `DeployRepository`, `LinkedRepository`, `EnvVars`, `FileEntry`, `Snapshot`, `SnapshotCreated`, `SnapshotScope`, `AnalyticsFilters`, `NetworkAnalytics`, `NetworkErrors`, `NetworkLog`, `NetworkPerformance`, `DNSRecord`, `Database`, `DatabaseCreated`, `DatabaseType`, `Workspace`, `WorkspaceCreated`, `WorkspaceGroup`, `ServiceStatus`, `ServiceEntry`, `ChatRequest`, `ChatMessage`, `ChatCompletion`, `RealtimeEvent`, `RealtimeStatus`. Sostituiscono le dataclass `data/*` della v4 (`UserData`, `StatusData`, `AppData`, ...). `squarecloud.Response` ora è il protocollo della risposta del transport (vedi [Transport personalizzato](/it/sdks/py/client#transport-personalizzato)); la `Response` della v4 restituita dalle mutazioni non esiste più, e ora queste restituiscono `None`.

## Errori

| v4                                                             | v5                                                                                                                      |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `AuthenticationFailure`                                        | `e.status == 401` (`ACCESS_DENIED`, anche per una chiave scaduta)                                                       |
| `NotFoundError`, `ApplicationNotFound`                         | `e.status == 404` (`APP_NOT_FOUND`, `WORKSPACE_NOT_FOUND`, ...)                                                         |
| `BadRequestError` e la famiglia `InvalidConfig`                | `e.status == 400`, controlla `e.code`                                                                                   |
| `TooManyRequests`                                              | `e.status == 429` (`RATE_LIMITED`, `KEEP_CALM`)                                                                         |
| `FewMemory` (mai sollevata: l'API invia `INSUFFICIENT_MEMORY`) | `e.code == 'INSUFFICIENT_MEMORY'`                                                                                       |
| `InvalidDomain` (`REGEX_VALIDATION`, non più inviato)          | `e.code == 'INVALID_DOMAIN'`                                                                                            |
| `RequestError` per 403/503                                     | `e.code` tra `MISSING_SCOPE`, `RESOURCE_NOT_ALLOWED`, `BLOCKED_PATH`, `UPLOAD_BUSY`, ...                                |
| Eccezioni di `aiohttp` che trapelavano                         | `e.status == 0`, `e.code` tra `NETWORK_ERROR`, `TIMEOUT` (eccezione originale in `e.cause`)                             |
| —                                                              | `e.status == 0` con `FILE_TOO_LARGE` o `INVALID_ID`: controlli locali, non è stato inviato nulla                        |
| HTML di un proxy o un body non JSON che trapelavano            | `UNKNOWN_ERROR` con lo status reale e il messaggio `HTTP <status>` (`Invalid JSON in HTTP <status> response` su un 2xx) |

`str(e)` è `'<METHOD> <path>: HTTP <status> <CODE>: <message>'`, senza `HTTP <status>` quando lo status è `0` e senza `: <message>` quando è vuoto. `e.message` è `''` quando il server ha inviato solo un codice.

## Cambiamenti di comportamento

* I modificatori facoltativi sono solo keyword: `account.snapshots(scope=)`, `apps.status_all(workspace_id=)`, `apps.status(id, raw=)`, `databases.status(id, raw=)`, `apps.commit(id, file, path=, filename=)`, `apps.network.errors(..., include_4xx=)`, i filtri di `apps.network.analytics(...)`, `databases.update(id, name=, ram=)` e `databases.create(name, type=, version=, memory=)`. Il `path` facoltativo di `apps.files.list` resta posizionale.
* Un body 2xx `{"status": "error"}` solleva un'eccezione. I rifiuti di avvio/arresto di app e database sono 409 con solo un codice (`CONTAINER_ALREADY_STARTED`, `ACTION_FAILED`, ...). Un 202 `SNAPSHOT_PROCESSING` restituisce `{'pending': True}` invece di sollevare un'eccezione: interroga `list`, non chiamare mai di nuovo `create`.
* I valori di query facoltativi non impostati (e `''`) vengono omessi invece di essere inviati.
* I risultati stringa non sono mai `None`: `reset_credentials(id, 'certificate')` e un webhook rimosso restituiscono `''`.
* `apps.files.write` invia una `str` come testo e i `bytes` codificati in base64; un contenuto vuoto crea un file vuoto; un contenuto oltre 1 MiB viene inviato senza timeout. `apps.files.read` richiede sempre il base64 e restituisce i `bytes` decodificati.
* `apps.files.list` di una directory mancante solleva 404 `FILE_NOT_FOUND`.
* 503 `DATABASE_UNAVAILABLE` viene ripetuto solo su `GET`, perché può verificarsi dopo che una mutazione è stata applicata; ripetere una mutazione idempotente spetta al chiamante.
* Il flusso realtime produce eventi `{'event', 'data', 'id', ...}`, si riapre al massimo 3 volte di fila con un'apertura ogni 5,5 s e solleva un'eccezione quando un'apertura fallisce.

## Async

La v4 era solo asincrona. Nella v5, `SquareCloud` è sincrono e `AsyncSquareCloud` è la facciata `await`: gli stessi gruppi e metodi, con ogni chiamata eseguita in `asyncio.to_thread`, quindi l'event loop non viene mai bloccato. Il flusso realtime diventa `async for` (un thread lettore alimenta il loop); chiudilo con `async with` o `close()`.

v4:

```python theme={"system"}
client = squarecloud.Client(key)
status = await client.app_status(app_id)
print(status.ram)
async for data in client.realtime(app_id):
    print(data)
```

v5:

```python theme={"system"}
async with squarecloud.AsyncSquareCloud(key) as client:
    status = await client.apps.status(app_id)
    print(status['ram'])
    async with client.apps.realtime(app_id) as stream:
        async for event in stream:
            print(event['data'])
```
