The Files API — upload a file once, then reference it by file_id in as
many Messages requests as you like instead of re-sending the bytes.
{:ok, file} = Claudex.Files.upload(client, "report.pdf")
Claudex.Messages.create(client, %{
model: "claude-opus-5",
max_tokens: 1024,
messages: [%{role: "user", content: [
%{type: "document", source: %{type: "file", file_id: file.id}},
%{type: "text", text: "Summarise this."}
]}]
})This saves upload time and request size, not tokens — the file's content still enters the context window and is still billed.
Uploaded files are visible to the whole workspace, not scoped to a user or
conversation, so never accept a file_id from an untrusted source.
download/2 only works on files Claude created through skills or the code
execution tool; downloading one you uploaded returns a 400.
Summary
Functions
Deletes a file. Deleted files can't be recovered.
Downloads a file's contents.
Lists the files in your workspace, newest first.
Looks up one file's metadata.
Uploads a file, either from a path or as {content, filename}.
Functions
@spec delete(Claudex.Client.t(), String.t()) :: :ok | {:error, Claudex.Error.t()}
Deletes a file. Deleted files can't be recovered.
@spec download(Claudex.Client.t(), String.t()) :: {:ok, binary()} | {:error, Claudex.Error.t()}
Downloads a file's contents.
Only works on files Claude created — check downloadable on the metadata
first. Returns the whole file in memory, so mind the 500 MB ceiling.
@spec list(Claudex.Client.t(), keyword()) :: {:ok, Claudex.Page.t(Claudex.FileMetadata.t())} | {:error, Claudex.Error.t()}
Lists the files in your workspace, newest first.
Options
:limit- how many per page, 1 to 1000. Defaults to 20.:page- thenext_pagecursor from a previous page.
Files paginate with an opaque cursor rather than ids: pass
page: page.next_page for the next page, and stop when it's nil.
@spec retrieve(Claudex.Client.t(), String.t()) :: {:ok, Claudex.FileMetadata.t()} | {:error, Claudex.Error.t()}
Looks up one file's metadata.
@spec upload(Claudex.Client.t(), Path.t() | {iodata(), String.t()}, keyword()) :: {:ok, Claudex.FileMetadata.t()} | {:error, Claudex.Error.t()}
Uploads a file, either from a path or as {content, filename}.
Options
:content_type- the file's MIME type. The API detects it from the content when you leave this out.:expires_in_seconds- delete the file automatically after this long. Between 3600 (an hour) and 7_776_000 (90 days); without it the file lives until you delete it.
Raises if a path can't be read — that's a problem with your filesystem rather than with the API.