# Complete File Upload

> Part of the [SpatialGen API documentation](/index.md). [HTML version of this page](/api/direct-upload/complete/).

Finalize one uploaded file inside a direct upload group.

## API context

-   API requests use `https://spatialgen.com` with the `/api` path prefix, included once.
-   Authenticate requests to SpatialGen API endpoints with the `X-SPATIALGEN-APPKEY` HTTP header.
-   Use a key from the workspace that owns the requested resources. Follow the roles and scopes specified for each endpoint below.
-   Keep `admin` and `upload` keys on a trusted backend. `readonly` keys may be used in client apps for permitted read operations.
-   Presigned upload URLs have their own authorization. Do not send your SpatialGen API key to those URLs.

See the [API key guide](/guides/get-started.md) for roles, key creation, expiration, revocation, and rotation.

* * *

**POST** `/api/videos/uploads/{uploadSessionId}/files/{uploadId}/complete`

Finalize one uploaded file. Once every file in the upload group reaches `completed`, SpatialGen will automatically create the downstream library record and processing job for the full video asset set.

Allowed API Key Roles

`admin`, `upload`

## Route Parameters

-   `uploadSessionId`: string UUID for the upload group
-   `uploadId`: string UUID for the file within that group

## Request Body

-   `parts`: array, optional for `single_put`, required for `multipart`

Each multipart `parts[]` item includes:

-   `partNumber`: positive integer
-   `etag`: string

## Behavior Notes

-   Multipart completion must include every part number from `1` through `totalParts`.
-   Single-part uploads do not need to send a `parts` array.
-   Repeating `complete` on an already completed file is safe and will return the same file state.
-   When the last file in the group completes, SpatialGen creates one library record and queues one processing job automatically.
-   `libraryId` stays `null` until the upload group is fully completed. The final successful `complete` call returns the new library ID.

## Response Shape

-   `uploadId`: string
-   `fileRole`: string
-   `fileName`: string
-   `totalBytes`: number
-   `partSizeBytes`: number
-   `totalParts`: number
-   `uploadStrategy`: `"single_put" | "multipart"`
-   `status`: string
-   `libraryId`: `number | null`

## Example Request

complete-upload.js

```js
fetch('https://spatialgen.com/api/videos/uploads/5ea97a59-95e0-4c8b-a634-b0fa9976f6ff/files/402f0bda-a0fa-4551-ae28-7300ab6678dd/complete', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-SPATIALGEN-APPKEY': apiKey,
  },
  body: JSON.stringify({
    parts: [
      { partNumber: 1, etag: '"etag-part-1"' },
      { partNumber: 2, etag: '"etag-part-2"' },
    ],
  }),
})
```

## Example Response

complete-response.json

```json
{
  "uploadId": "402f0bda-a0fa-4551-ae28-7300ab6678dd",
  "fileRole": "video_left",
  "fileName": "left-eye.mov",
  "totalBytes": 1287340032,
  "partSizeBytes": 104857600,
  "totalParts": 13,
  "uploadStrategy": "multipart",
  "status": "completed",
  "libraryId": 4821
}
```
