# Upload flow

> Three-step upload protocol, multipart transfers, status polling, and signed download links.

Uploading and downloading objects through the REST API follows a structured protocol that verifies the cryptographic integrity of your data.

## Upload architecture

The upload lifecycle consists of three sequential phases:

1. Step 1: the client requests an upload ticket via `POST /files/upload-ticket`, supplying metadata and the SHA-256 hash.
2. Step 2: binary payloads are uploaded using HTTP PUT to the granted capabilities. Large files transfer across individual part links.
3. Step 3: the client confirms transfer completion using `POST /files/complete`, then polls `GET /files/{file_id}` until `status` becomes `ready`.

## Step 1: Request upload ticket (POST /files/upload-ticket)

This request reserves quota and yields authorized transfer links:

```http
POST /files/upload-ticket HTTP/1.1
Host: api.mmwcloud.tech
Authorization: Bearer mmwc_<prefix>_<secret>
Content-Type: application/json

{
  "name": "backup-archive.tar.gz",
  "size_bytes": 15728640,
  "sha256": "4b68e210ac124b868e826b5278c5208fe3b0c44298fc1c149afbf4c8996fb924",
  "kind": "tar.gz"
}
```

Required scope: `files:write`.

### Request payload parameters

- `name` (required): file name string, containing no carriage returns or null bytes (`\r`, `\n`, `\0`).
- `size_bytes` (required): exact file length in bytes.
- `sha256` (required): full file SHA-256 hexadecimal digest string.
- `kind` (optional): file extension or MIME label string (up to 64 characters).
- `is_media` (optional): boolean media classification flag.
- `taken_at` (optional): media timestamp string (up to 64 characters).
- `agent_sync_enabled`: cannot be set via Developer API (setting this flag returns HTTP 400).

### Response for small files (under 8 MiB)

The backend provides a single direct `upload_url`:

```json
{
  "id": "file_01h8upload123",
  "upload_url": "https://storage.mmwcloud.tech/v1/upload/file_01h8upload123?capability=token123"
}
```

### Response for multipart files (8 MiB and above)

The backend provides an array of upload chunks under `parts`:

```json
{
  "id": "file_01h8upload456",
  "parts": [
    {
      "part_number": 1,
      "upload_url": "https://storage.mmwcloud.tech/v1/upload-part/file_01h8upload456/1?capability=tokenPart1"
    },
    {
      "part_number": 2,
      "upload_url": "https://storage.mmwcloud.tech/v1/upload-part/file_01h8upload456/2?capability=tokenPart2"
    }
  ]
}
```

## Step 2: Binary transmission

- For small files: issue an HTTP PUT with `Content-Type: application/octet-stream` delivering the binary payload to `upload_url`.
- For multipart files: issue an HTTP PUT to each respective chunk URL in `parts`. The JSON response for each part provides an `etag` token. Retain the `etag` value for each `part_number`.

## Step 3: Complete upload (POST /files/complete)

Once all bytes are transferred, commit the upload:

```http
POST /files/complete HTTP/1.1
Host: api.mmwcloud.tech
Authorization: Bearer mmwc_<prefix>_<secret>
Content-Type: application/json

{
  "id": "file_01h8upload456",
  "parts": [
    { "part_number": 1, "etag": "\"etag-part-1\"" },
    { "part_number": 2, "etag": "\"etag-part-2\"" }
  ]
}
```

Required scope: `files:write`.

For single-part files, the `parts` array may be empty. The endpoint returns HTTP 202 Accepted.

## Polling for ready status

Following completion, the storage backend performs assembly and checksum validation. Poll `GET /files/{file_id}` periodically until the `status` field equals `ready`.

## Downloading files

Downloading an object is completed in two stages:

1. Obtain a download link: call `GET /files/{file_id}/download` with scope `files:read`. The server responds with an expiring `download_url`:

```json
{
  "download_url": "https://storage.mmwcloud.tech/v1/download/file_01h8upload456?capability=tokenDownload"
}
```

2. Download payload: perform a standard GET request to `download_url`. Signed download links expire automatically after a short duration.
