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
- List files in a folder — retrieve file objects and their IDs from a known folder
- Search for a file by path — look up a file ID when you know its path but not its ID
- Download a file — retrieve file content using a file ID from steps 1 or 2
- Upload a file — initiate a chunked upload and transfer file content to a folder
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.
/rest/folders/{folder_id}/files
Path parameters
| Parameter | Type | Description |
|---|---|---|
folder_id | string (UUID) | The UUID of the folder whose files you want to list. |
Query parameters
All parameters are optional.
| Parameter | Type | Description |
|---|---|---|
name | string | Filter by exact file name. |
userId | string | Filter by the UUID of the file's creator. |
orderBy | string | Sort field and direction — e.g. name:asc. See Sorting. |
offset | integer | Number of records to skip. See Pagination. |
limit | integer | Maximum number of files to return. |
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"
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.
[
{
"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.
/rest/search?path={kw_path_to_file}
Query parameters
| Parameter | Type | Description |
|---|---|---|
path | string | The full Kiteworks path to the file, ending in the file name — e.g. MyFolder/Reports/Q1.pdf. Must be URL-encoded. |
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.
# 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"
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.
{
"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.
/rest/files/{file_id}/content
Path parameters
| Parameter | Type | Description |
|---|---|---|
file_id | string (UUID) | The UUID of the file to download. |
# -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"
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()
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.
/rest/folders/{folder_id}/actions/initiateUpload
Request body
| Field | Type | Required | Description |
|---|---|---|---|
filename | string | Required | The name the file will have on Kiteworks once uploaded. |
totalSize | integer | Required | Total size of the file in bytes. |
totalChunks | integer | Required | Number of chunks the file will be split into. Use 1 for single-chunk uploads. |
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.
{
"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.
/rest/uploads/{upload_id}
Request fields (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
compressionMode | string | Required | Compression mode for the chunk. Only NORMAL is supported. |
compressionSize | integer | Required | Compressed size of the chunk in bytes. When compressionMode is NORMAL, this equals originalSize. |
originalSize | integer | Required | Original size of the chunk in bytes. |
content | file | Required | The binary content of this chunk. |
index | integer | Optional | Chunk index, starting from 1. Required when uploading multiple chunks. |
lastChunk | integer | Optional | Set to 1 to signal that this is the final chunk and the upload should be completed. |
# 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"
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.
{
"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..."
}
{
"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"
}
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:
- Manage Mail → Send and receive mail on behalf of authenticated users
- Manage Folders → Create and navigate the folder structure files live in
- API Reference → Full
/filesand/uploadsendpoint specifications