File upload endpoints
In shortReference for the 10xGraph REST endpoints that upload files and retrieve them, with request fields, responses, and authentication requirements.
- 5 min read
- 8 sections
- Updated
- v0.10.0
- Markdown
The file endpoints upload images, audio and documents to the 10xGraph server and return a file_id you reference from graph messages. Each file belongs to the user who uploaded it, and every read checks that ownership. This page lists each route with its permission, request, response and errors.
Base path: /v1/files. Every route needs authentication when auth is configured (see Authentication), and JSON responses use the standard envelope described in REST API conventions. For task-oriented guidance see Files and multimodal on the server and Files and multimodal in the client.
| Method and path | Permission | Purpose |
|---|---|---|
POST /v1/files/upload |
files:upload |
Upload a file, receive a file_id. |
GET /v1/files/{file_id} |
files:read |
Download the raw bytes. |
GET /v1/files/{file_id}/info |
files:read |
Read metadata only. |
GET /v1/files/{file_id}/url |
files:read |
Get a direct access URL. |
GET /v1/config/multimodal |
config:read |
Read the server’s media settings. |
POST /v1/files/upload
Uploads one file as multipart/form-data and returns its file_id, size and type. The response also carries extracted text for documents when text extraction is on.
Request: Content-Type: multipart/form-data
| Field | Type | Description |
|---|---|---|
file |
binary | The file to upload (required, must have a filename) |
Accepted types:
The server accepts any content type by default. MEDIA_ALLOWED_CONTENT_TYPES is empty out of the box, and an empty allowlist means allow everything. Set it to restrict uploads:
MEDIA_ALLOWED_CONTENT_TYPES=image/*,application/pdfEntries may be exact (image/png) or wildcard subtype (image/*). A rejected type returns 415.
Types commonly sent to an agent: image/jpeg, image/png, image/webp, image/gif, audio/mpeg, audio/wav, audio/ogg, application/pdf, text/plain.
Size limit: MEDIA_MAX_SIZE_MB (environment variable, default 25.0 MB). Exceeding it returns 413. The body is read in 1 MiB chunks with a running size cap, so an oversized or chunked upload is rejected before it is buffered whole.
Example:
curl -X POST http://127.0.0.1:8000/v1/files/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@photo.jpg"Response:
{
"data": {
"file_id": "f_abc123",
"mime_type": "image/jpeg",
"size_bytes": 24576,
"filename": "photo.jpg",
"extracted_text": null,
"url": "/v1/files/f_abc123",
"direct_url": null,
"direct_url_expires_at": null
},
"metadata": {"request_id": "...", "timestamp": "...", "message": "OK"}
}| Field | Description |
|---|---|
file_id |
Opaque storage key. Reference this from message content blocks. |
mime_type |
The content type the server recorded. |
size_bytes |
Stored size in bytes. |
filename |
Original filename from the upload. |
extracted_text |
Extracted document text, when DOCUMENT_HANDLING=extract_text and the type is extractable. null otherwise. |
url |
API-relative retrieval path. |
direct_url |
Signed direct URL, when the storage backend can produce one. |
direct_url_expires_at |
Expiry of the signed URL as a Unix timestamp. |
The uploader is recorded as the file’s owner. Every read path checks it, and a file owned by another user returns 404, not 403, so the API never confirms that a foreign file_id exists.
GET /v1/files/{file_id}
Returns the raw bytes of a stored file with its recorded Content-Type. Types that could run script in a browser (HTML, XML, SVG, JavaScript) are served as application/octet-stream downloads with Content-Disposition: attachment, and every response carries X-Content-Type-Options: nosniff.
Inline types (served with Content-Disposition: inline): image/png, image/jpeg, image/gif, image/webp, image/avif, image/bmp, application/pdf, text/plain, and any audio/* or video/* type. Other types are sent as attachments.
Example:
curl http://127.0.0.1:8000/v1/files/f_abc123 --output photo.jpgErrors: 404 if the file does not exist or is owned by another user.
GET /v1/files/{file_id}/info
Returns the metadata of a file without downloading its bytes.
Response:
{
"data": {
"file_id": "f_abc123",
"mime_type": "image/jpeg",
"size_bytes": 24576,
"filename": "photo.jpg",
"extracted_text": null,
"direct_url": null,
"direct_url_expires_at": null
},
"metadata": {"request_id": "...", "timestamp": "...", "message": "OK"}
}| Field | Description |
|---|---|
file_id |
The requested file ID. |
mime_type |
The content type the server recorded. |
size_bytes |
Stored size in bytes. |
filename |
Original filename. |
extracted_text |
Extracted document text if enabled, otherwise null. |
direct_url |
Signed direct URL for the storage backend, if available. |
direct_url_expires_at |
Expiry of the signed URL as a Unix timestamp. |
Errors: 404 if the file does not exist or is owned by another user.
GET /v1/files/{file_id}/url
Returns a URL for fetching the file. With a cloud storage backend this is a signed URL that bypasses the API server; otherwise it is the API-relative path /v1/files/{file_id}. Use it when a consumer needs a URL instead of a file_id.
Response:
{
"data": {
"file_id": "f_abc123",
"url": "https://storage.example.com/10xgraph-media/f_abc123?...",
"expires_at": 1728427200,
"mime_type": "image/jpeg"
},
"metadata": {"request_id": "...", "timestamp": "...", "message": "OK"}
}| Field | Description |
|---|---|
file_id |
The requested file ID. |
url |
Direct URL to the file (signed if cloud storage, API-relative if local). |
expires_at |
Expiry of the signed URL as a Unix timestamp in seconds. null when no signed URL exists. Signed URLs live MEDIA_SIGNED_URL_TTL_SECONDS (default 3600). |
mime_type |
The content type of the file. |
Errors: 404 if the file does not exist or is owned by another user.
Using a file_id in a message
Send the file_id inside a media block in the message content array. The block holds a media reference of kind file_id, not a bare file_id field:
{
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image", "media": {"kind": "file_id", "file_id": "f_abc123", "mime_type": "image/jpeg"}}
]
}
],
"config": {"thread_id": "media-thread"}
}Use audio, video or document as the block type for other files. The graph receives an ImageBlock (or the matching block type) whose media points at the uploaded file, ready to send to a model that supports that input. See Send media to a model for the Python side.
Error responses
Uploads fail with 400, 413 or 415; reads fail with 404. A missing permission returns the standard authorization error (see Authentication).
| Status | Description |
|---|---|
400 |
Missing filename or empty file. |
413 |
File exceeds MEDIA_MAX_SIZE_MB (or the media service rejects its size). The limit is checked as data arrives in 1 MiB chunks, so uploads are rejected before the whole file is buffered. |
415 |
Content type not allowed by MEDIA_ALLOWED_CONTENT_TYPES. |
404 |
file_id not found or owned by another user. |
GET /v1/config/multimodal
Returns the media settings the server runs with, so a client can check limits before uploading. It requires config:read.
curl http://127.0.0.1:8000/v1/config/multimodal -H "Authorization: Bearer $TOKEN"| Field | Description |
|---|---|
media_storage_type |
memory, local or cloud. |
media_max_size_mb |
Upload size limit in MB (MEDIA_MAX_SIZE_MB). |
document_handling |
extract_text, pass_raw or skip (DOCUMENT_HANDLING, default extract_text). |
Permissions
Uploads require files:upload. Downloads, info and URL requests require files:read. Settings are described in Environment variables. Setting MEDIA_REQUIRE_OWNER=true also denies files that have no recorded owner.