Documentation Index
Fetch the complete documentation index at: https://docs.modelroute.ai/llms.txt
Use this file to discover all available pages before exploring further.
How uploading works
ModelRoute handles file uploads with zero trust. You send the raw file bytes, and ModelRoute:
- Detects the content type from file magic bytes (never trusts client-declared MIME types)
- Generates a UUID filename (original filenames are never stored or exposed)
- Verifies the actual file size from the uploaded bytes (declared sizes are always overridden)
- Streams the upload to storage without buffering the entire file in memory
You never need to tell ModelRoute what your file is. ModelRoute figures it out from the bytes.
Step 1: Request a presigned upload URL
curl -X POST https://api.modelroute.ai/v1/files/upload \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"purpose": "INPUT"
}'
Request body
| Field | Type | Required | Description |
|---|
purpose | string | Yes | INPUT for execution inputs, UPLOAD for general storage |
region | string | No | Storage region: US (default), EU, or APAC |
ttl_days | integer | No | Auto-delete after N days (1-365). Uses your org default if omitted. |
Response
{
"file_id": "file_f47ac10b-58cc-4372-a567-0e02b2c3d479",
"upload_url": "https://storage.modelroute.ai/uploads/file_f47ac10b...?X-Amz-Signature=...",
"expires_at": "2026-03-20T11:30:00Z"
}
| Field | Description |
|---|
file_id | The file reference to use in execution inputs |
upload_url | Presigned PUT URL. Valid for 1 hour. |
expires_at | When the upload URL expires |
Step 2: Upload the file
Use an HTTP PUT request to upload the raw file bytes to the presigned URL:
curl -X PUT "https://storage.modelroute.ai/uploads/file_f47ac10b...?X-Amz-Signature=..." \
--data-binary @photo.png
ModelRoute detects the file type from the first bytes of the uploaded content. You do not need to set a Content-Type header on the PUT request. If you do set one, ModelRoute will override it with the server-detected type.
Step 3: Confirm the upload
After the PUT completes, confirm the upload so ModelRoute can verify the file in storage:
curl -X POST https://api.modelroute.ai/v1/files/confirm \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"file_id": "file_f47ac10b-58cc-4372-a567-0e02b2c3d479"
}'
ModelRoute will:
- Verify the file exists in storage
- Read the actual file size from storage (not from any client-declared value)
- Detect the content type from the file bytes
- Generate a compliance clone for audit purposes
- Return the confirmed file metadata
Confirm response
{
"id": "file_f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "ACTIVE",
"content_type": "image/png",
"size_bytes": 2048576,
"created_at": "2026-03-20T10:30:00Z"
}
Step 4: Use the file reference
Pass the file_id in your execution input:
curl -X POST https://api.modelroute.ai/v1/executions \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-1.1-pro",
"input": {
"prompt": "Transform this photo into an oil painting",
"image": "file_f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}'
Security model
ModelRoute follows a zero-trust approach to file handling:
| Concern | How ModelRoute handles it |
|---|
| Filename injection | Original filenames are discarded. All files get UUID-based names. |
| Content type spoofing | MIME type is detected server-side from file magic bytes, never from client headers. |
| Size manipulation | Actual file size is read from storage after upload, not from client-declared values. |
| Path traversal | Object keys are generated server-side using UUIDs. No user input enters storage paths. |
| Memory exhaustion | Uploads are streamed through io.Pipe. The server never holds the full file in memory. |
| Cross-org access | Every file is scoped to the uploading organization. Ownership is verified on every access. |
Error handling
| Error code | Cause |
|---|
UNSUPPORTED_CONTENT_TYPE | The detected file type is not in the allow-list |
QUOTA_EXCEEDED | Organization storage quota reached. Delete files or request a quota increase. |
FILE_NOT_FOUND | The file reference does not exist or the upload was not completed |
RATE_LIMITED | Too many upload requests. Retry after a short delay. |
Complete upload flow example
import requests
API_KEY = "sk_your_api_key_here"
BASE = "https://api.modelroute.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# 1. Request presigned URL
upload_resp = requests.post(
f"{BASE}/files/upload",
headers=HEADERS,
json={"purpose": "INPUT"}
).json()
file_id = upload_resp["file_id"]
upload_url = upload_resp["upload_url"]
# 2. Upload the file (just raw bytes, no metadata needed)
with open("photo.png", "rb") as f:
requests.put(upload_url, data=f)
# 3. Confirm the upload
confirm = requests.post(
f"{BASE}/files/confirm",
headers=HEADERS,
json={"file_id": file_id}
).json()
print(f"Uploaded: {confirm['content_type']}, {confirm['size_bytes']} bytes")
# 4. Use in execution
result = requests.post(
f"{BASE}/executions",
headers=HEADERS,
json={
"model": "flux-1.1-pro",
"input": {
"prompt": "Make this photo look like a watercolor painting",
"image": file_id
}
}
).json()
print(f"Execution {result['id']}: {result['status']}")