ToolRouter has one shared file contract for every tool: local files must be uploaded to ToolRouter first, and media inputs must accept either a ToolRouter file ID or a hosted HTTP(S) URL.
This keeps agents from inventing tool-specific upload flows. The same rules apply to images, video, audio, PDFs, documents, and generated files.
What should callers pass to tools?
Use one of these forms for media inputs:
| Input form | Example | Use when |
|---|---|---|
| ToolRouter file ID | ast_a1b2c3d4e5f67890 | The file was uploaded to ToolRouter Files or produced by another tool |
| Hosted HTTP(S) URL | https://example.com/image.png | The file is already reachable by the hosted ToolRouter backend |
Do not pass a caller-machine path such as /Users/alex/Desktop/photo.png to a hosted tool. The hosted backend cannot read files from your laptop or agent sandbox. Upload the file first, then pass the returned asset_id.
Server-local paths are only valid for tool handlers running inside the ToolRouter server, and only for schema fields explicitly named and documented as server-local *_path fields.
Upload a local file
Upload files with POST /v1/assets/upload using multipart/form-data.
curl -X POST https://api.toolrouter.com/v1/assets/upload \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@./screenshot.png;type=image/png" \
-F "filename=screenshot.png" \
-F "content_type=image/png"Response:
{
"asset_id": "ast_a1b2c3d4e5f67890",
"url": "https://api.toolrouter.com/v1/assets/ast_a1b2c3d4e5f67890",
"page_url": "https://toolrouter.com/a/ast_a1b2c3d4e5f67890",
"content_type": "image/png",
"size_bytes": 120602
}Use asset_id in later tool calls. You can also use url, but the file ID is the preferred portable reference because ToolRouter can resolve it, check ownership, and validate MIME type before the handler starts.
Call a tool with an uploaded file
Any media input field such as image_url, image_urls, video_url, audio_url, media_url, file_url, source_images, overview_image, or mask_url must accept ToolRouter file IDs and hosted HTTP(S) URLs.
curl -X POST https://api.toolrouter.com/v1/tools/call \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "photo-location-finder",
"skill": "read_exif",
"input": {
"image_url": "ast_a1b2c3d4e5f67890"
}
}'Before the handler runs, ToolRouter resolves the file ID into a hosted URL. The handler receives a URL it can fetch from the backend, not the raw caller-local file path.
Arrays and simple media objects are normalized too:
{
"image_urls": [
"ast_a1b2c3d4e5f67890",
"https://example.com/reference.png",
{ "src": "ast_b2c3d4e5f67890a1", "alt": "Reference" }
]
}MIME checks
ToolRouter validates uploaded file content types against the intent of the input field:
| Field intent | Accepted uploaded file content type |
|---|---|
Image fields such as image_url, photo_url, thumbnail_url, mask_url | image/* |
Video fields such as video_url, clip_url | video/* |
Audio fields such as audio_url, voice_url, track_url | audio/* |
| Generic file/document fields | Any supported uploaded file type |
If a caller passes a text file to an image field, the call fails before the tool handler runs with an INVALID_MEDIA_INPUT error.
What content types can be uploaded?
The direct upload endpoint accepts capturable files up to 100 MB. Supported types include:
- images: PNG, JPEG, GIF, WebP, SVG, AVIF, BMP, TIFF
- video: MP4, WebM, MOV, MPEG
- audio: MP3, WAV, OGG, MP4 audio, FLAC, AAC
- documents and data: PDF, ZIP, PPTX, DOCX, XLSX, CSV, TXT, Markdown, XML, YAML, RTF, JSON
- 3D models: GLB and glTF
Set a real MIME type on the file part or pass content_type. Uploads with only application/octet-stream are rejected because ToolRouter cannot safely validate how the file should be used.
How generated files come back
Tool handlers should return generated local files as *_path output keys. The gateway uploads those files to ToolRouter storage and injects URL/file metadata beside the original key.
Example handler output:
{
"image_path": "/tmp/toolrouter-output.png"
}Response after asset processing:
{
"image_path": "/tmp/toolrouter-output.png",
"image_url": "https://api.toolrouter.com/v1/assets/ast_fedcba9876543210",
"image_asset": {
"asset_id": "ast_fedcba9876543210",
"content_type": "image/png",
"size_bytes": 48291
},
"image_page": "https://toolrouter.com/a/ast_fedcba9876543210"
}For generated media, prefer returning *_path from the handler. ToolRouter owns the upload, storage, URL rewriting, and response metadata.
Rules for tool authors
Tool schemas must make media support explicit. Any media input field must say that it accepts ToolRouter file IDs and hosted HTTP(S) URLs.
Good:
image_url: {
type: 'string',
description: 'Image as a ToolRouter file ID or hosted HTTP(S) URL',
}Bad:
image_url: {
type: 'string',
description: 'Public image URL to process',
}The bad schema is rejected by manifest validation because it creates URL-only behavior and breaks uploaded profile/library files.
Use these conventions:
- use
*_urlor*_urlsfor media inputs that accept file IDs or hosted URLs - use
*_data,*_base64, or*_bytesonly for explicit inline binary payloads - use
*_pathonly for server-local files produced or consumed inside the ToolRouter runtime - return generated files as
*_pathoutputs so the asset pipeline can upload them - never tell callers to pass laptop or agent-sandbox absolute paths to hosted tools
Why this standard exists
Without one standard, every tool ends up with a slightly different answer to "how do I pass an image?" Some tools accept URLs, some accept local paths, some accept uploaded profile files, and some fail only after the job starts.
ToolRouter now enforces the media contract at the platform layer:
- manifests fail validation when media fields are URL-only or local-path-only
- uploaded file IDs are resolved before handlers run
- content type mismatches fail early
- generated files are uploaded through one asset pipeline
- all clients can use the same file references across REST, CLI, MCP, and the website