> ## 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.

# Start large upload

> Get a signed URL to upload a file of up to 2 GB to a computer.

Step one of the two-step upload, for files up to 2 GB. It returns a signed URL and an `upload_token`. PUT the file's bytes to the URL, then call [Complete large upload](/api-reference/files/complete-upload) to have the computer pull the file into place. The bytes go straight to storage and never pass through Orgo's API.

The file lands on one computer only. No file record is written, so it does not appear in the [list endpoint](/api-reference/files/list). For files up to 10 MB that should be stored in the workspace, use [Upload file](/api-reference/files/upload).

## Request

Send a JSON body.

<ParamField body="desktopId" type="string" required>
  Computer ID (UUID) the file goes to. The computer does not need to be running yet. It must be running when you complete the upload.
</ParamField>

<ParamField body="filename" type="string" required>
  Name the file will have on the computer. A plain name only: no `/` or `\`, not `.` or `..`, at most 255 characters.
</ParamField>

<ParamField body="size_bytes" type="integer" required>
  Exact size of the file in bytes, from 1 to 2147483648 (2 GB). The signed URL accepts a body of exactly this size and no other.
</ParamField>

<ParamField body="destPath" type="string">
  Absolute folder on the computer, such as `/root/Documents` or, on Windows, `C:\Users\Public`. Omit it, or send `desktop`, for the desktop folder of the computer's user. On Linux, a folder that does not exist is created.
</ParamField>

## Response

<ResponseField name="upload_url" type="string">
  Signed URL to PUT the file's bytes to. Send exactly `size_bytes` bytes.
</ResponseField>

<ResponseField name="method" type="string">
  Always `PUT`.
</ResponseField>

<ResponseField name="expires_in" type="integer">
  Seconds the `upload_url` stays valid: `900` (15 minutes).
</ResponseField>

<ResponseField name="upload_token" type="string">
  Pass it to [Complete large upload](/api-reference/files/complete-upload). It is valid for one hour and only for the same caller and computer.
</ResponseField>

<ResponseField name="next" type="string">
  A one-line reminder of the next two steps.
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={null}
  SIZE=$(wc -c < ./dataset.zip | tr -d ' ')

  START=$(curl -s -X POST https://www.orgo.ai/api/files/uploads \
    -H "Authorization: Bearer $ORGO_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"desktopId\": \"$COMPUTER_ID\", \"filename\": \"dataset.zip\", \"size_bytes\": $SIZE, \"destPath\": \"/root/Documents\"}")

  curl -T ./dataset.zip "$(echo "$START" | jq -r .upload_url)"

  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\": \"$(echo "$START" | jq -r .upload_token)\"}"
  ```

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

  headers = {"Authorization": f"Bearer {os.environ['ORGO_API_KEY']}"}
  computer_id = os.environ["COMPUTER_ID"]

  start = requests.post(
      "https://www.orgo.ai/api/files/uploads",
      headers=headers,
      json={
          "desktopId": computer_id,
          "filename": "dataset.zip",
          "size_bytes": os.path.getsize("dataset.zip"),
          "destPath": "/root/Documents",
      },
  ).json()

  with open("dataset.zip", "rb") as f:
      requests.put(start["upload_url"], data=f).raise_for_status()

  done = requests.post(
      "https://www.orgo.ai/api/files/uploads/complete",
      headers=headers,
      json={"desktopId": computer_id, "upload_token": start["upload_token"]},
  )
  print(done.json())
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "upload_url": "https://objects.example.com/…",
  "method": "PUT",
  "expires_in": 900,
  "upload_token": "eyJ1Ijoi…",
  "next": "PUT exactly 52428800 bytes to upload_url (for example: curl -T <file> '<upload_url>'), then call POST /api/files/uploads/complete with desktopId and upload_token."
}
```

## 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": "desktopId is required"}` | `desktopId` was absent or empty. |
| `400` | `{"error": "filename must be a plain file name"}` | `filename` was absent, empty, too long, `.` or `..`, or contained a slash, a backslash or a null byte. |
| `400` | `{"error": "size_bytes must be the file size in bytes"}` | `size_bytes` was absent or not a positive integer. |
| `413` | `{"error": "File is 3221225472 bytes; the limit is 2147483648. Split or compress it first."}` | `size_bytes` is above 2 GB. |
| `400` | `{"error": "destPath must be an absolute folder on the computer"}` | `destPath` is not absolute, contains a null byte, or is longer than 4096 characters. Also returned when the path's form does not match the computer's OS. |
| `404` | `{"error": "Desktop not found"}` | No computer has that ID. A `desktopId` that is not a UUID returns `500` instead. |
| `403` | `{"error": "Access denied"}` | The caller neither owns the computer's workspace nor belongs to it, 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. |
| `500` | `{"error": "Could not start the upload"}` | Unexpected failure. |


## OpenAPI

````yaml POST /files/uploads
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:
    post:
      tags:
        - Files
      summary: Start file upload
      description: >-
        Step 1 of putting a file of up to 2 GB on a computer: returns a signed
        `upload_url` and an `upload_token`. PUT exactly `size_bytes` bytes to
        `upload_url` (for example `curl -T file '<upload_url>'`), then call
        `POST /files/uploads/complete`. The bytes go straight to storage.
      operationId: startFileUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - desktopId
                - filename
                - size_bytes
              properties:
                desktopId:
                  type: string
                  description: Computer ID.
                filename:
                  type: string
                  description: A plain file name, no folders.
                size_bytes:
                  type: integer
                  minimum: 1
                  maximum: 2147483648
                  description: The file's exact size in bytes. Up to 2 GB.
                destPath:
                  type: string
                  description: >-
                    Absolute folder on the computer to place it in. Omitted or
                    `desktop`: the desktop folder of the computer's user.
            example:
              desktopId: a3bb189e-8bf9-3888-9912-ace4e6543002
              filename: data.csv
              size_bytes: 52428800
              destPath: /root/Desktop
      responses:
        '200':
          description: Where to send the bytes, and the token to finish with.
          content:
            application/json:
              schema:
                type: object
                properties:
                  upload_url:
                    type: string
                    description: Signed URL to PUT the bytes to. Works for 15 minutes.
                  method:
                    type: string
                    enum:
                      - PUT
                  expires_in:
                    type: integer
                    description: Seconds `upload_url` works (900).
                  upload_token:
                    type: string
                    description: >-
                      Send it to `POST /files/uploads/complete`. Works for one
                      hour.
                  next:
                    type: string
                    description: What to do next, in words.
              example:
                upload_url: https://objects.example.com/…
                method: PUT
                expires_in: 900
                upload_token: eyJ1Ijoi…
                next: >-
                  PUT exactly 52428800 bytes to upload_url (for example: curl -T
                  <file> '<upload_url>'), then call POST
                  /api/files/uploads/complete with desktopId and upload_token.
        '400':
          description: >-
            `desktopId` missing, `filename` not a plain file name, `size_bytes`
            not a positive integer, or `destPath` not an absolute folder.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: size_bytes must be the file size in bytes
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/TrialInactive'
        '403':
          description: View-only access, or the key is scoped to another workspace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Access denied
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          description: The file is over 2 GB. Split or compress it first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: >-
                  File is 3221225472 bytes; the limit is 2147483648. Split or
                  compress it first.
        '500':
          description: The upload could not be started.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Could not start the upload
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.