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

# Upload file

> Upload a file to a workspace and push it to running computers.

Uploads a file to a workspace and pushes it to the computers that are running at that moment.

Passing `destPath` switches the endpoint to a second mode. That mode drops the file into one exact folder on one computer and records nothing. The two modes return different bodies. See [Folder-targeted upload](#folder-targeted-upload).

## Request

Send a `multipart/form-data` request.

<ParamField body="file" type="file" required>
  The file to upload. Maximum 10MB. A larger file returns 400. The name is preserved as `filename`. The copy in object storage is keyed under a sanitised form of it.
</ParamField>

<ParamField body="workspaceId" type="string" required>
  Workspace ID to upload into. Omitting it returns 400.
</ParamField>

<ParamField body="desktopId" type="string">
  Computer ID to associate the file with. When omitted, the file is recorded against the workspace with a `desktop_id` of `null`. Association does not change where the file is pushed. Every running computer in the workspace receives it either way. Required when `destPath` is set.
</ParamField>

<ParamField body="destPath" type="string">
  Absolute folder path on the computer, such as `/root/Documents` or, on Windows, `C:\Users\Public`. The literal value `desktop` means the desktop folder of the computer's user, which the computer resolves itself. When set, the file is placed at `<destPath>/<file name>` on the computer named by `desktopId`, and nothing else happens: no file record is written, no other computer receives it, and the response body is different. On Linux, a folder that does not exist is created. When omitted, the file is stored in the workspace and pushed to running computers.
</ParamField>

## Response

Without `destPath`, the whole stored row is returned under `file`.

<ResponseField name="file" type="object">
  The stored file record.
</ResponseField>

<ResponseField name="file.id" type="string">
  File ID. Pass it as `id` to the download and delete endpoints.
</ResponseField>

<ResponseField name="file.workspace_id" type="string">
  Workspace the file belongs to.
</ResponseField>

<ResponseField name="file.project_id" type="string">
  The same value as `workspace_id`, under the workspace's older name. Read `workspace_id` in new code.
</ResponseField>

<ResponseField name="file.desktop_id" type="string | null">
  The `desktopId` you supplied, or `null`.
</ResponseField>

<ResponseField name="file.user_id" type="string">
  ID of the account that uploaded the file.
</ResponseField>

<ResponseField name="file.filename" type="string">
  Original file name.
</ResponseField>

<ResponseField name="file.storage_key" type="string">
  Internal object-storage key. It is not a URL and cannot be fetched directly. Use the download endpoint to get a signed URL.
</ResponseField>

<ResponseField name="file.size_bytes" type="string">
  Size in bytes as a decimal string, such as `"102400"`. The value is a 64-bit integer, so it is serialized as a string.
</ResponseField>

<ResponseField name="file.content_type" type="string">
  MIME type reported by the client, or `application/octet-stream` when the client sent none.
</ResponseField>

<ResponseField name="file.created_at" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="file.source" type="string">
  Always `upload` for this endpoint.
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://www.orgo.ai/api/files/upload \
    -H "Authorization: Bearer $ORGO_API_KEY" \
    -F "file=@./document.pdf" \
    -F "workspaceId=$WORKSPACE_ID" \
    -F "desktopId=$COMPUTER_ID"
  ```

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

  with open("document.pdf", "rb") as f:
      response = requests.post(
          "https://www.orgo.ai/api/files/upload",
          headers={"Authorization": f"Bearer {os.environ['ORGO_API_KEY']}"},
          files={"file": f},
          data={
              "workspaceId": os.environ["WORKSPACE_ID"],
              "desktopId": os.environ["COMPUTER_ID"],  # optional
          },
      )

  file_info = response.json()["file"]
  print(f"Uploaded: {file_info['filename']} ({file_info['id']})")
  ```

  ```javascript JavaScript theme={null}
  import { openAsBlob } from 'node:fs';

  const formData = new FormData();
  formData.append('file', await openAsBlob('document.pdf'), 'document.pdf');
  formData.append('workspaceId', process.env.WORKSPACE_ID);
  formData.append('desktopId', process.env.COMPUTER_ID); // optional

  const response = await fetch('https://www.orgo.ai/api/files/upload', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.ORGO_API_KEY}` },
    body: formData,
  });

  const { file } = await response.json();
  console.log(`Uploaded: ${file.filename} (${file.id})`);
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "file": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "workspace_id": "550e8400-e29b-41d4-a716-446655440000",
    "project_id": "550e8400-e29b-41d4-a716-446655440000",
    "desktop_id": "a3bb189e-8bf9-3888-9912-ace4e6543002",
    "user_id": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
    "filename": "document.pdf",
    "storage_key": "550e8400-e29b-41d4-a716-446655440000/uploads/1705314600000-document.pdf",
    "size_bytes": "102400",
    "content_type": "application/pdf",
    "created_at": "2024-01-15T10:30:00Z",
    "source": "upload"
  }
}
```

## How the file reaches a computer

<Info>
  The file is stored in the workspace and pushed to the desktop folder (`/root/Desktop` on Linux) of every computer that is running at the time of upload. Computers that are stopped, or created afterwards, do not receive it. Upload again to reach them. The push is best-effort and runs in the background without being awaited, so it does not affect the status you get back.
</Info>

Each push sends the workspace's full set of uploaded files. A computer skips any file whose name already exists in that folder rather than overwriting it.

## Folder-targeted upload

Setting `destPath` places the file at one exact path on one computer. The response is a different shape and no file record is created. The file does not appear in the list endpoint, and it cannot be downloaded or deleted through this API.

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

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

```bash cURL theme={null}
curl -X POST https://www.orgo.ai/api/files/upload \
  -H "Authorization: Bearer $ORGO_API_KEY" \
  -F "file=@./document.pdf" \
  -F "workspaceId=$WORKSPACE_ID" \
  -F "desktopId=$COMPUTER_ID" \
  -F "destPath=/root/Documents"
```

```json theme={null}
{
  "ok": true,
  "placedIn": "/root/Documents"
}
```

## Errors

Every error carries an `error` message. The scope-mismatch `403` adds `code`, `key_workspace_id`, and `target_workspace_id`.

| Status | Body | Cause |
| - | - | - |
| `400` | `{"error": "No file provided"}` | The `file` part was absent. |
| `400` | `{"error": "workspaceId is required"}` | The `workspaceId` field was absent. |
| `400` | `{"error": "File too large (max 10MB)"}` | The file exceeded 10MB. |
| `400` | `{"error": "destPath requires desktopId"}` | `destPath` was set without `desktopId`. |
| `400` | `{"error": "Invalid destination folder"}` | `destPath` was neither `desktop` nor an absolute path (starting with `/`, or with a drive letter such as `C:\`), or it contained a null byte. |
| `400` | `{"error": "Invalid file name"}` | The file name resolved to nothing, `.`, or `..`. |
| `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. |
| `401` | `{"error": "Service temporarily unavailable. …"}` | Orgo could not verify the credential because of a server-side fault. Retry. |
| `403` | `{"error": "…", "code": "workspace_scope_mismatch", "key_workspace_id": "…", "target_workspace_id": "…"}` | A workspace-scoped key named a different workspace. |
| `403` | `{"error": "Access denied"}` | The caller neither owns the workspace nor has write access to it. A view-only member gets this too, and so does a workspace ID that does not exist. This endpoint never returns 404. |
| `500` | `{"error": "Failed to save file"}` | The upload reached storage but no file record came back. |
| `500` | `{"error": "Upload failed"}` | The upload failed. The message may instead carry the underlying error. |
| `502` | `{"error": "Could not place document.pdf in /root/Documents."}` | `destPath` mode only. The computer did not confirm it had written the file, or the request to it failed or ran past 130 seconds. |
| `503` | `{"error": "Computer could not be reached."}` | `destPath` mode only. The computer is not addressable: it has no connection details, or `desktopId` names no computer in this workspace. |


## OpenAPI

````yaml POST /files/upload
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: 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
paths:
  /files/upload:
    post:
      tags:
        - Files
      summary: Upload file
      description: >-
        Uploads a file of up to 10MB to a workspace and pushes it to
        `/root/Desktop` on every computer running at that moment. With
        `destPath`, it instead places the file in one folder on one computer and
        records nothing.
      operationId: uploadFile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
                - workspaceId
              properties:
                file:
                  type: string
                  format: binary
                  description: File to upload (max 10MB)
                desktopId:
                  type: string
                  description: >-
                    Computer to associate the file with. Omitted, the file
                    belongs to the workspace and is synced to every running
                    computer in it. Required when you send `destPath`.
                destPath:
                  type: string
                  description: >-
                    Folder on the computer to place the file in. Sending it
                    changes what the endpoint does: no file record is written,
                    nothing is synced, and the bytes are pulled straight into
                    that folder on the computer named by `desktopId`. It must be
                    an absolute path, starting with `/` or with a drive letter
                    such as `C:\` or `C:/` (for example `C:\Users\Public` on
                    Windows), or the literal `desktop`, which places the file in
                    the desktop folder of the computer's user. Anything else
                    returns `400`. Omitted, the file is stored against the
                    workspace and synced to its running computers.
                  example: /root/Desktop
                workspaceId:
                  type: string
                  description: Workspace ID. Required; omitting it is a `400`.
      responses:
        '200':
          description: >-
            File uploaded. `destPath` requests answer with the placement result
            instead of a file record.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/FileUploadResponse'
                  - $ref: '#/components/schemas/FilePlacedResponse'
              examples:
                stored:
                  summary: Stored against the workspace
                  value:
                    file:
                      id: f47ac10b-58cc-4372-a567-0e02b2c3d479
                      workspace_id: 550e8400-e29b-41d4-a716-446655440000
                      project_id: 550e8400-e29b-41d4-a716-446655440000
                      desktop_id: a3bb189e-8bf9-3888-9912-ace4e6543002
                      user_id: 9f8e7d6c-5b4a-3210-fedc-ba9876543210
                      filename: document.pdf
                      storage_key: >-
                        550e8400-e29b-41d4-a716-446655440000/uploads/1705314600000-document.pdf
                      size_bytes: '102400'
                      content_type: application/pdf
                      created_at: '2024-01-15T10:30:00Z'
                      source: upload
                placed:
                  summary: Placed on the computer via destPath
                  value:
                    ok: true
                    placedIn: /root/Documents
        '400':
          description: >-
            The file part or `workspaceId` is missing, the file is over 10MB,
            `destPath` was sent without `desktopId`, `destPath` is neither
            `desktop` nor an absolute path (or contains a null byte), or the
            file name resolved to nothing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                no-file:
                  summary: No file
                  value:
                    error: No file provided
                no-workspace:
                  summary: No workspace
                  value:
                    error: workspaceId is required
                too-large:
                  summary: Over 10MB
                  value:
                    error: File too large (max 10MB)
                dest-needs-desktop:
                  summary: destPath without desktopId
                  value:
                    error: destPath requires desktopId
                bad-dest:
                  summary: Bad destination
                  value:
                    error: Invalid destination folder
                bad-name:
                  summary: Bad file name
                  value:
                    error: Invalid file name
        '401':
          description: >-
            No usable credential. A server-side fault while verifying the
            credential also returns `401`, with a `Service temporarily
            unavailable` message; retry that one.
          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
                service-unavailable:
                  summary: Server-side fault while verifying the credential. Retry.
                  value:
                    error: >-
                      Service temporarily unavailable. The database is not
                      accepting requests. Retry shortly.
        '403':
          description: >-
            A workspace-scoped key named a different workspace, or you neither
            own the workspace nor have write access to it. A view-only member
            and a workspace ID that does not exist both get `Access denied`.
            This endpoint never returns `404`.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/ScopeError'
                  - $ref: '#/components/schemas/Error'
              examples:
                scope-mismatch:
                  summary: Key scoped to another workspace
                  value:
                    error: >-
                      This API key is scoped to workspace
                      550e8400-e29b-41d4-a716-446655440000. It cannot access
                      workspace 7c9e6679-7425-40de-944b-e07fc1f90ae7. Use an
                      account-wide key, or create a key scoped to the target
                      workspace.
                    code: workspace_scope_mismatch
                    key_workspace_id: 550e8400-e29b-41d4-a716-446655440000
                    target_workspace_id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
                access-denied:
                  summary: No write access
                  value:
                    error: Access denied
        '500':
          description: >-
            The upload failed, or it reached storage but no file record came
            back. The message may instead carry the underlying error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                save:
                  summary: No record
                  value:
                    error: Failed to save file
                upload:
                  summary: Upload failed
                  value:
                    error: Upload failed
        '502':
          description: >-
            `destPath` mode only. The computer did not confirm it had written
            the file, or the request to it failed or ran past 130 seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Could not place document.pdf in /root/Documents.
        '503':
          description: >-
            `destPath` mode only. The computer is not addressable, or
            `desktopId` names no computer in this workspace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Computer could not be reached.
components:
  schemas:
    FileUploadResponse:
      type: object
      properties:
        file:
          description: >-
            The stored file record: every column of the row, plus
            `workspace_id`.
          allOf:
            - $ref: '#/components/schemas/File'
            - type: object
              properties:
                workspace_id:
                  type: string
                  description: Workspace the file belongs to.
                project_id:
                  type: string
                  description: >-
                    The same value as `workspace_id`, under the workspace's
                    older name. Read `workspace_id` in new code.
                user_id:
                  type: string
                  description: ID of the account that uploaded the file.
                storage_key:
                  type: string
                  description: >-
                    Internal object-storage key. It is not a URL and cannot be
                    fetched directly. Use the download endpoint to get a signed
                    URL.
    FilePlacedResponse:
      type: object
      description: >-
        Returned instead of `FileUploadResponse` when the request carried
        `destPath`. No file record is created.
      properties:
        ok:
          type: boolean
          example: true
        placedIn:
          type: string
          description: >-
            The folder the file was placed in: your `destPath` with surrounding
            whitespace trimmed, or `the desktop` when you sent `desktop`. The
            file name is not appended.
          example: /root/Documents
    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.
    ScopeError:
      type: object
      description: >-
        A workspace-scoped API key was used against a workspace it is not scoped
        to. Use an account-wide key, or a key scoped to the workspace you are
        addressing.
      required:
        - error
        - code
        - key_workspace_id
        - target_workspace_id
      properties:
        error:
          type: string
        code:
          type: string
          enum:
            - workspace_scope_mismatch
        key_workspace_id:
          type: string
          description: The workspace the key is scoped to.
        target_workspace_id:
          type: string
          description: The workspace the request tried to reach.
    File:
      type: object
      properties:
        id:
          type: string
          description: File ID
        desktop_id:
          type:
            - string
            - 'null'
          description: >-
            Computer the file is associated with, or `null` when it is not tied
            to one.
        filename:
          type: string
          description: Original filename
        size_bytes:
          type:
            - string
            - 'null'
          description: >-
            File size in bytes, as a decimal string such as `"102400"`. The
            value is a 64-bit integer, so it is serialized as a string. Parse it
            before doing arithmetic. `null` when no size was recorded.
          example: '102400'
        content_type:
          type:
            - string
            - 'null'
          description: MIME type
        source:
          type: string
          enum:
            - upload
            - export
          description: How the file entered the workspace.
        created_at:
          type: string
          format: date-time
  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.