// API Reference

Files API

Upload once, reference by id across the project. JSONL for batch jobs, fine-tune corpora, prompt assets, all on the OpenAI /v1/files shape with two-tier storage, per-project isolation, and tier-bound retention.

Endpoints

Files are project-scoped. All paths are relative to your project base URL https://api.xerotier.ai/proj_ABC123.

Method Path Description
POST /{project_id}/v1/files Upload a file
GET /{project_id}/v1/files List files
GET /{project_id}/v1/files/{file_id} Get file metadata
GET /{project_id}/v1/files/{file_id}/content Download file content
DELETE /{project_id}/v1/files/{file_id} Delete a file

Upload Files

POST /{project_id}/v1/files

Upload a file using multipart/form-data:

Field Type Description
filerequired binary File content. Maximum 500MB.
purposerequired string File purpose. See Supported Purposes.
relative_pathoptional string Relative path within an upload tree, forward-slash separated (e.g. "sub/data.jsonl"). Preserved on the row and echoed back as x_relative_path. Used for directory uploads.
curl
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/files \ -H "Authorization: Bearer xero_myproject_your_api_key" \ -F "file=@requests.jsonl" \ -F "purpose=batch"

Response

Returns HTTP 201 Created with the file object:

{ "id": "file-1a2b3c4d5e6f7890abcd1234", "object": "file", "purpose": "batch", "filename": "requests.jsonl", "bytes": 1048576, "status": "uploaded", "created_at": 1709000000, "expires_at": null, "x_relative_path": null }

File IDs follow the format file-<24 hex chars>. x_relative_path echoes the optional relative_path form field; it is null for flat uploads. See Status Lifecycle for the expires_at contract.

List Files

GET /{project_id}/v1/files

Returns a list of all files for the project. Supports filtering by purpose and limits up to 100 per page.

Query Parameters

Parameter Type Description
limitoptional integer Maximum files to return (1-100, default 20).
purposeoptional string Filter by purpose (e.g., "batch").
x_prefixoptional string Filter by relative_path prefix. Returns rows whose path equals the prefix or begins with "<prefix>/". Case-sensitive. Xerotier extension.
curl
curl https://api.xerotier.ai/proj_ABC123/v1/files \ -H "Authorization: Bearer xero_myproject_your_api_key"

Response

{ "object": "list", "data": [ { "id": "file-1a2b3c4d5e6f7890abcd1234", "object": "file", "purpose": "batch", "filename": "requests.jsonl", "bytes": 1048576, "status": "uploaded", "created_at": 1709000000, "expires_at": null, "x_relative_path": null } ], "has_more": false }

Get File Metadata

GET /{project_id}/v1/files/{file_id}

curl
curl https://api.xerotier.ai/proj_ABC123/v1/files/file-1a2b3c4d5e6f7890abcd1234 \ -H "Authorization: Bearer xero_myproject_your_api_key"

Download File Content

GET /{project_id}/v1/files/{file_id}/content

Downloads the raw file content. The response Content-Type is application/jsonl for JSONL files.

curl
curl https://api.xerotier.ai/proj_ABC123/v1/files/file-1a2b3c4d5e6f7890abcd1234/content \ -H "Authorization: Bearer xero_myproject_your_api_key" \ -o output.jsonl

Delete Files

DELETE /{project_id}/v1/files/{file_id}

Marks the file as deleted in the database and removes it from object storage on a best-effort basis. There is no active-batch reference check; callers are responsible for not deleting files that are still in use by a running batch.

curl
curl -X DELETE https://api.xerotier.ai/proj_ABC123/v1/files/file-1a2b3c4d5e6f7890abcd1234 \ -H "Authorization: Bearer xero_myproject_your_api_key"

Response

Returns the full file object with status: "deleted". Xerotier does not emit OpenAI's canonical {id, object, deleted: true} shape; match on status.

{ "id": "file-1a2b3c4d5e6f7890abcd1234", "object": "file", "purpose": "batch", "filename": "requests.jsonl", "bytes": 1048576, "status": "deleted", "created_at": 1709000000, "expires_at": null, "x_relative_path": null }

Supported Purposes

A single 500MB upload cap applies to every purpose; there are no per-purpose size limits.

Purpose Format Description
batch JSONL Input/output files for batch processing. Consumed by the Batch API.
assistants Various Accepted for OpenAI compatibility. Stored but not processed.
vision Various Accepted for OpenAI compatibility. Stored but not processed.
user_data Various Accepted for OpenAI compatibility. Stored but not processed.
fine-tune JSONL Accepted for OpenAI compatibility. Stored but not processed.

Note: Only batch purpose files are actively consumed by the batch processor. All other purposes are accepted for API compatibility but are stored without further processing.

Status Lifecycle

Status Description
uploaded File has been uploaded and stored in object storage.
validated File content has been validated and is ready for use (e.g., JSONL parsing succeeded).
error File validation failed. Check the batch job's error details for specifics.
deleted File has been deleted (terminal state).

Files are validated when they are first used by a batch job. Allowed transitions are:

  • uploaded -> validated on successful validation.
  • uploaded -> error on validation failure (terminal).
  • validated -> deleted on explicit delete (terminal).
  • error -> deleted on explicit delete (terminal).

expires_at contract: reserved on the response envelope and always null today. Files do not expire automatically; they remain until you call DELETE.

CLI Commands

The xeroctl CLI provides file management commands:

Upload a file
xeroctl files upload --purpose batch requests.jsonl
List files
xeroctl files list
Get file info
xeroctl files get file-1a2b3c4d5e6f7890abcd1234
Download file content
xeroctl files download file-1a2b3c4d5e6f7890abcd1234 -o output.jsonl
Delete a file
xeroctl files delete file-1a2b3c4d5e6f7890abcd1234

Client Examples

Python (requests)

Python
import requests headers = {"Authorization": "Bearer xero_myproject_your_api_key"} base = "https://api.xerotier.ai/proj_ABC123/v1" # Upload a file with open("requests.jsonl", "rb") as f: upload_resp = requests.post( f"{base}/files", headers=headers, files={"file": ("requests.jsonl", f)}, data={"purpose": "batch"} ) file_obj = upload_resp.json() print(f"Uploaded: {file_obj['id']}") # List files files = requests.get(f"{base}/files", headers=headers).json() for f in files["data"]: print(f"{f['id']} - {f['filename']} ({f['status']})") # Get file metadata meta = requests.get(f"{base}/files/{file_obj['id']}", headers=headers).json() print(f"File size: {meta['bytes']} bytes") # Download file content content = requests.get( f"{base}/files/{file_obj['id']}/content", headers=headers ) with open("output.jsonl", "wb") as out: out.write(content.content) # Delete a file requests.delete(f"{base}/files/{file_obj['id']}", headers=headers)

Node.js (fetch)

JavaScript
import { readFile, writeFile } from "node:fs/promises"; const base = "https://api.xerotier.ai/proj_ABC123/v1"; const headers = { "Authorization": "Bearer xero_myproject_your_api_key" }; // Upload a file const fileData = await readFile("requests.jsonl"); const formData = new FormData(); formData.append("file", new Blob([fileData]), "requests.jsonl"); formData.append("purpose", "batch"); const uploadRes = await fetch(`${base}/files`, { method: "POST", headers: { "Authorization": headers.Authorization }, body: formData }); const fileObj = await uploadRes.json(); console.log(`Uploaded: ${fileObj.id}`); // List files const listRes = await fetch(`${base}/files`, { headers }); const files = await listRes.json(); files.data.forEach(f => console.log(`${f.id} - ${f.filename}`)); // Get file metadata const metaRes = await fetch(`${base}/files/${fileObj.id}`, { headers }); const meta = await metaRes.json(); console.log(`File size: ${meta.bytes} bytes`); // Download file content const contentRes = await fetch(`${base}/files/${fileObj.id}/content`, { headers }); const content = await contentRes.arrayBuffer(); await writeFile("output.jsonl", Buffer.from(content)); // Delete a file await fetch(`${base}/files/${fileObj.id}`, { method: "DELETE", headers });

Error Handling

HTTP Status Envelope type Description
400 invalid_request_error Missing file, missing or invalid purpose, malformed relative_path or x_prefix, or non-multipart body.
401 invalid_request_error Invalid or missing API key.
404 invalid_request_error File not found.
413 invalid_request_error Upload body exceeds the 500MB maximum.
429 rate_limit_exceeded Too many requests. Check the Retry-After header.
500 internal_error Database write, object-storage upload/download, or status-update failures during upload, list, get, download, or delete.

OpenAI deviations: 401 emits type: invalid_request_error rather than the canonical authentication_error, and 404 carries no machine-readable code field. Match on HTTP status, not envelope type or code, when porting client error handlers.