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

# Blob Upload Tokens

> Genera un upload token di breve durata con POST /v1/upload-tokens così un browser può caricare direttamente su Blob Storage senza la tua chiave API.

<ParamField header="Authorization" type="string" placeholder="API Key" required>
  La chiave API del tuo account. Puoi trovarla nelle [impostazioni del tuo account](https://squarecloud.app/it/account/security).
</ParamField>

Upload Tokens permette al tuo **server** di generare un token di breve durata che un **browser** o un'app mobile usa per caricare direttamente su Blob Storage. Il file non passa mai per il tuo server e la tua chiave API non lo lascia mai. Richiede lo scope `blob:write` e un piano a pagamento.

Il token va nell'header `Authorization` di [Object Post](/it/blob-reference/endpoint/post) o delle route di [upload chunked](/it/blob-reference/endpoint/chunked-init), e non funziona da nessun'altra parte (`403 UPLOAD_TOKEN_NOT_ALLOWED`). Tutto ciò che imposti al momento della generazione è **fissato**: il browser non può modificare nome, prefisso, visibilità, scadenza o metadati, inviare un file più grande o usare un altro tipo di file.

* Ogni upload consuma un utilizzo: una chiamata a Object Post, oppure l'apertura di un upload chunked (le sue parti e il completamento non ne consumano altri).
* Senza un `name`, lo sceglie il browser e il nome riceve sempre un security hash, così un token trapelato non può mai sostituire i tuoi file esistenti.
* Il token smette di funzionare quando esaurisce gli utilizzi (`401 UPLOAD_TOKEN_USED`), quando scade o quando la chiave API che lo ha generato viene revocata (`401 ACCESS_DENIED`).

<ParamField body="name" type="string">
  Fissa il nome del file. Senza di esso, il browser invia `name` nella query.
</ParamField>

<ParamField body="prefix" type="string">
  Fissa il prefisso. Un browser che ne invia uno diverso riceve `403 PREFIX_NOT_ALLOWED`.
</ParamField>

<ParamField body="private" type="boolean">
  Fissa la visibilità.
</ParamField>

<ParamField body="security_hash" type="boolean">
  Solo con `name`: `false` permette all'upload di sostituire il file con quel nome. Altrimenti il nome riceve sempre un hash.
</ParamField>

<ParamField body="expire" type="string | null">
  Fissa la scadenza (`30d`, `6h`), oppure `null` per nessuna. Sotto i 7 giorni richiede Enterprise.
</ParamField>

<ParamField body="max_size" type="number">
  Dimensione massima del file in byte, da 512 a 10737418240 (10 GiB).
</ParamField>

<ParamField body="allowed_extensions" type="string[]">
  Estensioni accettate, da 1 a 20, in minuscolo e senza il punto (`png`, `tar.gz`).
</ParamField>

<ParamField body="metadata" type="object">
  Metadati aggiunti a ogni file caricato con il token. Solo Pro ed Enterprise.
</ParamField>

<ParamField body="expires_in" type="number" default="900">
  Quanto dura il token, da 60 a 3600 secondi.
</ParamField>

<ParamField body="max_uses" type="number" default="1">
  Quanti upload consente il token, da 1 a 100.
</ParamField>

### Limiti di frequenza

<Note>120 token al minuto (`RATE_LIMITED`, 429). La generazione non archivia nulla, quindi va benissimo un token per ogni upload dell'utente finale.</Note>

### Risposta

<ResponseField name="status" type="string">
  "success" in caso di successo, "error" in caso contrario.
</ResponseField>

<ResponseField name="response" type="object">
  <Expandable title="Mostra oggetto">
    <ResponseField name="token" type="string">
      L'upload token (`squp_...`). Consegnalo al browser.
    </ResponseField>

    <ResponseField name="expires_at" type="ISO 8601">
      Quando scade il token.
    </ResponseField>

    <ResponseField name="max_uses" type="number">
      Quanti upload consente il token.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```javascript Server (Node.js) theme={null}
  // Your backend: mint a token for the signed-in user and return it to the page.
  const res = await fetch('https://blob.squarecloud.app/v1/upload-tokens', {
    method: 'POST',
    headers: {
      Authorization: process.env.SQUARE_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      prefix: `avatars/${userId}`,
      allowed_extensions: ['png', 'jpg', 'webp'],
      max_size: 2 * 1024 * 1024,
      expires_in: 300,
    }),
  });
  const { response } = await res.json();
  // send response.token to the browser
  ```

  ```javascript Browser theme={null}
  const form = new FormData();
  form.append('file', input.files[0]);

  const res = await fetch('https://blob.squarecloud.app/v1/objects?name=avatar', {
    method: 'POST',
    headers: { Authorization: token },
    body: form,
  });
  const { response } = await res.json();
  // response.url is the public URL of the new file
  ```

  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://blob.squarecloud.app/v1/upload-tokens' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{ "prefix": "avatars", "allowed_extensions": ["png", "jpg"], "max_size": 2097152 }'
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "status": "success",
    "response": {
      "token": "squp_Tq7xLm2Vb9Rk4Wz1Nc8Hy5Js0Gd3Fp6Ae.Hq3mZ8vT1kLw9xRb",
      "expires_at": "2026-09-25T12:15:00.000Z",
      "max_uses": 1
    }
  }
  ```
</ResponseExample>

### Errori

| Codice                                                                                                                                                            | HTTP | Quando                                                                             |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------------------------------- |
| `INVALID_BODY`                                                                                                                                                    | 400  | Il corpo non è un oggetto JSON.                                                    |
| `INVALID_OBJECT_NAME` / `INVALID_OBJECT_PREFIX` / `INVALID_OBJECT_PRIVATE` / `INVALID_OBJECT_SECURITY_HASH` / `INVALID_OBJECT_EXPIRE` / `INVALID_OBJECT_METADATA` | 400  | Un valore fissato non è valido.                                                    |
| `INVALID_MAX_SIZE` / `INVALID_ALLOWED_EXTENSIONS` / `INVALID_EXPIRES_IN` / `INVALID_MAX_USES`                                                                     | 400  | Un limite è fuori intervallo.                                                      |
| `UPLOAD_TOKEN_TOO_LARGE`                                                                                                                                          | 400  | Troppe opzioni per un solo token. Accorcia i metadati o l'elenco delle estensioni. |
| `PERMISSION_DENIED`                                                                                                                                               | 401  | L'account non ha un piano a pagamento attivo.                                      |
| `UPGRADE_REQUIRED`                                                                                                                                                | 403  | La scadenza o i metadati richiedono un piano superiore.                            |
| `RATE_LIMITED`                                                                                                                                                    | 429  | Più di 120 token in un minuto.                                                     |

Quando il browser carica: `401 UPLOAD_TOKEN_USED` (utilizzi esauriti), `401 ACCESS_DENIED` (scaduto o revocato), `403 PREFIX_NOT_ALLOWED` (altro prefisso), `400 FILE_TYPE_NOT_ALLOWED` (estensione non consentita) e `413 FILE_TOO_LARGE` (oltre `max_size`).
