# Upload (https://s3.dimah.dev/docs/react/custom-backend/upload)



1. **`upload`** — your backend returns a presigned URL (POST fields or PUT headers).
2. The browser uploads directly to S3.
3. **`confirm`** — your backend verifies the object (typically `HeadObject`) and returns metadata.

Large files: [Multipart](https://s3.dimah.dev/docs/react/custom-backend/multipart).

<Callout>
  `upload` does not receive `key`. Generate it on the server and return it in
  the presign response. `confirm` sends that stored `key` plus the same `route`.
</Callout>

## Presign [#presign]

<AutoTypeTable path="packages/core/src/types/requests.ts" name="UploadPayload" />

Return **all** of these fields. `fields` when `method` is `"POST"`; `headers`
when `method` is `"PUT"`.

<AutoTypeTable path="packages/core/src/types/responses.ts" name="UploadPresignResponse" />

## Confirm [#confirm]

<AutoTypeTable path="packages/core/src/types/requests.ts" name="ConfirmPayload" />

`contentLength` and `metadata` are required. `eTag` and `contentType` come
from HeadObject when available.

<AutoTypeTable path="packages/core/src/types/responses.ts" name="UploadConfirmResponse" />

```ts
import type {
  UploadPayload,
  UploadPresignResponse,
  ConfirmPayload,
  UploadConfirmResponse,
} from "@dimah-s3/core";
```

## Example [#example]

Add these to the [`defineApi`](https://s3.dimah.dev/docs/react/custom-backend) skeleton. Paths
are yours — [`@dimah-s3/server`](https://s3.dimah.dev/docs/server) uses
`/api/s3/presign/upload` and `/api/s3/presign/upload/confirm`.

```ts
async function post<T>(url: string, body: unknown): Promise<T> {
  const res = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

const upload = (payload) => post("/api/files/presign/upload", payload);
const confirm = (payload) => post("/api/files/presign/upload/confirm", payload);
```
