# Complete upload

Verify the uploaded bytes and create one file. Send only the upload ID; Brigg collects multipart receipts automatically. The same upload always produces the same file ID. If scanning is pending, retry this call with the same idempotency key after the retry delay. Blocked files cannot be completed.

POST https://api.brigg.app/v1/operations/documents.CompleteUpload

## Access & visibility

Allow `documents.CompleteUpload` on your API key.

**Workspace access by default.** Private member data requires [approved member delegation](https://developer.brigg.app/authentication#member-delegation) through `Brigg-Act-As`; the key supplies permissions while private ownership and project boundaries remain enforced.

[Set up authentication](https://developer.brigg.app/authentication) · [Choose permissions](https://developer.brigg.app/permissions)

## Request fields

Send a JSON object with the following fields. Required fields must be included; omit optional fields you do not need.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `upload_id` | string | Yes | Keep the upload_id and the same credential and acting member throughout the upload. Completion returns a separate file_id. [documents.CreateUpload](https://developer.brigg.app/reference/documents/CreateUpload). At least 1 character. |
| `parts` | array of [object](#request-parts) | No | Optional receipts for uploaded parts. Completion can retrieve receipts when this list is omitted. |

### request.parts[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `part_number` | integer | Yes | One-based part number. Use 1 for a single upload; for multipart uploads, transfer every part from 1 through part_count. Greater than 0. |
| `etag` | string | Yes | Optional explicit multipart receipt. When supplying parts, preserve the complete ETag value including quotes. Otherwise omit the parts array and Brigg collects the receipts automatically. At least 1 character. |

## Where this fits in an upload

The [file guide](https://developer.brigg.app/files) includes a streaming Node.js helper and the complete HTTP sequence: create an upload, request a signed URL for each part, PUT the bytes directly to that URL, then complete the upload. Never send the Brigg API key to a transfer URL.

Keep the same credential and acting member for every step. Follow the returned part size and count rather than imposing your own split. Completing with only `upload_id` lets Brigg collect part receipts. A successful byte transfer is not a completed file: verification and scanning must finish before you receive a usable `file_id`.

Retain `upload_id` for recovery. Read its state before retransferring an interrupted upload; `ready` returns the existing file ID, while an expired upload requires a new start. Temporary transfer URLs and completed file IDs have different lifetimes.

## Response

Returns **200 OK** with a JSON body.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file_id` | string | Yes | Stable file ID returned by documents.CompleteUpload, documents.ListDocuments or a document creation operation. Use the same ID for downloads, attachments and supported file-based workflows. Do not use a filename or storage path. |
| `filename` | string | Yes | Original filename, including its extension. |
| `content_type` | string or null | Yes | Media type of the file, such as application/pdf. Null when no binary file exists. |
| `size_bytes` | number or null | Yes | File size in bytes. Null for a document without a downloadable binary file. |
| `visibility` | string | Yes | Who may read the completed file. Personal files belong to the approved acting member; workspace files use shared access. Allowed: `"personal"`, `"workspace"`. |
| `scan_status` | string or null | Yes | Malware scan state. Threats and failed scans block downloads. An unsupported file cannot be processed; only its original uploader can retrieve it as a forced download. Null is not proof of readiness. |

## Errors & retries

Set `BRIGG_REQUEST_ID` once for each logical action. The samples send it as `Idempotency-Key`. Reuse the same value and body on retries, including after a timeout; a missing response does not prove the action failed.

For rate limits, follow the server’s retry guidance and back off. See [HTTP errors](https://developer.brigg.app/http#errors) and [safe retries and limits](https://developer.brigg.app/reliability).

| HTTP status | Code | What to do |
| --- | --- | --- |
| 400 | `invalid_json` | Send a JSON object with Content-Type: application/json. |
| 400 | `invalid_input` | Check the request fields and types. Supply a stable Idempotency-Key containing 1–200 characters. |
| 401 | `invalid_key` | The workspace API key is missing, invalid or revoked. |
| 403 | `forbidden` | Check the key’s operation and resource grants. If Brigg-Act-As is supplied, the key must be authorized to delegate to that member. |
| 404 | `not_found` | The requested operation or resource is not available to this credential. |
| 429 | `rate_limited` | Wait for Retry-After before retrying. Reuse the original Idempotency-Key. |
| 409 | `upload_incomplete` | Transfer all parts with their exact planned byte lengths before completing the upload. |
| 409 | `file_scan_pending` | Wait Retry-After seconds, then retry with the same input and idempotency key. |
| 409 | `file_scan_blocked` | The file cannot be used because its malware scan was blocked. |
| 409 | `upload_closed` | This upload was cancelled or is no longer open. |
| 410 | `upload_expired` | Start a new upload with a new idempotency key. |

## Next steps

- [Create upload](https://developer.brigg.app/reference/documents/CreateUpload)
- [Get upload part](https://developer.brigg.app/reference/documents/GetUploadPart)
- [Get upload](https://developer.brigg.app/reference/documents/GetUpload)
- [Upload and download files](https://developer.brigg.app/files)

[Download OpenAPI](https://developer.brigg.app/openapi.json) · [Versioning & retirement](https://developer.brigg.app/versioning)

## Examples

### Example request

Fictional values. Replace IDs and business details with values from your workspace.
#### cURL


```sh
curl 'https://api.brigg.app/v1/operations/documents.CompleteUpload' \
  -H "Authorization: Bearer $BRIGG_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $BRIGG_REQUEST_ID" \
  --data-raw '{
  "upload_id": "upl_example"
}'
```

#### TypeScript


```typescript
const requestId = process.env.BRIGG_REQUEST_ID;
if (!requestId) throw new Error("Set BRIGG_REQUEST_ID once per action and reuse it on retries.");

const response = await fetch("https://api.brigg.app/v1/operations/documents.CompleteUpload", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BRIGG_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": requestId, // Create once per action; reuse on retries.
  },
  body: JSON.stringify({
    "upload_id": "upl_example"
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error} — ${result.message}`);
console.log(result);
```

#### Python


```python
import json
import os
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request_id = os.environ["BRIGG_REQUEST_ID"]  # Reuse for retries of this action.
payload = {
  "upload_id": "upl_example"
}
request = Request(
    "https://api.brigg.app/v1/operations/documents.CompleteUpload",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['BRIGG_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": request_id,  # Create once per action; reuse on retries.
    },
    method="POST",
)
try:
    with urlopen(request) as response:
        print(json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))
    raise
```

#### Response example


```json
{
  "file_id": "f_example",
  "filename": "Agreement.pdf",
  "content_type": "application/pdf",
  "size_bytes": 48231,
  "visibility": "workspace",
  "scan_status": "clean"
}
```

## JSON schemas


```json-schema-request
{
  "type": "object",
  "properties": {
    "upload_id": {
      "type": "string",
      "minLength": 1,
      "description": "Keep the upload_id and the same credential and acting member throughout the upload. Completion returns a separate file_id. [documents.CreateUpload](https://developer.brigg.app/reference/documents/CreateUpload)."
    },
    "parts": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "part_number": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "One-based part number. Use 1 for a single upload; for multipart uploads, transfer every part from 1 through part_count."
          },
          "etag": {
            "type": "string",
            "minLength": 1,
            "description": "Optional explicit multipart receipt. When supplying parts, preserve the complete ETag value including quotes. Otherwise omit the parts array and Brigg collects the receipts automatically."
          }
        },
        "required": [
          "part_number",
          "etag"
        ],
        "additionalProperties": false
      },
      "description": "Optional receipts for uploaded parts. Completion can retrieve receipts when this list is omitted."
    }
  },
  "required": [
    "upload_id"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "upload_id": "upl_example"
    }
  ]
}
```


```json-schema-response
{
  "type": "object",
  "properties": {
    "file_id": {
      "type": "string",
      "description": "Stable file ID returned by documents.CompleteUpload, documents.ListDocuments or a document creation operation. Use the same ID for downloads, attachments and supported file-based workflows. Do not use a filename or storage path."
    },
    "filename": {
      "type": "string",
      "description": "Original filename, including its extension."
    },
    "content_type": {
      "type": [
        "string",
        "null"
      ],
      "description": "Media type of the file, such as application/pdf. Null when no binary file exists."
    },
    "size_bytes": {
      "type": [
        "number",
        "null"
      ],
      "description": "File size in bytes. Null for a document without a downloadable binary file."
    },
    "visibility": {
      "type": "string",
      "enum": [
        "personal",
        "workspace"
      ],
      "description": "Who may read the completed file. Personal files belong to the approved acting member; workspace files use shared access."
    },
    "scan_status": {
      "type": [
        "string",
        "null"
      ],
      "description": "Malware scan state. Threats and failed scans block downloads. An unsupported file cannot be processed; only its original uploader can retrieve it as a forced download. Null is not proof of readiness."
    }
  },
  "required": [
    "file_id",
    "filename",
    "content_type",
    "size_bytes",
    "visibility",
    "scan_status"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "file_id": "f_example",
      "filename": "Agreement.pdf",
      "content_type": "application/pdf",
      "size_bytes": 48231,
      "visibility": "workspace",
      "scan_status": "clean"
    }
  ]
}
```

