# Upload Chunk

Send a file, or one piece of it, to an upload's upload URL, and read what the answer means.

> Kind: REST endpoint · Updated: Sep 25, 2026 · PUT {uploadUrl}

Sends the file, or one piece of it, to the upload URL. There is no API key: the URL is the credential. The PUT that delivers the last missing piece finishes the upload and answers with the file. For the whole flow, see Upload a File.

### Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| uploadUrl | string | required | The uploadUrl from Create Upload, used exactly as returned. |

### Header parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| Content-Length | integer | required | The size of the body in bytes. HTTP clients set it when the body is a file or a buffer. |
| Content-Range | string | optional | Only when sending in pieces: bytes {first}-{last}/{size}, for example bytes 52428800-104857599/120000000. A piece starts at a multiple of chunkSize and is chunkSize bytes long, except the last. Leave it out when the whole file is one piece. |
| Content-MD5 | string | optional | The piece's MD5, base64-encoded. AWS S3 and Azure Blob check it; SharePoint and Google Drive refuse it. |

### Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
| (body) | binary | required | The raw bytes of the file or the piece. Not a form and not base64. |

## Example request

**cURL**
```bash
# The second 50 MB piece of a 120,000,000-byte file, saved as piece2.bin
curl --request PUT "$UPLOAD_URL" \
  --header 'Content-Range: bytes 52428800-104857599/120000000' \
  --data-binary @piece2.bin
```

## Response body

| Status | When |
|---|---|
| 202 Accepted | The piece is stored; nextExpectedRanges lists what is still missing. An empty list means the file is being put together: GET the upload URL after a few seconds. |
| 201 Created | This piece finished the upload. The answer carries file. |
| 200 OK | The upload was already finished, for example when you resend the last piece after its answer was lost. Same file as the 201. |

A 201 or 200 looks like this. file.id works with every file endpoint, and file.fullName is the stored name.

```json
{
  "id": "upl_66f3c1a2b4d5e6f708192a3b",
  "status": "completed",
  "library": "s3",
  "driveId": "workspace",
  "name": "Q3-report.pdf",
  "size": 120000000,
  "chunkSize": 52428800,
  "chunkCount": 3,
  "maxConcurrency": 4,
  "bytesReceived": 120000000,
  "nextExpectedRanges": [],
  "expiresAt": "2026-09-25T11:05:09.000Z",
  "fileId": "66f3c1b9b4d5e6f708192a40",
  "file": {
    "id": "66f3c1b9b4d5e6f708192a40",
    "name": "Q3-report",
    "fullName": "Q3-report.pdf",
    "type": "file",
    "extension": "pdf",
    "source": "INTERNAL",
    "size": 120000000,
    "createdAt": "2026-09-25T10:05:12.000Z",
    "updatedAt": "2026-09-25T10:05:12.000Z",
    "createdBy": "Priya Shah",
    "driveId": "workspace",
    "library": "s3",
    "parent": {
      "id": "66f3c1a2b4d5e6f708192a32",
      "type": "folder",
      "driveId": "workspace"
    },
    "path": "Contracts/2026/Q3-report.pdf"
  }
}
```

## Errors

| Status | errorCode | What it means |
|---|---|---|
| 400 | UPLOAD/INVALID_RANGE | Content-Range or the body's length does not match the upload. message says how. |
| 404 | UPLOAD/NOT_FOUND | No upload at this URL. |
| 409 | UPLOAD/OUT_OF_ORDER | SharePoint and Google Drive take pieces in order. GET the upload URL and send the first missing piece. |
| 409 | UPLOAD/NOT_ACCEPTING_CHUNKS | The upload was cancelled or ended by an error. Create a new one. |
| 410 | UPLOAD/EXPIRED | The upload expired. Create a new one. |
| 411 | UPLOAD/LENGTH_REQUIRED | The request had no Content-Length. |
| 429 | UPLOAD/CHUNKS_IN_FLIGHT | Too many pieces at once. Send it again after Retry-After seconds. |
| 503 | UPLOAD/STORAGE_UNAVAILABLE | The storage was busy. Send it again after Retry-After seconds. |

Upload Errors lists every code and what to do.
