> ## 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 SDK: uploads and upload tokens

> Upload files with put(): file paths, Blob/File or bytes, automatic multipart uploads above 90 MiB, and upload tokens for uploading straight from the browser.

Examples use the `blob` client from [Creating the client](/en/sdks/blob/client#creating-the-client).

## `put(file, options)`

`put()` uploads one file and returns the new object.

```typescript theme={"system"}
const { id, url } = await blob.put("./photo.png", {
    name: "photo",
    prefix: "avatars",
});
```

### Inputs

| Input | Environment | Notes |
| - | - | - |
| `string` (file path) | **Node.js only** | Opened with `fs.openAsBlob` and **streamed from disk**, never read whole into memory. The extension comes from the path's basename. |
| `Blob` / `File` | Node.js and browsers | A `File` gives its `name` (and its extension). |
| `Uint8Array` (including `Buffer`) | Node.js and browsers | Pass `filename` to set the extension. |
| `ArrayBuffer` | Node.js and browsers | Pass `filename` to set the extension. |

The object's extension comes from `filename`, then the path's basename or the `File`'s name. Bytes and a plain `Blob` have no name, so **pass `filename`** (for example `"data.json"`) when uploading them.

<Note>
  In Node.js, a path that cannot be opened throws a plain `Error` (`Cannot open file: <path>`, with the original error in `cause`). In a browser, passing a path fails earlier, with the error from importing `node:fs`. Use a `File` from an `<input type="file">` instead.
</Note>

### Options

| Option | Type | Description |
| - | - | - |
| `name` | `string` | Object name **without extension**, matching `^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$`, no `..`, not ending in `-ex<digits>`. Required unless an upload token fixes it. |
| `prefix` | `string` | Folder-like path: up to 8 segments of `^[A-Za-z0-9_][A-Za-z0-9_.-]{0,63}$`, 256 characters. |
| `private` | `boolean` | Default `false`. Private objects always get a security hash and have `url: null`. |
| `security_hash` | `boolean` | Appends `_<hash>` to the name. |
| `expire` | `string` | `"30"` (days), `"30d"` or `"168h"`; from 7 to 1825 days (Enterprise down to 1 hour). The expiration becomes part of the id. |
| `overwrite` | `boolean` | `false` fails with `OBJECT_ALREADY_EXISTS` when the name is taken. **Simple uploads only.** |
| `disposition` | `"inline"` \| `"attachment"` | `Content-Disposition` of the object. |
| `auto_download` | `boolean` | Forces a download (`application/octet-stream`). |
| `cache_control` | `string` | `immutable`, `max-age=60..31536000` or `no-cache` (Enterprise). |
| `metadata` | `Record<string, string>` | Pro and Enterprise. Keys `^[a-z0-9-]{1,64}$` (never `sq-*`), up to 5 keys and 512 bytes. |
| `checksum_sha256` | `string` | 64 lowercase hex characters. A mismatch fails with `CHECKSUM_MISMATCH` and nothing is stored. **Simple uploads only.** |
| `filename` | `string` | File name whose extension becomes the object's extension. Defaults to the `File` name or the path's basename. |
| `mime_type` | `string` | Only used to pick the extension when `filename` has none. The server derives the `Content-Type`. |

See [Object Post](/en/blob-reference/endpoint/post) for the full server-side rules.

### Result

| Field | Type | Description |
| - | - | - |
| `id` | `string` | Opaque object id. [Store it as-is](/en/sdks/blob/client#object-ids). |
| `private` | `boolean` | Whether the object is private. |
| `url` | `string \| null` | Public URL, or `null` for a private object (use [`downloadUrl()`](/en/sdks/blob/objects#download-links)). |
| `expires_at` | `string` | When the object expires, if it has an expiration. |
| `size` | `number` | Size in bytes. |
| `name`, `prefix`, `sha256`, `replaced` | | Simple uploads only. |
| `parts` | `number` | Multipart uploads only: number of parts sent. |

## Simple and multipart uploads

`put()` picks the upload flow from the file size:

| File size | Flow | Endpoint |
| - | - | - |
| Up to **90 MiB** (94,371,840 bytes) inclusive | Simple upload, one request | [Object Post](/en/blob-reference/endpoint/post) |
| Above 90 MiB, up to **10 GiB** | Multipart (chunked) upload | [Chunked Init](/en/blob-reference/endpoint/chunked-init) |

Every file must have **at least 512 bytes**; smaller files fail with `FILE_TOO_SMALL`. A rule or upload token `max_size`, or your storage quota, can lower the 10 GiB ceiling.

### How a multipart upload runs

1. The SDK starts the upload, and the server answers with its part limits (`max_size`, `max_parts`).
2. The file is split into parts of `min(max_size, max(16 MiB, ceil(size / max_parts)))` bytes. A part is usually 16 MiB or more, but it is smaller when the server's `max_size` is smaller.
3. Parts are sent **6 at a time**, the server's limit for parts in flight. A failed part is [retried](/en/sdks/blob/errors#retry-policy) within `maxRetries`.
4. When every part arrived, the SDK completes the upload.

If a part fails for good, or completing fails, the SDK **waits for the parts still in flight** and then **aborts** the upload, so no part lands after the abort. The original error is thrown. There is **no resume**: call `put()` again.

<Warning>
  A multipart upload **does not check `overwrite: false` or `checksum_sha256`**. It always replaces an existing object with the same name.
</Warning>

<Note>
  An account can have at most **32 open multipart uploads** (shared with the S3 gateway); one more fails with `TOO_MANY_OPEN_UPLOADS`. Since parts go 6 at a time per account, run **one large upload at a time**.
</Note>

## Uploading from the browser

Never send the API key to a browser. Instead, your server mints a short-lived **upload token** and the browser uploads with it.

<Tabs>
  <Tab title="Server">
    ```typescript theme={"system"}
    import { SquareCloudBlob } from "@squarecloud/blob";

    const blob = new SquareCloudBlob(process.env.SQUARECLOUD_API_KEY);

    // e.g. inside your API route
    const { token } = await blob.uploadTokens.create({
        prefix: "avatars/",
        security_hash: true,
        max_size: 5 * 1024 * 1024,
        allowed_extensions: ["png", "jpg"],
    });
    // send only `token` to the browser
    ```
  </Tab>

  <Tab title="Browser">
    ```typescript theme={"system"}
    import { SquareCloudBlob } from "@squarecloud/blob";

    const upload = new SquareCloudBlob(token);
    const { url } = await upload.put(input.files[0], { name: "avatar" });
    ```
  </Tab>
</Tabs>

A client created with a token can **only call `put()`**, including multipart uploads. Any other method fails with `403 UPLOAD_TOKEN_NOT_ALLOWED`.

### `uploadTokens.create(options)`

The token pins every option it was minted with.

| Option | Type | Description |
| - | - | - |
| `name` | `string` | Fixes the object name. Without it, the security hash is always applied. |
| `prefix` | `string` | Fixes the prefix. |
| `private` | `boolean` | Fixes the visibility. |
| `security_hash` | `boolean` | Appends `_<hash>` to the name. |
| `expire` | `string` | Object expiration, as in `put()`. |
| `max_size` | `number` | Maximum file size in bytes. |
| `allowed_extensions` | `string[]` | 1 to 20 extensions. |
| `metadata` | `Record<string, string>` | Metadata applied to the uploaded object. |
| `expires_in` | `number` | Token lifetime in seconds, 60 to 3600 (default 900). |
| `max_uses` | `number` | Uploads allowed, 1 to 100 (default 1). Then the token fails with `UPLOAD_TOKEN_USED`. |

It returns `{ token, expires_at, max_uses }`. See [Upload Tokens](/en/blob-reference/endpoint/upload-tokens) for the server-side rules.

<Note>
  `uploadTokens.create()` is a write and gets a **single attempt**: it is never retried.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Objects" icon="folder-open" href="/en/sdks/blob/objects">
    List, update, copy, move and delete objects.
  </Card>

  <Card title="Upload API reference" icon="code" href="/en/blob-reference/endpoint/post">
    The REST endpoints behind put().
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.