Skip to content

Files and Media

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 formExampleUse when
ToolRouter file IDast_a1b2c3d4e5f67890The file was uploaded to ToolRouter Files or produced by another tool
Hosted HTTP(S) URLhttps://example.com/image.pngThe 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.

bash
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:

json
{
  "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.

bash
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:

json
{
  "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 intentAccepted uploaded file content type
Image fields such as image_url, photo_url, thumbnail_url, mask_urlimage/*
Video fields such as video_url, clip_urlvideo/*
Audio fields such as audio_url, voice_url, track_urlaudio/*
Generic file/document fieldsAny 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:

json
{
  "image_path": "/tmp/toolrouter-output.png"
}

Response after asset processing:

json
{
  "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:

typescript
image_url: {
  type: 'string',
  description: 'Image as a ToolRouter file ID or hosted HTTP(S) URL',
}

Bad:

typescript
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 *_url or *_urls for media inputs that accept file IDs or hosted URLs
  • use *_data, *_base64, or *_bytes only for explicit inline binary payloads
  • use *_path only for server-local files produced or consumed inside the ToolRouter runtime
  • return generated files as *_path outputs 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