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:

Terminal
MEDIA_ALLOWED_CONTENT_TYPES=image/*,application/pdf

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

Terminal
curl -X POST http://127.0.0.1:8000/v1/files/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg"

Response:

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

Terminal
curl http://127.0.0.1:8000/v1/files/f_abc123 --output photo.jpg

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

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

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

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

Terminal
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.

Last updated for v0.10.0Edit this page on GitHubReport an issue