# Uploads & Downloads (/docs/uploads)



## Uploading [#uploading]

Pass the body as-is. The client doesn't JSON-encode it and picks the right `Content-Type`:

```ts
const form = new FormData();
form.append("avatar", file);
await api.post("/me/avatar", form);   // multipart, boundary set by the browser

await api.put("/files/1", file);      // File or Blob: sent as raw bytes
await api.post("/raw", new Uint8Array([1, 2, 3]));
```

| Body                        | Sent as                                                             |
| --------------------------- | ------------------------------------------------------------------- |
| Plain object or array       | JSON                                                                |
| `FormData`                  | `multipart/form-data`                                               |
| `File`, `Blob`              | Raw bytes with the file's type                                      |
| `ArrayBuffer`, typed arrays | Raw bytes                                                           |
| `URLSearchParams`           | `application/x-www-form-urlencoded`                                 |
| `string`                    | As-is, with `Content-Type: application/json` unless you set another |

## Long uploads [#long-uploads]

The default timeout is 30 s. Raise it for big files:

```ts
await api.post("/upload", form, { timeout: 10 * 60_000 });
```

If the token expires mid-upload, the server answers 401 and the client refreshes and re-sends the file automatically. To avoid uploading twice, refresh *before* starting when the token won't last the whole upload:

```ts
await api.post("/upload", form, { uploadSkewMs: 10 * 60_000 });
```

## Streams [#streams]

A `ReadableStream` body works only with `worker: false`, because a stream can't be passed into a Web Worker. It also can't be replayed after a 401, so combine it with `uploadSkewMs`.

## Downloading files [#downloading-files]

Non-text responses (images, PDFs, zip files) resolve as a `Blob`:

```ts
const { data } = await api.get<Blob>("/reports/42.pdf");
const url = URL.createObjectURL(data!);
```

Pass `responseType: "blob"` or `"arrayBuffer"` to force a format.

Upload progress isn't available: `fetch` doesn't report it. Use `XMLHttpRequest` for a progress bar.
