Skip to main content

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

FieldTypeRequiredDescription
purposestringYesINPUT for execution inputs, UPLOAD for general storage
regionstringNoStorage region: US (default), EU, or APAC
ttl_daysintegerNoAuto-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"
}
FieldDescription
file_idThe file reference to use in execution inputs
upload_urlPresigned PUT URL. Valid for 1 hour.
expires_atWhen 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:
ConcernHow ModelRoute handles it
Filename injectionOriginal filenames are discarded. All files get UUID-based names.
Content type spoofingMIME type is detected server-side from file magic bytes, never from client headers.
Size manipulationActual file size is read from storage after upload, not from client-declared values.
Path traversalObject keys are generated server-side using UUIDs. No user input enters storage paths.
Memory exhaustionUploads are streamed through io.Pipe. The server never holds the full file in memory.
Cross-org accessEvery file is scoped to the uploading organization. Ownership is verified on every access.

Error handling

Error codeCause
UNSUPPORTED_CONTENT_TYPEThe detected file type is not in the allow-list
QUOTA_EXCEEDEDOrganization storage quota reached. Delete files or request a quota increase.
FILE_NOT_FOUNDThe file reference does not exist or the upload was not completed
RATE_LIMITEDToo 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']}")