> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orgo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Complete large upload

> Place a file uploaded with Start large upload on the computer.

Step two of the two-step upload. Call it after the file's bytes are PUT to the `upload_url` from [Start large upload](/api-reference/files/start-upload). The computer pulls the file into the folder you asked for, replacing a file of the same name, and the temporary copy in storage is deleted.

The computer must be running. If placing the file fails, the stored copy is kept, so you can call this again with the same `upload_token` until it expires (one hour after the upload started).

## Request

Send a JSON body.

<ParamField body="desktopId" type="string" required>
  The same computer ID you passed to Start large upload.
</ParamField>

<ParamField body="upload_token" type="string" required>
  The `upload_token` from Start large upload.
</ParamField>

## Response

<ResponseField name="ok" type="boolean">
  `true` when the file was placed on the computer.
</ResponseField>

<ResponseField name="filename" type="string">
  The file's name on the computer.
</ResponseField>

<ResponseField name="placedIn" type="string">
  The folder the file was placed in: your `destPath`, or `the desktop` when you sent none or `desktop`. The file name is not appended.
</ResponseField>

<ResponseField name="size_bytes" type="integer">
  The file's size in bytes.
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://www.orgo.ai/api/files/uploads/complete \
    -H "Authorization: Bearer $ORGO_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"desktopId\": \"$COMPUTER_ID\", \"upload_token\": \"$UPLOAD_TOKEN\"}"
  ```

  ```python Python theme={null}
  import os
  import requests

  response = requests.post(
      "https://www.orgo.ai/api/files/uploads/complete",
      headers={"Authorization": f"Bearer {os.environ['ORGO_API_KEY']}"},
      json={
          "desktopId": os.environ["COMPUTER_ID"],
          "upload_token": os.environ["UPLOAD_TOKEN"],
      },
  )
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://www.orgo.ai/api/files/uploads/complete', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.ORGO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      desktopId: process.env.COMPUTER_ID,
      upload_token: process.env.UPLOAD_TOKEN,
    }),
  });

  console.log(await response.json());
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "ok": true,
  "filename": "dataset.zip",
  "placedIn": "/root/Documents",
  "size_bytes": 52428800
}
```

## Errors

Every error carries the shape `{"error": "<message>"}`. The scope-mismatch `403` adds `code`, `key_workspace_id`, and `target_workspace_id`. The checks below run in order.

| Status | Body | Cause |
| - | - | - |
| `401` | `{"error": "Invalid API key"}` | The Bearer token starts with `sk_` but is not a known Orgo key. |
| `401` | `{"error": "Authentication required"}` | No `Authorization` header, or a Bearer token that is not an `sk_` key. |
| `400` | `{"error": "Invalid upload token. Start the upload again."}` | `upload_token` was absent, malformed or altered. |
| `400` | `{"error": "This upload expired. Start it again."}` | More than an hour passed since the upload started. |
| `403` | `{"error": "This upload belongs to another computer or user."}` | `desktopId` or the caller differs from the ones the upload started with. |
| `404` | `{"error": "Desktop not found"}` | No computer has that ID. |
| `403` | `{"error": "Access denied"}` | The caller no longer belongs to the computer's workspace, or has view-only access to it. |
| `402` | `{"error": "Choose a plan to continue."}` | The computer is a free trial computer that can no longer run. |
| `402` | `{"error": "Manage this computer’s payment in Account → Usage."}` | The computer is paid for by its own subscription or dedicated purchase, and that payment has lapsed. |
| `403` | `{"error": "…", "code": "workspace_scope_mismatch", "key_workspace_id": "…", "target_workspace_id": "…"}` | A workspace-scoped key named a computer in a different workspace. |
| `403` | `{"error": "This computer moved to another workspace. Start the upload again."}` | The computer moved to another workspace after the upload started. |
| `503` | `{"error": "File storage did not answer. Retry shortly."}` | Storage could not be checked. Retry with the same token. |
| `409` | `{"error": "Nothing uploaded yet. PUT the file to upload_url first. If you already finished, it is in place."}` | No bytes are stored for this upload: they were never PUT, or an earlier call already placed the file. |
| `409` | `{"error": "Upload is 1024 bytes, expected 52428800."}` | The stored size differs from `size_bytes`. |
| `400` | `{"error": "Desktop is not running. Start it, then finish the upload."}` | The computer's status is anything other than `running`. The stored copy is kept. |
| `503` | `{"error": "Computer not ready"}` | The computer has no connection details recorded, so it cannot be addressed. |
| `409` | `{"error": "A folder named dataset.zip already exists in /root/Documents. Start the upload again with another name."}` | A folder in the destination has the file's name. |
| `502` | `{"error": "Could not place dataset.zip in /root/Documents. Try again."}` | The computer could not be reached, or did not confirm it had written the file within about 90 seconds. The stored copy is kept, so retry with the same token. |
| `500` | `{"error": "Could not finish the upload"}` | Unexpected failure. |


## OpenAPI

````yaml POST /files/uploads/complete
openapi: 3.1.0
info:
  title: Orgo API
  description: >-
    Launch cloud computers that AI agents can control and interact with. Create
    workspaces, provision computers, and control them programmatically.
  version: 2.0.0
  contact:
    name: Orgo Support
    email: spencer@orgo.ai
    url: https://orgo.ai
servers:
  - url: https://www.orgo.ai/api
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Account
    description: >-
      Account capacity: how many computers an account may run, and adding or
      giving back more.
  - name: Agent accounts
    description: >-
      Accounts an AI agent makes for itself, and the claim link a person uses to
      take one over.
  - name: Clients
    description: >-
      Run Orgo for your clients from your own app: a workspace and scoped key
      each, billed to you or to them.
  - name: Workspaces
    description: Organize computers into named workspaces
  - name: Computers
    description: Provision and manage virtual computers
  - name: Computer Lifecycle
    description: Start, stop, and restart computers
  - name: Computer Actions
    description: Control mouse, keyboard, and execute commands
  - name: Screens
    description: >-
      More than one desktop on a single computer. Each screen is its own X
      server with its own cursor and window manager, so an agent working on one
      cannot disturb another.
  - name: Files
    description: Upload and download files
  - name: Templates
    description: Author, build, and launch reproducible computers from templates
  - name: Chat
    description: Run an agent on a computer through OpenAI-compatible chat completions.
paths:
  /files/uploads/complete:
    post:
      tags:
        - Files
      summary: Complete file upload
      description: >-
        Step 2: once the bytes are uploaded, the computer pulls the file into
        the folder you named. The computer must be `running`. A failed placement
        keeps the upload, so you can retry with the same token until it expires.
      operationId: completeFileUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - desktopId
                - upload_token
              properties:
                desktopId:
                  type: string
                  description: The same computer ID as step 1.
                upload_token:
                  type: string
                  description: The `upload_token` from `POST /files/uploads`.
            example:
              desktopId: a3bb189e-8bf9-3888-9912-ace4e6543002
              upload_token: eyJ1Ijoi…
      responses:
        '200':
          description: The file is in place.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  filename:
                    type: string
                  placedIn:
                    type: string
                    description: The folder it was placed in.
                  size_bytes:
                    type: integer
              example:
                ok: true
                filename: data.csv
                placedIn: /root/Desktop
                size_bytes: 52428800
        '400':
          description: >-
            The token is invalid or expired (start again), or the computer is
            not `running`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                expired:
                  summary: Token expired
                  value:
                    error: This upload expired. Start it again.
                not-running:
                  summary: Not running
                  value:
                    error: Desktop is not running. Start it, then finish the upload.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/TrialInactive'
        '403':
          description: >-
            The token is for another computer or user, the computer moved to
            another workspace, view-only access, or the key is scoped to another
            workspace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: This upload belongs to another computer or user.
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            Nothing was uploaded yet, the upload is a different size than
            declared, or a folder with that name is in the way.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: >-
                  Nothing uploaded yet. PUT the file to upload_url first. If you
                  already finished, it is in place.
        '500':
          description: The upload could not be finished.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Could not finish the upload
        '502':
          description: The computer could not place the file. Try again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Could not place data.csv in /root/Desktop. Try again.
        '503':
          description: Storage did not answer, or the computer is not ready. Retry shortly.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: File storage did not answer. Retry shortly.
components:
  schemas:
    Error:
      type: object
      description: >-
        The base error body. Every failure carries `error`; individual endpoints
        add the fields named in the schemas below.
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable message.
          example: Access denied
        code:
          type: string
          description: >-
            Machine-readable reason. Present on the failures that define one,
            absent otherwise.
  responses:
    Unauthorized:
      description: 'No usable credential. Send `Authorization: Bearer $ORGO_API_KEY`.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalid-key:
              summary: The key is not one of yours
              value:
                error: Invalid API key
            no-credential:
              summary: No key and no session
              value:
                error: Authentication required
    TrialInactive:
      description: >-
        The computer is a free trial computer whose trial is no longer active,
        or it is paid for by its own subscription or dedicated purchase and that
        payment has lapsed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            trial:
              summary: Trial no longer active
              value:
                error: Choose a plan to continue.
            payment:
              summary: The computer's own payment has lapsed
              value:
                error: Manage this computer’s payment in Account → Usage.
    NotFound:
      description: No computer with this id on your account.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Desktop not found
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key authentication. Get your key at orgo.ai/workspaces

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.