[view as .md]

Files

modelux proxies the Anthropic Files API (/v1/files/*) as an authenticated thin passthrough. Upload PDFs / images / text once, then reference them by file_id from /anthropic/v1/messages content blocks instead of inlining base64 every turn.

POST   /anthropic/v1/files                multipart upload
GET    /anthropic/v1/files                list (limit / before_id / after_id)
GET    /anthropic/v1/files/{id}           metadata
GET    /anthropic/v1/files/{id}/content   download bytes (API-generated files only)
DELETE /anthropic/v1/files/{id}

Beta header

The Files API is currently a beta on Anthropic’s side. The proxy forwards your anthropic-beta header verbatim — the SDK’s beta-tag declaration reaches the upstream as-is, so the proxy doesn’t have to track which tag is current:

curl https://api.modelux.ai/anthropic/v1/files \
  -H "Authorization: Bearer mlx_sk_..." \
  -H "anthropic-beta: files-api-2025-04-14" \
  -F "file=@report.pdf"

The official Anthropic SDKs set this header automatically when you opt into the beta on the client.

Upload

multipart/form-data with a single file part. The proxy buffers the body once before forwarding (Anthropic’s upload endpoint requires Content-Length and doesn’t accept chunked transfer encoding):

from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.modelux.ai/anthropic",
    api_key="mlx_sk_...",
    default_headers={"anthropic-beta": "files-api-2025-04-14"},
)

with open("report.pdf", "rb") as f:
    file = client.beta.files.upload(file=f)

print(file.id)  # file_011...

Response shape:

{
  "id": "file_011...",
  "type": "file",
  "filename": "report.pdf",
  "mime_type": "application/pdf",
  "size_bytes": 102400,
  "created_at": "2026-04-16T..."
}

Reference from messages

Once uploaded, use the file id in a content block. The standard pattern is a document block on a user message:

{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1024,
  "messages": [{
    "role": "user",
    "content": [
      {
        "type": "document",
        "source": {"type": "file", "file_id": "file_011..."}
      },
      {"type": "text", "text": "Summarize the executive summary section."}
    ]
  }]
}

Same pattern for image inputs — {"type":"image","source":{"type":"file","file_id":"..."}}.

Retrieve / list / delete

Standard CRUD shapes:

# Metadata
curl https://api.modelux.ai/anthropic/v1/files/file_011... \
  -H "Authorization: Bearer mlx_sk_..." \
  -H "anthropic-beta: files-api-2025-04-14"

# List with pagination
curl "https://api.modelux.ai/anthropic/v1/files?limit=20&after_id=file_011..." \
  -H "Authorization: Bearer mlx_sk_..." \
  -H "anthropic-beta: files-api-2025-04-14"

# Delete
curl -X DELETE https://api.modelux.ai/anthropic/v1/files/file_011... \
  -H "Authorization: Bearer mlx_sk_..." \
  -H "anthropic-beta: files-api-2025-04-14"

Download

GET /anthropic/v1/files/{id}/content

The proxy streams the upstream body chunk-by-chunk via io.Copy (Content-Type preserved) — works for arbitrarily large files without buffering them in proxy memory.

Anthropic restriction: the download endpoint is limited to API-GENERATED files — for example, files produced by computer-use tool calls. User-uploaded files (the ones you POST’d) come back from the upstream with 400 invalid_request_error: File is not downloadable. The proxy passes that error envelope through verbatim. Use the file id for in-message references instead.

BYOK

X-Modelux-Provider-Key: sk-ant-... overrides the org’s stored Anthropic credential for this call. Same precedence as messages / batches.

Observability

Each endpoint logs to ClickHouse with a distinct request_type:

  • file_upload
  • file_list
  • file_retrieve
  • file_download
  • file_delete

Same dashboard breakdowns work for both Anthropic and OpenAI files (the request_type names are shared).

See also