The VecML File System RAG API provides a hierarchical file system-based approach to document management and RAG queries. Upload documents to your personal file system, organize them in folders, and query them using natural language. This is syncronized with chat.vecml.com AI Knowledge Hub. All your operation can be visualized at chat.vecml.com AI Knowledge Hub.
Organize files in folders with familiar experience like Finder or Windows Explorer
Query files or folders as you like
Files are automatically synchronized to the chat.vecml.com AI Knowledge Hub
First, you need to obtain an API key from VecML:
All API requests should be made to the following base URL:
https://filesystem.vecml.com/apiUpload a file to your file system. The file will be automatically indexed for RAG queries by default, and will be synchronized to the chat.vecml.com AI Knowledge Hub.
We introduce how to upload a file, multiple files, and a folder with structure in the following sections.
POST /uploadfile (file, required): The file to uploadtarget_path (string, optional): Target directory path (default: "/" for root). Directories are created automatically if they don't exist.use_mm (boolean, optional): Enable multimodal indexing for images/figures (default: false)auto_index (boolean, optional): Automatically index the file after upload (default: true)success (boolean): Whether the upload was successfulmessage (string): Status messagefile (object): File information including:file_id (string): Unique identifier for the uploaded file (use this for /force-reindex and other operations)filename (string): Name of the filepath (string): Relative path in your file systemsize_bytes (integer): File size in bytesmime_type (string): Detected MIME typeis_indexed (boolean): Whether the file is indexed for RAGis_indexing (boolean): Whether the file is currently being indexedimport requests
# Get your API key from https://account.vecml.com/user-api-keys
api_key = "your_api_key_here"
base_url = "https://filesystem.vecml.com/api"
headers = {
"X-API-Key": api_key
}
# Upload a file to your file system
files = {
'file': ('document.pdf', open('path/to/document.pdf', 'rb'), 'application/pdf')
}
data = {
'target_path': '/my_documents', # Target directory (will be created if not exists)
'use_mm': 'false', # Enable multimodal indexing
'auto_index': 'true' # Automatically index after upload
}
response = requests.post(
f"{base_url}/upload",
headers=headers,
files=files,
data=data
)
result = response.json()
print(f"File ID: {result['file']['file_id']}")
print(f"Uploaded: {result['file']['filename']}")
print(f"Path: {result['file']['path']}")
print(f"Indexed: {result['file']['is_indexed']}")# Upload a file to your file system
curl -X POST "https://filesystem.vecml.com/api/upload" \
-H "X-API-Key: your_api_key_here" \
-F "file=@/path/to/document.pdf" \
-F "target_path=/my_documents" \
-F "use_mm=false" \
-F "auto_index=true"To upload multiple files, make separate API calls for each file. For best performance:
import requests
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
api_key = "your_api_key_here"
base_url = "https://filesystem.vecml.com/api"
headers = {"X-API-Key": api_key}
# List of files to upload
files_to_upload = [
"document1.pdf",
"document2.pdf",
"report.docx",
"data.csv"
]
def upload_file(filepath, target_path="/my_documents"):
"""Upload a single file with proper timeout"""
with open(filepath, 'rb') as f:
response = requests.post(
f"{base_url}/upload",
headers=headers,
files={'file': (os.path.basename(filepath), f)},
data={'target_path': target_path, 'auto_index': 'true'},
timeout=1800 # 30 minutes timeout for large files
)
return filepath, response.json()
# Upload with max 5 concurrent connections
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(upload_file, f): f for f in files_to_upload}
for future in as_completed(futures):
filepath, result = future.result()
if result.get('success'):
file_id = result['file']['file_id']
print(f"✓ Uploaded: {filepath} (file_id: {file_id})")
else:
print(f"✗ Failed: {filepath} - {result.get('detail')}")# Upload multiple files using a bash loop
# Recommended: max 5 concurrent uploads, 1800s timeout
FILES=("doc1.pdf" "doc2.pdf" "report.docx")
for file in "${FILES[@]}"; do
curl -X POST "https://filesystem.vecml.com/api/upload" \
-H "X-API-Key: your_api_key_here" \
-F "file=@${file}" \
-F "target_path=/my_documents" \
-F "auto_index=true" \
--max-time 1800 &
# Limit to 5 concurrent uploads
if [[ $(jobs -r -p | wc -l) -ge 5 ]]; then
wait -n
fi
done
wait
echo "All uploads complete!"To upload a folder while preserving its directory structure, iterate through all files and use thetarget_path parameter to specify each file's destination directory. Directories are automatically created if they don't exist.
target_path set to the remote directory pathimport requests
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
api_key = "your_api_key_here"
base_url = "https://filesystem.vecml.com/api"
headers = {"X-API-Key": api_key}
def upload_folder(local_folder, remote_base_path="/"):
"""
Upload a local folder while preserving directory structure.
The target_path parameter auto-creates directories if they don't exist.
"""
files_to_upload = []
# Collect all files with their remote paths
for root, dirs, files in os.walk(local_folder):
# Calculate relative path from local_folder
rel_path = os.path.relpath(root, local_folder)
if rel_path == ".":
remote_dir = remote_base_path.rstrip("/")
else:
remote_dir = f"{remote_base_path.rstrip('/')}/{rel_path}".replace("\\", "/")
for filename in files:
local_file = os.path.join(root, filename)
files_to_upload.append((local_file, remote_dir, filename))
def upload_single(args):
local_file, remote_dir, filename = args
with open(local_file, 'rb') as f:
response = requests.post(
f"{base_url}/upload",
headers=headers,
files={'file': (filename, f)},
data={'target_path': remote_dir, 'auto_index': 'true'},
timeout=1800 # 30 minutes timeout
)
return f"{remote_dir}/{filename}", response.json()
# Upload with max 5 concurrent connections
results = {"success": 0, "failed": 0}
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(upload_single, args) for args in files_to_upload]
for future in as_completed(futures):
path, result = future.result()
if result.get('success'):
results["success"] += 1
file_id = result['file']['file_id']
print(f"✓ {path} (file_id: {file_id})")
else:
results["failed"] += 1
print(f"✗ {path}: {result.get('detail')}")
print(f"\nDone! {results['success']} succeeded, {results['failed']} failed")
# Usage: Upload local "my_project" folder to remote "/projects/my_project"
upload_folder("./my_project", "/projects/my_project")Create a directory (or nested directories) in your file system. This works like mkdir -p - all intermediate directories are created automatically if they don't exist. If the directory already exists, it returns success without error.
POST /mkdirpath (string, required): Directory path to create (e.g., "/my_folder/nested/subfolder")success (boolean): Whether the operation was successfulmessage (string): Success or error messagedirectory (object): Created directory info (file_id, filename, path, is_directory)/upload endpoint also auto-creates directories if target_path doesn't exist# Create a directory (supports nested paths)
import requests
import json
response = requests.post(
f"{base_url}/mkdir",
headers={**headers, "Content-Type": "application/json"},
data=json.dumps({
"path": "/my_folder/nested/subfolder" # Creates all intermediate directories
})
)
result = response.json()
print(f"Directory created: {result['directory']['path']}")# Create a directory (supports nested paths like mkdir -p)
curl -X POST "https://filesystem.vecml.com/api/mkdir" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"path": "/my_folder/nested/subfolder"}'List files and folders in a directory with sorting and pagination support. This is syncronized with chat.vecml.com AI Knowledge Hub.
GET /listpath (string, optional): Directory path to list (default: "/" for root)recursive (boolean, optional): Include subdirectories (default: false)sort_by (string, optional): Sort field — name, updated_at, size, type, or status (default: "name")sort_dir (string, optional): Sort direction — asc or desc (default: "asc")offset (integer, optional): Number of items to skip for pagination (default: 0)limit (integer, optional): Maximum number of items to return, 1–500 (default: 100)success (boolean): Whether the operation was successfulpath (string): The listed directory pathfiles (array): List of files and folders with metadata (filename, path, size, is_directory, is_indexed, is_indexing)total_count (integer): Total number of items matching the queryoffset (integer): Current offsetlimit (integer): Current limithas_more (boolean): Whether there are more items beyond this page# List files in a directory with sorting and pagination
response = requests.get(
f"{base_url}/list",
headers=headers,
params={
"path": "/my_documents", # Directory path (use "/" for root)
"recursive": False, # Set to True to include subdirectories
"sort_by": "name", # Sort by: name, updated_at, size, type, status
"sort_dir": "asc", # Sort direction: asc or desc
"offset": 0, # Skip N items (for pagination)
"limit": 100 # Max items to return (1-500)
}
)
result = response.json()
print(f"Path: {result['path']}")
print(f"Total: {result['total_count']} items (showing {len(result['files'])})")
print(f"Has more: {result['has_more']}")
for file_info in result['files']:
file_type = "DIR" if file_info['is_directory'] else "FILE"
status = "indexing" if file_info['is_indexing'] else ("indexed" if file_info['is_indexed'] else "not indexed")
print(f" [{file_type}] {file_info['filename']} - {status}")# List files with sorting and pagination
curl -X GET "https://filesystem.vecml.com/api/list?path=/my_documents&sort_by=name&sort_dir=asc&offset=0&limit=100" \
-H "X-API-Key: your_api_key_here"Search for files by name using fuzzy matching. Supports both CJK (Chinese, Japanese, Korean) substring search and similarity-based search for other languages.
GET /searchquery (string, required): Search query to match against filenamespath (string, optional): Directory scope to search within (default: "/" for all files)limit (integer, optional): Maximum number of results, 1–100 (default: 20)success (boolean): Whether the search was successfulquery (string): The search query usedresults (array): List of matching files, each with:file_id, filename, path, size_bytes, mime_typeis_directory, is_indexed, is_indexingsimilarity_score (float): How closely the filename matches (0–1)# Search for files by name
response = requests.get(
f"{base_url}/search",
headers=headers,
params={
"query": "report", # Search query (matches filename)
"path": "/my_documents", # Directory scope (default: "/" for all)
"limit": 20 # Max results (1-100, default: 20)
}
)
result = response.json()
print(f"Search: '{result['query']}'")
print(f"Found {len(result['results'])} results:")
for item in result['results']:
print(f" {item['filename']} (score: {item['similarity_score']}, path: {item['path']})")# Search for files by name
curl -X GET "https://filesystem.vecml.com/api/search?query=report&path=/my_documents&limit=20" \
-H "X-API-Key: your_api_key_here"Get detailed information about a specific file or folder, including indexing status.
GET /filepath (string, required): Relative path of the file or folderfile_id (string): Unique identifierfilename (string): Name of the filepath (string): Relative pathsize_bytes (integer): File size in bytesis_directory (boolean): Whether it's a directoryis_indexed (boolean): Whether it's indexed for RAGis_indexing (boolean): Whether it's currently being indexedcreated_at, updated_at (string): Timestampsindexing_status: null for filesIn addition to the fields above, directories include an indexing_status object:
indexing_status.total_files (integer): Total number of files under this directoryindexing_status.indexed_files (integer): Number of files that are indexedindexing_status.indexing_files (integer): Number of files currently being indexedindexing_status.unindexed_files (integer): Number of files not yet indexedindexing_status.is_complete (boolean): Whether all files are indexed# Get information about a specific file
response = requests.get(
f"{base_url}/file",
headers=headers,
params={"path": "/my_documents/document.pdf"}
)
result = response.json()
print(f"Filename: {result['filename']}")
print(f"Path: {result['path']}")
print(f"Size: {result['size_bytes']} bytes")
print(f"Is Directory: {result['is_directory']}")
print(f"Is Indexed: {result['is_indexed']}")
print(f"Is Indexing: {result['is_indexing']}")# Get file information
curl -X GET "https://filesystem.vecml.com/api/file?path=/my_documents/document.pdf" \
-H "X-API-Key: your_api_key_here"
# Get directory info (includes indexing_status summary)
curl -X GET "https://filesystem.vecml.com/api/file?path=/my_documents" \
-H "X-API-Key: your_api_key_here"# Get directory info with indexing status summary
response = requests.get(
f"{base_url}/file",
headers=headers,
params={"path": "/my_documents"}
)
result = response.json()
print(f"Directory: {result['filename']}")
status = result['indexing_status']
print(f"Total files: {status['total_files']}")
print(f"Indexed: {status['indexed_files']}")
print(f"Indexing: {status['indexing_files']}")
print(f"Unindexed: {status['unindexed_files']}")
print(f"Complete: {status['is_complete']}")Download a file or directory from your file system. Single files are returned directly, while directories are automatically packaged as zip archives.
GET /downloadpath (string, required): Relative path of the file or directory to downloadContent-Disposition: attachment headerapplication/pdf, application/zip)Note: When downloading directories, the zip file is created on-the-fly and includes the complete directory structure. Large directories may take a moment to compress.
# Download a single file
response = requests.get(
f"{base_url}/download",
headers=headers,
params={"path": "/my_documents/document.pdf"}
)
# Save the downloaded file
with open("downloaded_document.pdf", "wb") as f:
f.write(response.content)
print("File downloaded successfully!")
# Download a directory (returns as zip)
response = requests.get(
f"{base_url}/download",
headers=headers,
params={"path": "/my_documents/"}
)
# Save the downloaded zip file
with open("my_documents.zip", "wb") as f:
f.write(response.content)
print("Directory downloaded as zip successfully!")# Download a single file
curl -X GET "https://filesystem.vecml.com/api/download?path=/my_documents/document.pdf" \
-H "X-API-Key: your_api_key_here" \
-o downloaded_document.pdf
# Download a directory (returns as zip)
curl -X GET "https://filesystem.vecml.com/api/download?path=/my_documents/" \
-H "X-API-Key: your_api_key_here" \
-o my_documents.zipIndex an existing file or an entire directory for RAG queries. Use this if you uploaded files with auto_index=false. If a file is already indexed, it will be skipped. When a directory path is provided, all unindexed files within it are indexed recursively.
POST /indexpath (string, required): Relative path of the file or directory to indexuse_mm (boolean, optional): Enable multimodal indexing (default: false)success (boolean): Whether indexing was successfulmessage (string): Status messagesuccess (boolean): Whether the overall operation succeededmessage (string): Summary messagepath (string): Directory path that was indexedtotal_files (integer): Total unindexed files foundindexed_count (integer): Files successfully indexedalready_indexed_count (integer): Files that were already indexedfailed_count (integer): Files that failed to indexresults (array): Per-file results, each with file_id, filename, path, success, message# Index a single file for RAG queries
response = requests.post(
f"{base_url}/index",
headers=headers,
data={
"path": "/my_documents/document.pdf",
"use_mm": "false" # Enable multimodal indexing
}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")# Index a single file
curl -X POST "https://filesystem.vecml.com/api/index" \
-H "X-API-Key: your_api_key_here" \
-F "path=/my_documents/document.pdf" \
-F "use_mm=false"
# Index an entire directory (recursive)
curl -X POST "https://filesystem.vecml.com/api/index" \
-H "X-API-Key: your_api_key_here" \
-F "path=/my_documents" \
-F "use_mm=false"# Index all unindexed files in a directory (recursive)
response = requests.post(
f"{base_url}/index",
headers=headers,
data={
"path": "/my_documents", # Pass a directory path
"use_mm": "false"
}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Total files: {result['total_files']}")
print(f"Indexed: {result['indexed_count']}")
print(f"Already indexed: {result['already_indexed_count']}")
print(f"Failed: {result['failed_count']}")
# Per-file results
for r in result.get('results', []):
status = "OK" if r['success'] else "FAIL"
print(f" [{status}] {r['filename']}: {r['message']}")Force reindex a file or directory. This endpoint will be useful when you believe a file or directory is not indexed correctly.
POST /force-reindexfile_id (string, required): ID of the file or directory to force reindexsuccess (boolean): Whether force reindexing was successfulmessage (string): Status messagesuccess (boolean): Whether the overall operation succeededmessage (string): Summary messagepath (string): Directory path that was indexedtotal_files (integer): Total unindexed files foundindexed_count (integer): Files successfully indexedalready_indexed_count (integer): Files that were already indexedfailed_count (integer): Files that failed to indexresults (array): Per-file results, each with file_id, filename, path, success, message# Force reindex a single file
response = requests.post(
f"{base_url}/force-reindex",
headers=headers,
data={
"file_id": "file_id"
}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")# Force reindex a single file
curl -X POST "https://filesystem.vecml.com/api/force-reindex" \
-H "X-API-Key: your_api-key_here" \
-F "file_id=file_id"# Force reindex an entire directory
response = requests.post(
f"{base_url}/force-reindex",
headers=headers,
data={
"file_id": "file_id"
}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")Query your indexed documents using natural language and get AI-generated answers. This single endpoint supports non-streaming responses, real-time streaming, and multimodal image attachments — controlled by the input parameters.
Note: This is a stateless, single-turn Q&A endpoint — it does not maintain conversation history. If you need multi-turn chat with files, please see the Chat System section below.
POST /queryquery (string, required): Natural language question or search queryfile_paths (array, required): List of file/folder paths to query (e.g., ["/docs/report.pdf", "/notes/"])llm_model (string, optional): LLM model to use (default: "qwen3_8b"). gpt-4.1 is suggested for better performance.streaming (boolean, optional): Enable real-time streaming response (default: false)temperature (float, optional): Response creativity 0.0-1.0 (default: 0.7)max_retrieve_tokens (integer, optional): Maximum RAG context tokens (default: 5000)system_prompt (string, optional): Custom system prompt for the LLMadditional_attachments (array, optional): Base64-encoded images as data URIs (e.g., "data:image/png;base64,..."). When provided, the multimodal LLM is used for visual understanding. Supported formats: PNG, JPEG, GIF, WebP, TIFF.custom_base_url (string, optional): Custom base URL for file reference links embedded in RAG answers. By default, the answer includes clickable file links pointing to VecML's hosted viewer (e.g., [report.pdf](https://chat.vecml.com/files?file_id=xxx)). When you set this parameter, those links will point to your own server instead (e.g., [report.pdf](https://myapp.example.com/files?file_id=xxx)). This is useful when you want to host your own file preview, download, or viewer page — you can retrieve the file_id from the /upload response in your backend server and map it to your custom backend for a fully branded experience.qwen3_8bqwen3_4bgpt-4.1-nanogemini-2.0-flashgpt-4o-minigpt-4.1-minigpt-4.1gemini-3-proclaude-4-5-sonnetgpt-5.2claude-4-5-opusclaude-4-6-opusanswer (string): AI-generated answer based on retrieved contentusage (object, optional): Token usage information with prompt_tokens, completion_tokens, and total_tokensstreaming: true)text/plainNote: Credit balance is checked before each query. If you have insufficient credits for the selected model, you will receive an HTTP 402 (Payment Required) error. Use the /usage endpoint to check your available credits, or use free models (qwen3_8b, qwen3_4b) which don't require credits.
# Query your files using natural language
query_data = {
"query": "What is the main topic of this document?",
"file_paths": ["/my_documents/document.pdf"], # Can include files and/or folders
"llm_model": "qwen3_8b", # Options: qwen3_8b, gemini-2.0-flash, gpt-4o-mini, etc.
"streaming": False,
"temperature": 0.7,
"max_retrieve_tokens": 5000, # Maximum tokens for RAG context
"system_prompt": "You are a helpful assistant.", # Optional custom prompt
# "custom_base_url": "https://myapp.example.com" # Optional: override file link URLs in answers
}
response = requests.post(
f"{base_url}/query",
headers={**headers, "Content-Type": "application/json"},
json=query_data
)
result = response.json()
print("Answer:", result['answer'])
if result.get('usage'):
print(f"Token usage: {result['usage']['total_tokens']} total tokens")# Query files with RAG
curl -X POST "https://filesystem.vecml.com/api/query" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"query": "What is the main topic of this document?",
"file_paths": ["/my_documents/document.pdf"],
"llm_model": "qwen3_8b",
"streaming": false,
"temperature": 0.7,
"custom_base_url": "https://myapp.example.com"
}'# Streaming query example
import requests
query_data = {
"query": "Summarize this document in detail.",
"file_paths": ["/my_documents"], # Query all files in a folder
"llm_model": "qwen3_8b",
"streaming": True,
"temperature": 0.7
}
response = requests.post(
f"{base_url}/query",
headers={**headers, "Content-Type": "application/json"},
json=query_data,
stream=True
)
print("Streaming response:")
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
print(chunk, end='', flush=True)
print() # New line after streaming# Streaming query
curl -X POST "https://filesystem.vecml.com/api/query" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"query": "Summarize this document in detail.",
"file_paths": ["/my_documents"],
"llm_model": "qwen3_8b",
"streaming": true,
"temperature": 0.7
}' \
--no-bufferimport requests
import base64
api_key = "your_api_key_here"
base_url = "https://filesystem.vecml.com/api"
headers = {"X-API-Key": api_key}
# Encode images as base64 data URIs
def encode_image(image_path):
with open(image_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
return f"data:image/png;base64,{b64}"
image_uris = [
encode_image("./chart.png"),
encode_image("./screenshot.png"),
]
# Query with images + optional RAG context
query_data = {
"query": "What information is shown in these images?",
"file_paths": ["/my_documents"], # Optional: combine with RAG
"llm_model": "qwen3_8b",
"additional_attachments": image_uris, # Base64 data URIs
"streaming": False
}
response = requests.post(
f"{base_url}/query",
headers={**headers, "Content-Type": "application/json"},
json=query_data
)
result = response.json()
print("Answer:", result['answer'])Rename a file or folder. The item stays in the same directory but gets a new name.
POST /renamepath (string, required): Relative path of the file or folder to rename (e.g., "/folder/old_name.pdf")new_name (string, required): New name for the file or folder (e.g., "new_name.pdf")success (boolean): Whether the rename was successfulmessage (string): Status messagefile (object): Updated file information with the new name and path# Rename a file or folder
import json
response = requests.post(
f"{base_url}/rename",
headers={**headers, "Content-Type": "application/json"},
data=json.dumps({
"path": "/my_documents/old_name.pdf",
"new_name": "new_name.pdf"
})
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")
print(f"New path: {result['file']['path']}")# Rename a file or folder
curl -X POST "https://filesystem.vecml.com/api/rename" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"path": "/my_documents/old_name.pdf", "new_name": "new_name.pdf"}'Move a file or folder to a different directory. The destination directory will be created automatically if it doesn't exist.
POST /movesource_path (string, required): Relative path of the file or folder to move (e.g., "/folder/file.pdf")destination_path (string, required): Relative path of the target directory (e.g., "/archive/")success (boolean): Whether the move was successfulmessage (string): Status messagefile (object): Updated file information with the new path# Move a file or folder to a new directory
import json
response = requests.post(
f"{base_url}/move",
headers={**headers, "Content-Type": "application/json"},
data=json.dumps({
"source_path": "/my_documents/document.pdf",
"destination_path": "/archive/"
})
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")
print(f"New path: {result['file']['path']}")# Move a file or folder
curl -X POST "https://filesystem.vecml.com/api/move" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"source_path": "/my_documents/document.pdf", "destination_path": "/archive/"}'Delete a file or folder from your file system. Deleting a folder removes all its contents.
DELETE /filepath (string, required): Relative path of the file or folder to deletesuccess (boolean): Whether deletion was successfulmessage (string): Status messageWarning: This action cannot be undone. Deleting a folder will permanently remove all files and subfolders within it.
# Delete a file or folder
response = requests.delete(
f"{base_url}/file",
headers=headers,
params={"path": "/my_documents/document.pdf"}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")# Delete a file or folder
curl -X DELETE "https://filesystem.vecml.com/api/file?path=/my_documents/document.pdf" \
-H "X-API-Key: your_api_key_here"Get your current credit balances and storage usage. This helps you monitor your usage and plan accordingly.
GET /usagecredits (object): Credit information for each tierlow_tier: Remaining credits and models (gpt-4.1-nano)middle_tier: Remaining credits and models (gpt-4o-mini, gpt-4.1-mini, gemini-2.0-flash)high_tier: Remaining credits and models (gpt-4o, gpt-4.1, gpt-5, etc.)free_models: Models that don't consume credits (qwen3_8b, qwen3_4b)available_models: All models you can currently use based on your creditstotal_credit_usage: Total credits consumed to datestorage (object): Storage usage informationused_bytes, used_mb: Current storage usedlimit_bytes, limit_mb: Your storage limitremaining_bytes, remaining_mb: Available storageusage_percentage: Percentage of storage useduser_level (string): Your account level (normal, plus, pro)# Get your credit and storage usage
response = requests.get(
f"{base_url}/usage",
headers=headers
)
result = response.json()
# Credit information
print("=== Credits ===")
print(f"Low tier remaining: {result['credits']['low_tier']['remaining']}")
print(f"Middle tier remaining: {result['credits']['middle_tier']['remaining']}")
print(f"High tier remaining: {result['credits']['high_tier']['remaining']}")
print(f"Free models: {result['credits']['free_models']}")
print(f"Available models: {result['credits']['available_models']}")
# Storage information
print("\n=== Storage ===")
print(f"Used: {result['storage']['used_mb']} MB / {result['storage']['limit_mb']} MB")
print(f"Remaining: {result['storage']['remaining_mb']} MB")
print(f"Usage: {result['storage']['usage_percentage']}%")# Get your credit and storage usage
curl -X GET "https://filesystem.vecml.com/api/usage" \
-H "X-API-Key: your_api_key_here"The Chat System API provides multi-turn conversation capabilities on top of the File System RAG. Create chat sessions, send messages with or without document context, and maintain full conversation history. Chat sessions are synchronized with chat.vecml.com and will appear in the website's chat history.
Create a new chat session and get a session ID for subsequent messages.
POST /chat/createtitle (string, optional): Initial title for the chat session (default: "API Chat Session"). The chat system automatically generates and updates the title based on your messages and LLM responses after each turn, so leaving this as default is perfectly fine. This parameter is useful when you want to assign a custom initial title (e.g., "New Chat", "Project Q&A") before any messages are sent.success (boolean): Whether creation was successfulchat_id (string): Unique session ID — use this in all subsequent callstitle (string): The chat titlecreated_at (string): ISO 8601 timestampimport requests
api_key = "your_api_key_here"
base_url = "https://filesystem.vecml.com/api"
headers = {"X-API-Key": api_key}
# Create a new chat session
response = requests.post(
f"{base_url}/chat/create",
headers={**headers, "Content-Type": "application/json"},
json={"title": "My Research Chat"} # Optional, defaults to "API Chat Session"
)
result = response.json()
chat_id = result["chat_id"]
print(f"Chat ID: {chat_id}")
print(f"Title: {result['title']}")
print(f"Created at: {result['created_at']}")# Create a new chat session
curl -X POST "https://filesystem.vecml.com/api/chat/create" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"title": "My Research Chat"}'Send a message to a chat session and get an LLM response. This single endpoint supports non-streaming responses, real-time streaming, and multimodal image attachments — controlled by the input parameters. It also supports RAG context via file_paths and maintains full conversation history.
POST /chat/messagechat_id (string, required): Session ID from /chat/createquery (string, required): User message / questionfile_paths (array, optional): File/folder paths for RAG context this turn. Empty or null means no RAG.llm_model (string, optional): LLM model to use (default: "qwen3_8b"). gpt-4.1 is suggested for better performance.streaming (boolean, optional): Enable real-time streaming response (default: false)temperature (float, optional): Response creativity 0.0–1.0 (default: 0.7)max_retrieve_tokens (integer, optional): Maximum RAG context tokens (default: 5000)system_prompt (string, optional): Custom system promptadditional_attachments (array, optional): Base64-encoded images as data URIs (e.g., "data:image/png;base64,..."). When provided, the multimodal LLM is used for visual understanding. Supported formats: PNG, JPEG, GIF, WebP, TIFF.custom_base_url (string, optional): Custom base URL for file reference links embedded in RAG answers. By default, the answer includes clickable file links pointing to VecML's hosted viewer (e.g., [report.pdf](https://chat.vecml.com/files?file_id=xxx)). When you set this parameter, those links will point to your own server instead (e.g., [report.pdf](https://myapp.example.com/files?file_id=xxx)). This is useful when you want to host your own file preview, download, or viewer page — you can retrieve the file_id from the /upload response in your backend server and map it to your custom backend for a fully branded experience.answer (string): LLM-generated answerchat_id (string): The session IDmessage_id (string): ID of the assistant messageusage (object, optional): Token usage infostreaming: true)text/plain# Send a message to a chat session (non-streaming)
response = requests.post(
f"{base_url}/chat/message",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id, # Required: from /chat/create
"query": "What is the main topic of the document?",
"file_paths": ["/my_documents/report.pdf"], # Optional: paths for RAG context
"llm_model": "qwen3_8b", # Optional: default "qwen3_8b"
"streaming": False, # Optional: default False
"temperature": 0.7, # Optional: default 0.7
"max_retrieve_tokens": 5000, # Optional: default 5000
"system_prompt": "You are a helpful assistant.", # Optional: custom prompt
# "custom_base_url": "https://myapp.example.com" # Optional: override file link URLs
}
)
result = response.json()
print(f"Answer: {result['answer']}")
print(f"Chat ID: {result['chat_id']}")
if result.get("usage"):
print(f"Tokens used: {result['usage']['total_tokens']}")# Send a message to a chat session
curl -X POST "https://filesystem.vecml.com/api/chat/message" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "your-chat-id",
"query": "What is the main topic of the document?",
"file_paths": ["/my_documents/report.pdf"],
"llm_model": "qwen3_8b",
"streaming": false,
"temperature": 0.7,
"custom_base_url": "https://myapp.example.com"
}'# Streaming chat message
response = requests.post(
f"{base_url}/chat/message",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id,
"query": "Summarize this document in detail.",
"file_paths": ["/my_documents"],
"llm_model": "qwen3_8b",
"streaming": True
},
stream=True
)
print("Streaming response:")
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
if chunk:
print(chunk, end='', flush=True)
print()# Streaming chat message
curl -X POST "https://filesystem.vecml.com/api/chat/message" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "your-chat-id",
"query": "Summarize this document in detail.",
"file_paths": ["/my_documents"],
"llm_model": "qwen3_8b",
"streaming": true
}' \
--no-bufferimport base64
# Read images and encode as data URIs
def encode_image(image_path):
with open(image_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
return f"data:image/png;base64,{b64}"
image_uris = [
encode_image("./image1.png"),
encode_image("./image2.png"),
]
# Send message with image attachments (triggers multimodal LLM)
response = requests.post(
f"{base_url}/chat/message",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id,
"query": "What do you see in these images?",
"llm_model": "qwen3_8b",
"additional_attachments": image_uris # Base64 data URIs
}
)
result = response.json()
print(f"Answer: {result['answer']}")# Note: For images, it's easier to use Python or another language
# to encode images as base64 data URIs.
# The additional_attachments field accepts an array of data URIs:
# "data:image/png;base64,iVBORw0KGgo..."
curl -X POST "https://filesystem.vecml.com/api/chat/message" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "your-chat-id",
"query": "What do you see in this image?",
"llm_model": "qwen3_8b",
"additional_attachments": ["data:image/png;base64,iVBORw0KGgo..."]
}'List all your chat sessions with pagination support. Only returns chats that have at least one message.
GET /chat/listoffset (integer, optional): Number of items to skip for pagination (default: 0)limit (integer, optional): Maximum items to return, 1–200 (default: 50)success (boolean): Whether the operation was successfulchats (array): List of chat sessions, each with:chat_id (string): Session IDtitle (string): Chat titlecreated_at (string): Creation timestampupdated_at (string): Last activity timestamptotal_count (integer): Total number of chat sessionsoffset, limit (integer): Current pagination valueshas_more (boolean): Whether there are more items beyond this page# List all chat sessions with pagination
response = requests.get(
f"{base_url}/chat/list",
headers=headers,
params={
"offset": 0, # Skip N items (for pagination)
"limit": 50 # Max items to return (1-200)
}
)
result = response.json()
print(f"Total chats: {result['total_count']}")
print(f"Has more: {result['has_more']}")
for chat in result['chats']:
print(f" [{chat['chat_id'][:8]}...] {chat['title']} (updated: {chat['updated_at']})")# List all chat sessions
curl -X GET "https://filesystem.vecml.com/api/chat/list?offset=0&limit=50" \
-H "X-API-Key: your_api_key_here"Retrieve the full message history of a chat session, along with the currently attached files.
GET /chat/messageschat_id (string, required): Session ID of the chatsuccess (boolean): Whether the operation was successfulchat_id (string): The session IDmessages (array): Ordered list of messages, each with:id (string): Message IDrole (string): "user" or "assistant"content (string): Message textcreated_at (string): Timestampattached_files (array or null): Currently attached file paths (most recent selection)# Get full message history of a chat session
response = requests.get(
f"{base_url}/chat/messages",
headers=headers,
params={"chat_id": chat_id}
)
result = response.json()
print(f"Chat ID: {result['chat_id']}")
print(f"Attached files: {result.get('attached_files')}")
print(f"Messages ({len(result['messages'])}):")
for msg in result['messages']:
role = msg['role'].upper()
content = msg['content'][:100]
print(f" [{role}] {content}...")# Get message history of a chat session
curl -X GET "https://filesystem.vecml.com/api/chat/messages?chat_id=your-chat-id" \
-H "X-API-Key: your_api_key_here"Rename a chat session. Useful for organizing your conversations with descriptive titles. Note that our chat system automatically generates a chat title based on your messages and the LLM response, so you may not need this endpoint if you are satisfied with the auto-generated titles.
POST /chat/renamechat_id (string, required): Session ID of the chat to renamenew_name (string, required): New title for the chat session (cannot be empty)success (boolean): Whether the rename was successfulchat_id (string): The session IDtitle (string): The new titlemessage (string): Status message# Rename a chat session
response = requests.post(
f"{base_url}/chat/rename",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id,
"new_name": "Q4 Financial Analysis"
}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"New title: {result['title']}")# Rename a chat session
curl -X POST "https://filesystem.vecml.com/api/chat/rename" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"chat_id": "your-chat-id", "new_name": "Q4 Financial Analysis"}'Delete a chat session and all its messages permanently.
DELETE /chatchat_id (string, required): Session ID of the chat to deletesuccess (boolean): Whether deletion was successfulmessage (string): Status messageWarning: This action cannot be undone. All messages in the chat session will be permanently deleted.
# Delete a chat session and all its messages
response = requests.delete(
f"{base_url}/chat",
headers=headers,
params={"chat_id": chat_id}
)
result = response.json()
print(f"Success: {result['success']}")
print(f"Message: {result['message']}")# Delete a chat session
curl -X DELETE "https://filesystem.vecml.com/api/chat?chat_id=your-chat-id" \
-H "X-API-Key: your_api_key_here"A complete example showing the typical workflow: create a session → send messages with RAG → follow-up questions → rename → review history.
import requests
import base64
api_key = "your_api_key_here"
base_url = "https://filesystem.vecml.com/api"
headers = {"X-API-Key": api_key}
# 1. Create a chat session
create_resp = requests.post(
f"{base_url}/chat/create",
headers={**headers, "Content-Type": "application/json"},
json={"title": "Document Analysis"}
)
chat_id = create_resp.json()["chat_id"]
print(f"Created chat: {chat_id}")
# 2. Ask questions with RAG context
msg1 = requests.post(
f"{base_url}/chat/message",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id,
"query": "What are the key findings in this report?",
"file_paths": ["/my_documents/report.pdf"],
"llm_model": "qwen3_8b"
}
)
print(f"Answer 1: {msg1.json()['answer'][:200]}...")
# 3. Follow-up question (uses chat history automatically)
msg2 = requests.post(
f"{base_url}/chat/message",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id,
"query": "Can you elaborate on the second point?",
"file_paths": ["/my_documents/report.pdf"],
"llm_model": "qwen3_8b"
}
)
print(f"Answer 2: {msg2.json()['answer'][:200]}...")
# 4. Ask without RAG (no file_paths = pure LLM)
msg3 = requests.post(
f"{base_url}/chat/message",
headers={**headers, "Content-Type": "application/json"},
json={
"chat_id": chat_id,
"query": "Summarize everything we discussed so far.",
"llm_model": "qwen3_8b"
}
)
print(f"Summary: {msg3.json()['answer'][:200]}...")
# 5. Rename the chat
requests.post(
f"{base_url}/chat/rename",
headers={**headers, "Content-Type": "application/json"},
json={"chat_id": chat_id, "new_name": "Q4 Report Analysis"}
)
print("Chat renamed!")
# 6. View all messages
msgs = requests.get(
f"{base_url}/chat/messages",
headers=headers,
params={"chat_id": chat_id}
).json()
print(f"Total messages: {len(msgs['messages'])}")
# 7. Clean up (optional)
# requests.delete(f"{base_url}/chat", headers=headers, params={"chat_id": chat_id})