# Upload a chunk

> Send one 16 MiB part of the file.

`PUT https://api.codebuckets.clatterlabs.xyz/v1/uploads/{upload_id}/chunks/{index}` (key permission: `buckets:write`)

Stores one chunk, encrypted. Chunk `index` covers bytes `index × chunk_size` up to the next chunk; every chunk is exactly `chunk_size` bytes except the last. Send chunks in any order and in parallel (up to 4 at a time works well). Sending a chunk again replaces it, so retries are safe. Chunk requests don't count toward the key's per-minute limit.

## Authorizations

- `Authorization` (string, required): Your API key as a bearer token: `Bearer clsk_...`. Create keys in the [Console](https://console.clatterlabs.com/api-keys) with the Code Buckets permissions you need.
- `X-Clatter-Account` (string, required): The Account ID that owns the key, like `CL-7K2M9QXA`. Find it in Console › Settings. A missing value answers `401 missing_account_id`; someone else's answers `401 account_mismatch`.

## Path parameters

- `upload_id` (string, required): The upload id (`cbo_…`) from [Start an upload](/api-reference/code-buckets/uploads/create).
- `index` (integer, required): 0 to `chunk_count - 1`.

## Response

- `object` (string): `upload.chunk`.
- `upload_id` (string): The upload.
- `index` (integer): The chunk index.
- `size` (integer): Bytes stored.
- `sha256` (string): SHA-256 of the chunk (hex).
- `duplicate` (boolean): `true` when the same chunk was already stored.

## Example request

```bash
curl -X PUT "https://api.codebuckets.clatterlabs.xyz/v1/uploads/cbo_m2n8b4v6c1x9z3l7k5j0/chunks/0" \
  -H "Authorization: Bearer $CLATTER_API_KEY" \
  -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @part-00000
```

## Errors

| Status | Code | When |
| --- | --- | --- |
| `400` | `bad_chunk / bad_chunk_size / checksum_mismatch` | Wrong index, wrong length, or the checksum didn't match. |
| `401` | `invalid_api_key / missing_account_id / account_mismatch` | The key or Account ID is wrong. |
| `402` | `quota_exceeded` | This month's free tokens are used up. |
| `403` | `insufficient_scope` | The key lacks the permission. |
| `404` | `not_found` | No upload with that id on your account. |
| `409` | `upload_closed` | The upload is already complete. |
| `502` | `storage_error` | Retry the chunk. |

## Example response

```json
{
  "object": "upload.chunk",
  "upload_id": "cbo_m2n8b4v6c1x9z3l7k5j0",
  "index": 0,
  "size": 16777216,
  "sha256": "4d9b0f3c…"
}
```

<Note>
**Request body:** The raw chunk bytes (`Content-Type: application/octet-stream`). Optional header `X-Content-SHA256`: the chunk's SHA-256 in hex; a mismatch answers `400 checksum_mismatch`.
</Note>

<Tip>
Split a file with `split -b 16777216 -d -a 5 storefront.zip part-` and send `part-00000`, `part-00001`, … as chunks 0, 1, …. The [Uploads guide](/code-buckets/uploads) has a full script in each language.
</Tip>
