How-To Guides

Manage Files with the Kiteworks REST API

How to find files in Kiteworks, download them, and upload new ones. This guide covers two ways to locate a file — by listing a folder's contents or by searching on a known path — then walks through downloading and uploading.

In this guide

  1. List files in a folder — retrieve file objects and their IDs from a known folder
  2. Search for a file by path — look up a file ID when you know its path but not its ID
  3. Download a file — retrieve file content using a file ID from steps 1 or 2
  4. Upload a file — initiate a chunked upload and transfer file content to a folder
All requests require a valid Bearer token in the Authorization header. The Python samples import KWOAuthClient from a get_access_token module — see Authentication for the implementation of this helper. Every request must also include the X-Accellion-Version: 28 header.

1. List Files in a Folder

Returns all files contained within a specific folder. You'll need a folder_id to make this request — if you don't have one yet, see Manage Folders for how to retrieve folder IDs. The id field on each file object is what you'll pass to the download endpoint in step 3.

GET /rest/folders/{folder_id}/files

Path parameters

ParameterTypeDescription
folder_idstring (UUID)The UUID of the folder whose files you want to list.

Query parameters

All parameters are optional.

ParameterTypeDescription
namestringFilter by exact file name.
userIdstringFilter by the UUID of the file's creator.
orderBystringSort field and direction — e.g. name:asc. See Sorting.
offsetintegerNumber of records to skip. See Pagination.
limitintegerMaximum number of files to return.
list-files.sh
curl -X GET "$KW_INSTANCE_URL/rest/folders/uuid0001-0000-0000-0000-000000000001/files" \
  -H "X-Accellion-Version: 28" \
  -H "Authorization: Bearer $KW_ACCESS_TOKEN" \
  -H "Content-Type: application/json"
list_files.py
import requests
from get_access_token import KWOAuthClient

def list_files_in_folder(base_url, access_token, folder_id):
    """Return all files contained within the given folder."""
    url = f"{base_url}/rest/folders/{folder_id}/files"
    headers = {
        "X-Accellion-Version": "28",
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    }
    params = {
        # "name": "example.docx",
        # "userId": "uuid0002-...",
        # "orderBy": "name:asc",
        # "offset": 0,
        # "limit": 10,
    }
    response = requests.get(url, headers=headers, params=params)
    response.raise_for_status()
    return response.json().get("data", [])

def main():
    base_url = input("Enter Base URL for Kiteworks instance: ")
    client = KWOAuthClient(base_url)
    access_token = client.get_access_token()
    folder_id = input("Enter folder ID: ")
    files = list_files_in_folder(base_url, access_token, folder_id)
    print(files)

if __name__ == "__main__":
    main()

Example response

Returns 200 OK with an array of file objects. The id field on each object is the file UUID used by the download and other file endpoints.

200 OK
[
  {
    "id": "uuid0003-0000-0000-0000-000000000003",
    "name": "Example_Document.docx",
    "path": "GenericFolder/Example_Subfolder/Example_Document.docx",
    "type": "f",
    "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "size": 75395,
    "parentId": "uuid0001-0000-0000-0000-000000000001",
    "userId": "uuid0002-0000-0000-0000-000000000002",
    "locked": false,
    "secure": false,
    "isShared": false,
    "deleted": false,
    "avStatus": "allowed",
    "dlpStatus": "allowed",
    "adminQuarantineStatus": "allowed",
    "creator": {
      "id": "uuid0002-0000-0000-0000-000000000002",
      "name": "Generic User",
      "email": "user@example.com"
    },
    "created": "2025-01-01T00:00:00+0000",
    "modified": "2025-01-01T00:00:00+0000",
    "permalink": "https://content.kiteworks.com/w/f-uuid0003-0000-0000-0000-000000000003"
  }
]

2. Search for a File by Path

If you know the full path to a file but not its ID, use the search endpoint to look it up. This is particularly useful in scripts where the folder structure is known in advance but IDs haven't been stored. The file id returned here can be used directly in the download endpoint.

GET /rest/search?path={kw_path_to_file}

Query parameters

ParameterTypeDescription
pathstringThe full Kiteworks path to the file, ending in the file name — e.g. MyFolder/Reports/Q1.pdf. Must be URL-encoded.
The path value must be URL-encoded before being passed as a query parameter. In Python, use urllib.parse.quote(path). A forward slash (/) encodes to %2F.
search-file.sh
# Path must be URL-encoded — spaces become %20, slashes become %2F
curl -X GET "$KW_INSTANCE_URL/rest/search?path=GenericFolder%2FExample_Subfolder%2FExample_Document.docx" \
  -H "X-Accellion-Version: 28" \
  -H "Authorization: Bearer $KW_ACCESS_TOKEN"
search_file.py
import requests
from urllib.parse import quote
from get_access_token import KWOAuthClient

def get_file_id_by_path(base_url, access_token, kw_path_to_file):
    """Return the file ID for a given Kiteworks file path."""
    headers = {
        "X-Accellion-Version": "28",
        "Authorization": f"Bearer {access_token}",
    }
    encoded_path = quote(kw_path_to_file)
    url = f"{base_url}/rest/search?path={encoded_path}"

    response = requests.get(url, headers=headers)
    response.raise_for_status()
    data = response.json()

    if data.get("files"):
        return data["files"][0]["id"]

    raise ValueError(f"No file found at path: {kw_path_to_file}")

def main():
    base_url = input("Enter Base URL for Kiteworks instance: ")
    client = KWOAuthClient(base_url)
    access_token = client.get_access_token()
    path = input("Enter Kiteworks file path (e.g. MyFolder/Reports/Q1.pdf): ")
    file_id = get_file_id_by_path(base_url, access_token, path)
    print(f"File ID: {file_id}")

if __name__ == "__main__":
    main()

Example response

Returns 200 OK. The file ID is in files[0].id.

200 OK
{
  "files": [
    {
      "id": "uuid0003-0000-0000-0000-000000000003",
      "name": "Example_Document.docx",
      "path": "GenericFolder/Example_Subfolder/Example_Document.docx",
      "type": "f",
      "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "size": 75395,
      "parentId": "uuid0001-0000-0000-0000-000000000001"
    }
  ],
  "folders": []
}

3. Download a File

Downloads the binary content of a file. Use a file id retrieved from step 1 or step 2. A successful response returns the raw file bytes — write them directly to disk.

GET /rest/files/{file_id}/content

Path parameters

ParameterTypeDescription
file_idstring (UUID)The UUID of the file to download.
download-file.sh
# -o writes the response body to a local file
curl -X GET "$KW_INSTANCE_URL/rest/files/uuid0003-0000-0000-0000-000000000003/content" \
  -H "X-Accellion-Version: 28" \
  -H "Authorization: Bearer $KW_ACCESS_TOKEN" \
  -o "./Example_Document.docx"
download_file.py
import requests
from urllib.parse import quote
from get_access_token import KWOAuthClient

def download_file(base_url, access_token, kw_path_to_file, dest_file_path):
    """
    Download a file from Kiteworks to a local path.

    kw_path_to_file: Full Kiteworks path ending in the file name,
                     e.g. "MyFolder/Reports/Q1.pdf"
    dest_file_path:  Local destination path, e.g. "/tmp/Q1.pdf"
    """
    headers = {
        "X-Accellion-Version": "28",
        "Authorization": f"Bearer {access_token}",
    }

    # Step 1: Look up the file ID from its path
    encoded_path = quote(kw_path_to_file)
    search_url = f"{base_url}/rest/search?path={encoded_path}"
    search_response = requests.get(search_url, headers=headers)
    search_response.raise_for_status()
    search_data = search_response.json()

    if not search_data.get("files"):
        raise ValueError(f"No file found at path: {kw_path_to_file}")

    file_id = search_data["files"][0]["id"]

    # Step 2: Download the file content
    download_url = f"{base_url}/rest/files/{file_id}/content"
    response = requests.get(download_url, headers=headers)
    response.raise_for_status()

    with open(dest_file_path, "wb") as f:
        f.write(response.content)

    print(f"File downloaded successfully to {dest_file_path}")

def main():
    base_url = input("Enter Base URL for Kiteworks instance: ")
    client = KWOAuthClient(base_url)
    access_token = client.get_access_token()
    kw_path = input("Enter Kiteworks file path (e.g. MyFolder/Reports/Q1.pdf): ")
    dest = input("Enter local destination path (e.g. /tmp/Q1.pdf): ")
    download_file(base_url, access_token, kw_path, dest)

if __name__ == "__main__":
    main()
The response body is raw binary content, not JSON. Open the destination file in wb (write binary) mode to avoid encoding corruption.

4. Upload a File

Kiteworks uses a chunked upload protocol — every upload, even for small files, follows three steps: initiate the upload session, transfer the file content in one or more chunks, then finalize on the last chunk. You'll need a folder_id for the destination folder — see Manage Folders if you need to retrieve one.

Step 4a — Initiate the upload

Create an upload session for the file. The response returns an id — this is your upload_id, used in every subsequent chunk request.

POST /rest/folders/{folder_id}/actions/initiateUpload

Request body

FieldTypeRequiredDescription
filenamestringRequiredThe name the file will have on Kiteworks once uploaded.
totalSizeintegerRequiredTotal size of the file in bytes.
totalChunksintegerRequiredNumber of chunks the file will be split into. Use 1 for single-chunk uploads.
initiate-upload.sh
curl -X POST "$KW_INSTANCE_URL/rest/folders/uuid0001-0000-0000-0000-000000000001/actions/initiateUpload" \
  -H "X-Accellion-Version: 28" \
  -H "Authorization: Bearer $KW_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "MyNewFile.txt",
    "totalSize": 2048,
    "totalChunks": 1
  }'

Example response

Returns 201 Created. Note the id field — this is your upload_id for all subsequent chunk requests.

201 Created
{
  "id": 7890,
  "error": "OK",
  "totalSize": 2048,
  "totalChunks": 1,
  "uploadedSize": 0,
  "uploadedChunks": 0,
  "completeOk": 0,
  "uri": "dacfs_upload1/rest/uploads/7890",
  "timestamp": "2020-05-15T08:11:07Z"
}

Step 4b — Upload the file content

Send the file content as a multipart form request. For files split across multiple chunks, repeat this request for each chunk, incrementing index each time and setting lastChunk to 1 on the final request.

POST /rest/uploads/{upload_id}

Request fields (multipart/form-data)

FieldTypeRequiredDescription
compressionModestringRequiredCompression mode for the chunk. Only NORMAL is supported.
compressionSizeintegerRequiredCompressed size of the chunk in bytes. When compressionMode is NORMAL, this equals originalSize.
originalSizeintegerRequiredOriginal size of the chunk in bytes.
contentfileRequiredThe binary content of this chunk.
indexintegerOptionalChunk index, starting from 1. Required when uploading multiple chunks.
lastChunkintegerOptionalSet to 1 to signal that this is the final chunk and the upload should be completed.
upload-chunk.sh
# Single-chunk upload — lastChunk=1 completes the upload immediately
curl -X POST "$KW_INSTANCE_URL/rest/uploads/7890?returnEntity=true" \
  -H "X-Accellion-Version: 28" \
  -H "Authorization: Bearer $KW_ACCESS_TOKEN" \
  -F "compressionMode=NORMAL" \
  -F "compressionSize=2048" \
  -F "originalSize=2048" \
  -F "index=1" \
  -F "lastChunk=1" \
  -F "content=@/tmp/MyNewFile.txt"
upload_file.py
import os
import json
import requests
from urllib.parse import quote
from get_access_token import KWOAuthClient

def upload_file(base_url, access_token, path_to_file, path_to_upload_folder, new_file_name):
    """
    Upload a local file to a Kiteworks folder.

    path_to_file:          Local file path, e.g. "/tmp/report.pdf"
    path_to_upload_folder: Kiteworks folder path, e.g. "MyFolder/Reports"
    new_file_name:         Name the file will have on Kiteworks
    """
    headers = {
        "X-Accellion-Version": "28",
        "Authorization": f"Bearer {access_token}",
    }

    # Step 1: Resolve the folder ID from its path
    encoded_path = quote(path_to_upload_folder)
    search_url = f"{base_url}/rest/search?path={encoded_path}"
    search_response = requests.get(search_url, headers=headers)
    search_response.raise_for_status()
    search_data = search_response.json()

    if not search_data.get("folders"):
        raise ValueError(f"Folder not found at path: {path_to_upload_folder}")

    folder_id = search_data["folders"][0]["id"]

    # Step 2: Initiate the upload session
    file_size = os.path.getsize(path_to_file)
    initiate_url = f"{base_url}/rest/folders/{folder_id}/actions/initiateUpload"
    initiate_payload = {
        "filename": new_file_name,
        "totalSize": file_size,
        "totalChunks": 1,
    }
    initiate_response = requests.post(initiate_url, headers=headers, json=initiate_payload)
    initiate_response.raise_for_status()
    upload_id = initiate_response.json()["id"]

    # Step 3: Upload the file as a single chunk
    upload_url = f"{base_url}/rest/uploads/{upload_id}?returnEntity=true"
    with open(path_to_file, "rb") as f:
        payload = {
            "compressionMode": (None, "NORMAL"),
            "compressionSize": (None, str(file_size)),
            "originalSize": (None, str(file_size)),
            "index": (None, "1"),
            "lastChunk": (None, "1"),
            "content": (new_file_name, f),
        }
        upload_response = requests.post(upload_url, headers=headers, files=payload)

    upload_response.raise_for_status()
    print("File uploaded successfully.")
    print(json.dumps(upload_response.json(), indent=2))

def main():
    base_url = input("Enter Base URL for Kiteworks instance: ")
    client = KWOAuthClient(base_url)
    access_token = client.get_access_token()
    path_to_file = input("Enter path to local file: ")
    path_to_folder = input("Enter Kiteworks folder path (e.g. MyFolder/Reports): ")
    new_file_name = input("Enter the file name on Kiteworks: ")
    upload_file(base_url, access_token, path_to_file, path_to_folder, new_file_name)

if __name__ == "__main__":
    main()

Example responses

An intermediate chunk (not the last) returns 200 OK with upload progress. The final chunk returns 201 Created with the completed file object.

201 Created — upload complete
{
  "id": 1240,
  "name": "MyNewFile.txt",
  "type": "f",
  "mime": "text/plain",
  "size": 2048,
  "parentId": 1234,
  "locked": false,
  "deleted": false,
  "permDeleted": false,
  "overriddenExpire": false,
  "expire": null,
  "created": "2020-05-15T06:25:16Z",
  "modified": "2020-05-15T09:30:14Z",
  "fingerprint": "Generating..."
}
200 OK — chunk received
{
  "id": 7890,
  "error": "OK",
  "totalSize": 2048,
  "totalChunks": 2,
  "uploadedSize": 1024,
  "uploadedChunks": 1,
  "completeOk": 0,
  "uri": "dacfs_upload1/rest/uploads/7890",
  "timestamp": "2020-05-15T08:11:07Z"
}
The Python sample above uses a single-chunk upload (totalChunks: 1), which is appropriate for most files. For very large files, split the content into chunks, send each with an incrementing index, and set lastChunk=1 only on the final request.

Frequently Asked Questions

Is there a maximum file size for Kiteworks API uploads?

The maximum file size is determined by your Kiteworks instance configuration. The chunked upload protocol — using initiateUpload followed by one or more chunk POSTs — handles large files by breaking them into smaller pieces, so the per-request payload stays within HTTP limits.

How do I find a file when I only know its path?

Use GET /rest/search?path=<full_path> to locate a file by its complete path string, for example /Top Folder/Subfolder/report.pdf. The response returns the file's id, which you can then use in download or metadata endpoints.

How do I download a file with the Kiteworks REST API?

Send GET /rest/files/{file_id}/content with a valid Bearer token. The API streams the file data directly in the response body with the appropriate Content-Type and Content-Disposition headers. Use the file id from a folder listing or path search to construct the URL.

What chunk size should I use when uploading a file?

A chunk size of 1–10 MB is typical for most network conditions. Larger chunks reduce the number of requests but increase the cost of a failed chunk. The upload protocol supports resuming interrupted uploads by re-sending only the failed chunk.

Next Steps

With file management covered, here's where to go next: