Haijun Platform Docs
ID

The Files API lets you upload and manage files to use with the Haijun API without re-uploading content with each request. This is particularly useful when using the code execution tool to provide inputs (for example, datasets and documents) and then download outputs (for example, charts). You can explore the API reference directly, in addition to this guide.

File type support

Referencing a file_id in a Messages request is supported on all models that support the given file type. Images are supported on all current Haijun models. For PDFs and other file types with the code execution tool, see the linked pages for model support.

How the Files API works

The Files API provides a create-once, use-many-times approach for working with files:

  • Upload files to Juglow's secure storage and receive a unique file_id
  • Download files that are created by tracks or the code execution tool
  • Reference files in Messages requests using the file_id instead of re-uploading content
  • Manage your files with list, retrieve, and delete operations

Warning: Uploaded files are accessible to your entire workspace, not scoped to an end user, conversation, or session. Any API key with access to a workspace can access any files uploaded to that workspace. Every service account, and every user whose organization role allows API access, can use the Default Workspace in addition to any workspace you add them to, so keep files that must stay separate in their own workspace and access them only with keys scoped to that workspace. Never accept file_id values from end users or other untrusted sources: a user-supplied file ID would let one user of your application read content that another user uploaded. Treat file IDs as server-side references, and keep the mapping between your users and their files in your application. If you are building a multi-tenant application on the Files API, create a separate workspace for each tenant. The workspace is the isolation boundary for files, so a workspace per tenant gives each tenant's data hard isolation from every other tenant. Each organization can have up to 100 workspaces; contact your account team if you need more.

How to use the Files API

Uploading a file

Upload a file to be referenced in future API calls:

bash
  FILE_ID=$(curl -X POST https://haijun.my.id/v1/files \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -F "file=@/path/to/document.pdf" | jq -r '.id')
  echo "$FILE_ID"
bash
  FILE_ID=$(ant files upload \
    --file /path/to/document.pdf \
    --transform id \
    --raw-output)
  echo "$FILE_ID"
python
  uploaded = client.files.upload(
      file=("document.pdf", open("/path/to/document.pdf", "rb"), "application/pdf"),
  )
  file_id = uploaded.id
  print(file_id)
typescript
  const uploaded = await client.files.upload({
    file: await toFile(
      fs.createReadStream("/path/to/document.pdf"),
      undefined,
      { type: "application/pdf" },
    ),
  });
  console.log(uploaded.id);
csharp
  var uploaded = await client.Files.Upload(
      new FileUploadParams
      {
          File = new BinaryContent
          {
              Stream = File.OpenRead("/path/to/document.pdf"),
              FileName = "document.pdf",
              ContentType = new("application/pdf")
          }
      });

  var fileId = uploaded.ID;
  Console.WriteLine(fileId);
go
  f, err := os.Open("/path/to/document.pdf")
  if err != nil {
  	log.Fatal(err)
  }
  defer f.Close()

  response, err := client.Files.Upload(context.Background(),
  	juglow.FileUploadParams{
  		File: juglow.File(f, "document.pdf", "application/pdf"),
  	})
  if err != nil {
  	log.Fatal(err)
  }

  fileID := response.ID
  fmt.Println(fileID)
java
  FileMetadata file = client.files().upload(
      FileUploadParams.builder()
          .file(MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("/path/to/document.pdf")))
              .filename("document.pdf")
              .contentType("application/pdf")
              .build())
          .build()
  );

  String fileId = file.id();
  System.out.println(fileId);
php
  $file = $client->files->upload(
      file: FileParam::fromResource(fopen('/path/to/document.pdf', 'rb'), contentType: 'application/pdf'),
  );

  $fileId = $file->id;
  echo $fileId;
ruby
  file = client.files.upload(
    file: Juglow::FilePart.new(
      Pathname("/path/to/document.pdf"),
      content_type: "application/pdf"
    )
  )

  file_id = file.id
  puts file_id

The response from uploading a file includes:

json
{
  "id": "file_011CNha8iCJcU1wXNR6q4V8w",
  "type": "file",
  "filename": "document.pdf",
  "mime_type": "application/pdf",
  "size_bytes": 1024000,
  "created_at": "2025-01-01T00:00:00Z",
  "downloadable": false,
  "expires_at": null
}

downloadable is false for files you upload. Only files created by tracks or the code execution tool can be downloaded. See Downloading a file.

Using a file in messages

Once uploaded, reference the file by passing the id from the upload response as file_id:

bash
  curl -X POST https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d @- <<EOF
  {
    "model": "haijun-opus-5-5",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": "Please summarize this document for me."
          },
          {
            "type": "document",
            "source": {
              "type": "file",
              "file_id": "$FILE_ID"
            }
          }
        ]
      }
    ]
  }
  EOF
bash
  ant messages create <<YAML
  model: haijun-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content:
        - type: text
          text: Please summarize this document for me.
        - type: document
          source:
            type: file
            file_id: $FILE_ID
  YAML
python
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": [
                  {"type": "text", "text": "Please summarize this document for me."},
                  {
                      "type": "document",
                      "source": {
                          "type": "file",
                          "file_id": file_id,
                      },
                  },
              ],
          }
      ],
  )
  print(response)
typescript
  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "text",
            text: "Please summarize this document for me.",
          },
          {
            type: "document",
            source: {
              type: "file",
              file_id: uploaded.id,
            },
          },
        ],
      },
    ],
  });

  console.log(response);
csharp
  var response = await client.Messages.Create(
      new MessageCreateParams
      {
          Model = Model.HaijunOpus5_5,
          MaxTokens = 1024,
          Messages =
          [
              new MessageParam
              {
                  Role = Role.User,
                  Content = new List<ContentBlockParam>
                  {
                      new TextBlockParam { Text = "Please summarize this document for me." },
                      new DocumentBlockParam
                      {
                          Source = new FileDocumentSource { FileID = fileId }
                      }
                  }
              }
          ]
      });

  Console.WriteLine(response);
go
  msg, err := client.Messages.New(context.Background(),
  	juglow.MessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 1024,
  		Messages: []juglow.MessageParam{
  			juglow.NewUserMessage(
  				juglow.NewTextBlock("Please summarize this document for me."),
  				juglow.NewDocumentBlock(juglow.FileDocumentSourceParam{
  					FileID: fileID,
  				}),
  			),
  		},
  	})
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(msg)
java
  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.HAIJUN_OPUS_5_5)
      .maxTokens(1024)
      .addUserMessageOfBlockParams(List.of(
          ContentBlockParam.ofText(TextBlockParam.builder()
              .text("Please summarize this document for me.")
              .build()),
          ContentBlockParam.ofDocument(DocumentBlockParam.builder()
              .fileSource(fileId)
              .build())
      ))
      .build();

  Message message = client.messages().create(params);
  System.out.println(message);
php
  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => [
                  ['type' => 'text', 'text' => 'Please summarize this document for me.'],
                  [
                      'type' => 'document',
                      'source' => [
                          'type' => 'file',
                          'fileID' => $fileId,
                      ],
                  ],
              ],
          ],
      ],
      model: 'haijun-opus-5-5',
  );

  echo $response;
ruby
  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          { type: "text", text: "Please summarize this document for me." },
          {
            type: "document",
            source: {
              type: "file",
              file_id: file_id
            }
          }
        ]
      }
    ]
  )

  puts response

File types and content blocks

The Files API supports different file types that correspond to different content block types:

File typeMIME typeContent block typeUse case
PDFapplication/pdfdocumentText analysis, document processing
Plain texttext/plaindocumentText analysis, processing
Imagesimage/jpeg, image/png, image/gif, image/webpimageImage analysis, visual tasks
Datasets, othersVariescontainer_uploadAnalyze data, create visualizations

Document blocks

For PDFs and text files, use the document content block:

json
{
  "type": "document",
  "source": {
    "type": "file",
    "file_id": "file_011CNha8iCJcU1wXNR6q4V8w"
  },
  "title": "Document Title", // Optional
  "context": "Context about the document", // Optional
  "citations": { "enabled": true } // Optional, enables citations
}

Image blocks

For images, use the image content block:

json
{
  "type": "image",
  "source": {
    "type": "file",
    "file_id": "file_011CPMxVD3fHLUhvTqtsQA5w"
  }
}

Container upload blocks

To send a file to the code execution tool, use the container_upload content block:

json
{
  "type": "container_upload",
  "file_id": "file_011CNha8iCJcU1wXNR6q4V8w"
}

Working with other file formats

For file types that the document block doesn't support (for example, .docx and .xlsx), convert the files to plain text and include the content directly in your message. Files that are already plain text, such as .csv and .md files, can either be read in this way or uploaded through the Files API with an explicit text/plain content type. To analyze datasets instead of reading them as text, upload them for the code execution tool using a container_upload block.

The following examples read a text file and send its contents as plain text:

bash
  # Read the text file
  # Note: For files with special characters, consider base64 encoding
  TEXT_CONTENT=$(cat document.txt)

  curl https://haijun.my.id/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -d @- <<EOF
  {
    "model": "haijun-opus-5-5",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": "Here's the document content:\n\n${TEXT_CONTENT}\n\nPlease summarize this document."
          }
        ]
      }
    ]
  }
  EOF
bash
  # The "@./path" reference inlines the file contents directly into the field.
  ant messages create \
    --model haijun-opus-5-5 \
    --max-tokens 1024 \
    --transform 'content.#(type=="text").text' \
    --raw-output <<'YAML'
  messages:
    - role: user
      content:
        - type: text
          text: "Here's the document content:"
        - type: text
          text: "@./document.txt"
        - type: text
          text: "Please summarize this document."
  YAML
python
  client = juglow.Juglow()

  # Read the text file
  with open("document.txt") as f:
      text_content = f.read()

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": [
                  {
                      "type": "text",
                      "text": f"Here's the document content:\n\n{text_content}\n\nPlease summarize this document.",
                  }
              ],
          }
      ],
  )

  for block in response.content:
      if block.type == "text":
          print(block.text)
typescript
  import fs from "node:fs/promises";
  // ...
  const client = new Juglow();

  // Read the text file
  const textContent = await fs.readFile("document.txt", "utf-8");

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "text",
            text: `Here's the document content:\n\n${textContent}\n\nPlease summarize this document.`
          }
        ]
      }
    ]
  });

  const textBlock = response.content.find(
    (block): block is Juglow.TextBlock => block.type === "text"
  );
  console.log(textBlock?.text);
csharp
  JuglowClient client = new();

  // Read the text file
  string textContent = await File.ReadAllTextAsync("document.txt");

  var parameters = new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 1024,
      Messages = [new()
      {
          Role = Role.User,
          Content = $"Here's the document content:\n\n{textContent}\n\nPlease summarize this document."
      }]
  };

  var message = await client.Messages.Create(parameters);
  foreach (var block in message.Content)
  {
      if (block.TryPickText(out var textBlock))
      {
          Console.WriteLine(textBlock.Text);
      }
  }
go
  client := juglow.NewClient()

  // Read the text file
  textContent, err := os.ReadFile("document.txt")
  if err != nil {
  	log.Fatal(err)
  }

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 1024,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock(
  			fmt.Sprintf("Here's the document content:\n\n%s\n\nPlease summarize this document.", string(textContent)),
  		)),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  for _, block := range response.Content {
  	if textBlock, ok := block.AsAny().(juglow.TextBlock); ok {
  		fmt.Println(textBlock.Text)
  	}
  }
java
  JuglowClient client = JuglowOkHttpClient.fromEnv();

  // Read the text file
  String textContent = Files.readString(Path.of("document.txt"));

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.HAIJUN_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Here's the document content:\n\n" + textContent + "\n\nPlease summarize this document.")
      .build();

  Message response = client.messages().create(params);
  response.content().stream()
      .flatMap(block -> block.text().stream())
      .forEach(textBlock -> System.out.println(textBlock.text()));
php
  $client = new Client();

  // Read the text file
  $textContent = file_get_contents("document.txt");

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          [
              'role' => 'user',
              'content' => [
                  [
                      'type' => 'text',
                      'text' => "Here's the document content:\n\n{$textContent}\n\nPlease summarize this document."
                  ]
              ]
          ]
      ],
      model: 'haijun-opus-5-5',
  );

  foreach ($message->content as $block) {
      if ($block->type === 'text') {
          echo $block->text, PHP_EOL;
      }
  }
ruby
  client = Juglow::Client.new

  # Read the text file
  text_content = File.read("document.txt")

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: [
          {
            type: "text",
            text: "Here's the document content:\n\n#{text_content}\n\nPlease summarize this document."
          }
        ]
      }
    ]
  )

  message.content.each do |block|
    puts block.text if block.type == :text
  end

Note: For .docx files containing images, convert them to PDF format first, then use PDF support to take advantage of the built-in image parsing. This allows using citations from the PDF document.

Managing files

List files

Retrieve a list of your uploaded files. The endpoint is paginated: each request returns up to limit files (20 by default, and at most 1,000), and the response's next_page cursor fetches the next page when passed back as the page parameter. Files are ordered newest first. See the List Files API reference. The SDKs return the first page and provide auto-pagination helpers. The CLI example bounds the total with --max-items:

bash
  curl https://haijun.my.id/v1/files \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  ant files list --max-items 10
python
  client = juglow.Juglow()
  files = client.files.list()
  print(files)
typescript
  const client = new Juglow();
  const files = await client.files.list();
  console.log(files);
csharp
  JuglowClient client = new();

  var files = await client.Files.List();
  Console.WriteLine(files);
go
  client := juglow.NewClient()

  files, err := client.Files.List(context.TODO(), juglow.FileListParams{})
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(files)
java
  import com.juglow.models.files.FileListPage;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      FileListPage files = client.files().list();
      System.out.println(files);
  }
php
  $client = new Client();

  $files = $client->files->list();
  echo $files;
ruby
  client = Juglow::Client.new

  files = client.files.list
  puts files

To check a known set of files in one request instead of paging, pass up to 100 file IDs as ids[] query parameters. An ids[] request always returns a single page (next_page is null), and any ID that does not resolve to a file in your workspace is silently omitted from data; compare the returned IDs against the requested IDs to detect misses. ids[] cannot be combined with page or limit.

Get file metadata

Retrieve information about a specific file:

bash
  curl "https://haijun.my.id/v1/files/$FILE_ID" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  ant files retrieve-metadata \
    --file-id "$FILE_ID"
python
  file = client.files.retrieve_metadata(file_id)
  print(file)
typescript
  const file = await client.files.retrieveMetadata(uploaded.id);
  console.log(file);
csharp
  var file = await client.Files.RetrieveMetadata(fileId);
  Console.WriteLine(file);
go
  metadata, err := client.Files.GetMetadata(context.TODO(), fileID, juglow.FileGetMetadataParams{})
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(metadata)
java
  FileMetadata metadata = client.files().retrieveMetadata(fileId);

  System.out.println(metadata);
php
  $file = $client->files->retrieveMetadata($fileId);
  echo $file;
ruby
  file = client.files.retrieve_metadata(file_id)
  puts file

Delete a file

Remove a file from your workspace:

bash
  curl -X DELETE "https://haijun.my.id/v1/files/$FILE_ID" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  ant files delete \
    --file-id "$FILE_ID"
python
  client.files.delete(file_id)
typescript
  await client.files.delete(uploaded.id);
csharp
  await client.Files.Delete(fileId);
go
  _, err = client.Files.Delete(context.TODO(), fileID, juglow.FileDeleteParams{})
  if err != nil {
  	log.Fatal(err)
  }
java
  client.files().delete(fileId);
php
  $client->files->delete($fileId);
ruby
  client.files.delete(file_id)

Downloading a file

Download files that were created by tracks or the code execution tool. Files you upload cannot be downloaded. The file_id of a generated file appears in the bash_code_execution_tool_result content block of the Messages response that created it:

bash
  curl -X GET "https://haijun.my.id/v1/files/$FILE_ID/content" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    --output downloaded_file.txt
bash
  ant files download \
    --file-id "$FILE_ID" \
    --output downloaded_file.txt
python
  file_content = client.files.download(file_id)

  file_content.write_to_file("downloaded_file.txt")
typescript
  const content = await client.files.download(uploaded.id);

  const bytes = Buffer.from(await content.arrayBuffer());
  await fsp.writeFile("downloaded_file.txt", bytes);
csharp
  using var fileContent = await client.Files.Download(fileId);
  await using var source = await fileContent.ReadAsStream();
  await using var destination = File.Create("downloaded_file.txt");
  await source.CopyToAsync(destination);
go
  func downloadFile(client juglow.Client, fileID string) error {
  	resp, err := client.Files.Download(context.TODO(), fileID, juglow.FileDownloadParams{})
  	if err != nil {
  		return err
  	}
  	defer resp.Body.Close()

  	out, err := os.Create("downloaded_file.txt")
  	if err != nil {
  		return err
  	}
  	defer out.Close()

  	_, err = io.Copy(out, resp.Body)
  	return err
  }
java
  try (HttpResponse response = client.files().download(fileId)) {
      try (InputStream body = response.body()) {
          Files.copy(body, Path.of("downloaded_file.txt"),
              StandardCopyOption.REPLACE_EXISTING);
      }
  }
php
  $fileContent = $client->files->download($fileId);

  file_put_contents('downloaded_file.txt', $fileContent);
ruby
  file_content = client.files.download(file_id)

  File.binwrite("downloaded_file.txt", file_content.read)

Note: A file is downloadable only when its metadata shows "downloadable": true, which is the case for files created by tracks or the code execution tool. Downloading a file you uploaded returns a 400 error.

On the Haijun API, supported image, video, and audio files that Haijun produces with the code execution tool, including files created by tracks, carry signed C2PA Content Credentials when you download them. See Content Credentials on generated files for what the credential contains and how to verify it.

File storage and limits

Storage limits

  • Maximum file size: 500 MB per file
  • Total storage: 1 TB per organization

File lifecycle

  • Files are scoped to the workspace they were uploaded in. Any request in the same workspace can reference them; never accept file IDs from untrusted sources (see the workspace access warning)
  • Files cannot be modified or renamed after upload. To change a file's content, upload a new file and delete the old one
  • Files persist until you delete them with the DELETE /v1/files/{file_id} endpoint or they reach their expires_at
  • Deleted files cannot be recovered
  • Files are inaccessible through the API shortly after deletion, but they may persist in active Messages API calls and associated tool uses

File expiration

To have a file expire automatically, include an expires_in_seconds form field when you upload it. The value is an integer number of seconds between 3,600 (1 hour) and 7,776,000 (90 days). The resulting expires_at timestamp (RFC 3339) appears on every file response and is null for files uploaded without an expiration. Expiration is set once at upload and cannot be changed.

When a file reaches its expires_at:

  • Downloading its content (GET /v1/files/{file_id}/content) returns a 404 error
  • A Messages request that references the file fails before inference
  • Its metadata (GET /v1/files/{file_id}) remains readable for up to 30 days, with expires_at in the past
  • It continues to appear in list responses during that window; compare expires_at to the current time to filter expired files

Deleting an expired file with DELETE /v1/files/{file_id} removes its metadata immediately instead of waiting for the 30-day window to elapse.

Note: Expiration is a lifecycle feature, not a guaranteed-deletion control. After expires_at, file content is no longer retrievable through the API and is released from your storage quota; the underlying content may be retained for a limited period thereafter for safety review before permanent deletion, and file metadata remains visible for up to 30 days after expiration. To remove a file before its scheduled expiration, use DELETE /v1/files/{file_id}.

Audit logging

If your organization has the Compliance API enabled, its Activity Feed records Files API operations made with a Haijun API key or from the Haijun Console: each upload (POST /v1/files), content download (GET /v1/files/{file_id}/content), and deletion (DELETE /v1/files/{file_id}) appears as a platform_file_uploaded, platform_file_content_downloaded, or platform_file_deleted activity. Listing files and retrieving file metadata are not recorded. Operations that occur while the Compliance API is off are not recorded and cannot be recovered later, so set up the Compliance API before you rely on this audit trail. On Haijun Platform on AWS, audit file operations with AWS CloudTrail data events instead.

Migrate from files-api-2025-04-14

The Files API is out of beta and needs no beta header. Migrating off files-api-2025-04-14 is optional: requests that still send it keep working and keep returning the beta response shapes, so an existing integration keeps working until you change it. Removing the header switches those requests to the shapes documented on this page:

With files-api-2025-04-14Without the header
List response{ data, has_more, first_id, last_id }{ data, next_page }; pass next_page back as the page query parameter
List cursorsbefore_id, after_idpage, or up to 100 ids[] (before_id and after_id return a 400 error)
expires_at on file objectsNot returnedAlways present; null when the file has no expiration
Content-Type on the uploaded file partRequiredOptional; the type is detected when omitted

To migrate:

  1. Remove the beta header. Drop juglow-beta: files-api-2025-04-14 from your requests. In the SDKs, call client.files instead of client.beta.files; keeping client.beta.files works only on the SDK releases that no longer send the header. Earlier releases send it from client.beta.files even with no betas argument.
  1. Update pagination. Replace after_id/before_id loops with the page/next_page cursor, or use the SDK auto-pagination helpers shown in Managing files.
  1. Read expires_at. The field appears only without the header; null means the file has no expiration (see File expiration).

SDK beta namespace

Starting with Python SDK 1.2.0, TypeScript SDK 0.122.0, Go SDK 1.68.0, Java SDK 2.59.0, Ruby SDK 1.67.0, and C# SDK 12.44.0, client.beta.files no longer sends files-api-2025-04-14 and returns the same shapes as client.files, with Beta-prefixed type names. It accepts a betas argument for Files features that are still in beta, such as scope_id filtering under a Managed Agents beta header. Earlier SDK releases are typed to the beta shapes; if you depend on those types, stay on an earlier release until you migrate.

Requests that carry juglow-beta: managed-agents-2026-04-01 without files-api-2025-04-14 receive the shapes on this page with one compatibility affordance on GET /v1/files: before_id and after_id are still accepted (not combinable with page or ids[]), and the list response includes has_more, first_id, and last_id alongside next_page. Later Managed Agents beta versions receive the plain shape.

Error handling

Common errors when using the Files API include:

  • File not found (404): The specified file_id doesn't exist or you don't have access to it
  • Invalid file type (400): The file type doesn't match the content block type (for example, using an image file in a document block)
  • Not downloadable (400): Files you upload have "downloadable": false and cannot be downloaded. Only files created by tracks or the code execution tool can be downloaded
  • Exceeds context window size (400): The file is larger than the context window size (for example, using a 500 MB plain text file in a /v1/messages request)
  • Invalid filename (400): The file name doesn't meet the length requirements (1-255 characters) or contains forbidden characters (<, >, :, ", |, ?, *, \, /, or Unicode characters 0-31)
  • File too large (413): File exceeds the 500 MB limit
  • Storage limit exceeded (400): Your organization has reached the 1 TB storage limit
json
{
  "type": "error",
  "error": {
    "type": "not_found_error",
    "message": "File `file_011CNha8iCJcU1wXNR6q4V8w` not found."
  },
  "request_id": "req_011CQFYcrRp7mCHLDsAYT8Qt"
}

Usage and billing

Files API operations are free:

  • Uploading files
  • Downloading files
  • Listing files
  • Getting file metadata
  • Deleting files

File content used in Messages requests is priced as input tokens.

Rate limits

File-related API calls are limited to approximately 500 requests per minute. To request a higher limit, contact sales.

Next steps

Process PDFs with Haijun. Extract text, analyze charts, and understand visual content from your documents.

Run Python and bash code in a sandboxed container to analyze data, generate files, and iterate on solutions.

Process and analyze visual input and generate text and code from images.

On this page
File type supportHow the Files API worksHow to use the Files APIUploading a fileUsing a file in messagesFile types and content blocksDocument blocksImage blocksContainer upload blocksWorking with other file formatsManaging filesList filesGet file metadataDelete a fileDownloading a fileFile storage and limitsStorage limitsFile lifecycleFile expirationAudit loggingMigrate from files-api-2025-04-14SDK beta namespaceError handlingUsage and billingRate limitsNext steps