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

> Générez un jeton d'envoi de courte durée avec POST /v1/upload-tokens afin qu'un navigateur puisse envoyer directement vers Blob Storage sans votre clé API.

<ParamField header="Authorization" type="string" placeholder="API Key" required>
  La clé d'API de votre compte. Vous pouvez la trouver dans les [paramètres de votre compte](https://squarecloud.app/fr/account/security).
</ParamField>

Upload Tokens permet à votre **serveur** de générer un jeton de courte durée qu'un **navigateur** ou une application mobile utilise pour envoyer directement vers Blob Storage. Le fichier ne passe jamais par votre serveur, et votre clé API ne le quitte jamais. Nécessite le scope `blob:write` et un plan payant.

Le jeton se place dans l'en-tête `Authorization` d'[Object Post](/fr/blob-reference/endpoint/post) ou des routes d'[envoi chunked](/fr/blob-reference/endpoint/chunked-init), et ne fonctionne nulle part ailleurs (`403 UPLOAD_TOKEN_NOT_ALLOWED`). Tout ce que vous définissez lors de sa création est **figé** : le navigateur ne peut pas modifier le nom, le préfixe, la visibilité, l'expiration ou les métadonnées, envoyer un fichier plus volumineux, ni utiliser un autre type de fichier.

* Chaque envoi consomme une utilisation : un appel à Object Post, ou l'ouverture d'un envoi chunked (ses parties et sa finalisation n'en consomment pas davantage).
* Sans `name`, c'est le navigateur qui le choisit et le nom reçoit toujours un security hash, de sorte qu'un jeton divulgué ne peut jamais remplacer vos fichiers existants.
* Le jeton cesse de fonctionner lorsque ses utilisations sont épuisées (`401 UPLOAD_TOKEN_USED`), lorsqu'il expire ou lorsque la clé API qui l'a généré est révoquée (`401 ACCESS_DENIED`).

<ParamField body="name" type="string">
  Fige le nom du fichier. Sans lui, le navigateur envoie `name` dans la query.
</ParamField>

<ParamField body="prefix" type="string">
  Fige le préfixe. Un navigateur qui en envoie un autre reçoit `403 PREFIX_NOT_ALLOWED`.
</ParamField>

<ParamField body="private" type="boolean">
  Fige la visibilité.
</ParamField>

<ParamField body="security_hash" type="boolean">
  Uniquement avec `name` : `false` permet à l'envoi de remplacer le fichier portant ce nom. Sinon, le nom reçoit toujours un hash.
</ParamField>

<ParamField body="expire" type="string | null">
  Fige l'expiration (`30d`, `6h`), ou `null` pour aucune. En dessous de 7 jours, nécessite Enterprise.
</ParamField>

<ParamField body="max_size" type="number">
  Taille maximale de fichier en octets, de 512 à 10737418240 (10 GiB).
</ParamField>

<ParamField body="allowed_extensions" type="string[]">
  Extensions acceptées, de 1 à 20, en minuscules et sans le point (`png`, `tar.gz`).
</ParamField>

<ParamField body="metadata" type="object">
  Métadonnées ajoutées à chaque fichier envoyé avec le jeton. Pro et Enterprise uniquement.
</ParamField>

<ParamField body="expires_in" type="number" default="900">
  Durée de validité du jeton, de 60 à 3600 secondes.
</ParamField>

<ParamField body="max_uses" type="number" default="1">
  Nombre d'envois autorisés par le jeton, de 1 à 100.
</ParamField>

### Limites de débit

<Note>120 jetons par minute (`RATE_LIMITED`, 429). La génération ne stocke rien, un jeton par envoi d'utilisateur final convient donc parfaitement.</Note>

### Réponse

<ResponseField name="status" type="string">
  "success" en cas de succès, "error" sinon.
</ResponseField>

<ResponseField name="response" type="object">
  <Expandable title="Afficher l'objet">
    <ResponseField name="token" type="string">
      Le jeton d'envoi (`squp_...`). Transmettez-le au navigateur.
    </ResponseField>

    <ResponseField name="expires_at" type="ISO 8601">
      Date d'expiration du jeton.
    </ResponseField>

    <ResponseField name="max_uses" type="number">
      Nombre d'envois autorisés par le jeton.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```javascript Serveur (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 Navigateur 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>

### Erreurs

| Code                                                                                                                                                              | HTTP | Quand                                                                                      |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------ |
| `INVALID_BODY`                                                                                                                                                    | 400  | Le corps n'est pas un objet JSON.                                                          |
| `INVALID_OBJECT_NAME` / `INVALID_OBJECT_PREFIX` / `INVALID_OBJECT_PRIVATE` / `INVALID_OBJECT_SECURITY_HASH` / `INVALID_OBJECT_EXPIRE` / `INVALID_OBJECT_METADATA` | 400  | Une valeur figée est invalide.                                                             |
| `INVALID_MAX_SIZE` / `INVALID_ALLOWED_EXTENSIONS` / `INVALID_EXPIRES_IN` / `INVALID_MAX_USES`                                                                     | 400  | Une limite est hors plage.                                                                 |
| `UPLOAD_TOKEN_TOO_LARGE`                                                                                                                                          | 400  | Trop d'options pour un seul jeton. Raccourcissez les métadonnées ou la liste d'extensions. |
| `PERMISSION_DENIED`                                                                                                                                               | 401  | Le compte n'a pas de plan payant actif.                                                    |
| `UPGRADE_REQUIRED`                                                                                                                                                | 403  | L'expiration ou les métadonnées nécessitent un plan supérieur.                             |
| `RATE_LIMITED`                                                                                                                                                    | 429  | Plus de 120 jetons en une minute.                                                          |

Lorsque le navigateur envoie le fichier : `401 UPLOAD_TOKEN_USED` (épuisé), `401 ACCESS_DENIED` (expiré ou révoqué), `403 PREFIX_NOT_ALLOWED` (autre préfixe), `400 FILE_TYPE_NOT_ALLOWED` (extension non autorisée) et `413 FILE_TOO_LARGE` (au-delà de `max_size`).
