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

1. **`upload`** — your backend picks the object key and returns a presigned URL (POST fields or PUT headers) and a `ticket`.
2. The browser uploads directly to S3.
3. **`confirm`** — the browser sends the `ticket`. Your backend verifies the object with `HeadObject` and returns what S3 reports.

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

## Presign [#presign]

The payload declares the file as `name`, `size`, and `type`. `checksum` is a
base64 SHA-256 of the file. The client sends it when the route's catalog entry
has `checksum: true`; sign it into the request so S3 checks it.

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

Return `fields` when `method` is `"POST"`, and `headers` when it is `"PUT"`.
The browser appends `fields` to the form before the file, or sends exactly
`headers` on the PUT.

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

## Confirm [#confirm]

`confirm` sends only the ticket from the presign response. Read the route and
key from it, never from the client.

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

Return the size and type from `HeadObject`, not the declared ones. If the
object is missing or does not match what was declared, throw an `APIError`
with a catalog code, such as `OBJECT_NOT_FOUND` or `UPLOAD_SIZE_MISMATCH`.

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

## Example [#example]

Paths are yours. [`@dimah-s3/server`](https://v2.s3.dimah.dev/docs/server) uses
`POST /api/s3/presign/upload` and `POST /api/s3/presign/upload/confirm`.

```ts title="lib/s3-api/upload.ts"
import type {
  TicketPayload,
  S3RequestOptions,
  UploadConfirmResponse,
  UploadPayload,
  UploadPresignResponse,
} from "@dimah-s3/core";
import { $fetch, call } from "./fetch";

export const upload = (payload: UploadPayload, options?: S3RequestOptions) =>
  $fetch<UploadPresignResponse>("/presign", {
    method: "POST",
    body: payload,
    ...call(options),
  });

export const confirm = (payload: TicketPayload, options?: S3RequestOptions) =>
  $fetch<UploadConfirmResponse>("/confirm", {
    method: "POST",
    body: payload,
    ...call(options),
  });
```

`$fetch` and `call` come from the
[skeleton](https://v2.s3.dimah.dev/docs/react/custom-backend#skeleton).
