> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mithunai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload files for a knowledge collection

> The only knowledge endpoint taking multipart/form-data. Uploading stores the files and adds nothing yet; post the returned configuration as an upload source.

This is the only knowledge endpoint that takes `multipart/form-data`. Uploading stores the files and adds nothing to the collection yet. To ingest them, post the response's `configuration` object unchanged as the `configuration` of an `upload` source with [Create a source](/api-reference/knowledge/create-source), then [sync that source](/api-reference/knowledge/sync-source). See [File uploads](/knowledge/file-uploads).

The whole batch is checked before anything is stored. If any single file is unacceptable, the request returns `400` and no file is stored, so a `201` means every file was accepted.

Limits:

* The whole request body is at most 64 MiB. A request that declares a larger `Content-Length` returns `400`. A body that exceeds the limit while it is being read is cut off with `413`.
* At most 1,000 files per request.
* Each file must be non-empty and within your deployment's per-document size limit, which is 1 MiB by default.
* The files together must be within your deployment's per-source byte limit, which is 256 MiB by default, so by default the 64 MiB request limit is the one you reach first.

Each filename must:

* be at most 255 characters
* be a plain relative name: no leading `/`, no `\`, no drive letter, no control characters, no `.` or `..` segments, no empty segments, no segment that starts or ends with a space or ends with `.`, at most 32 segments, no `|`, and not a reserved device name such as `CON`
* end in a supported extension:

| Content type        | Extensions                                                                                                                                                                                            |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `markdown`          | `.md`, `.markdown`, `.mdx`                                                                                                                                                                            |
| `plain_text`        | `.txt`, `.text`                                                                                                                                                                                       |
| `restructured_text` | `.rst`                                                                                                                                                                                                |
| `html`              | `.html`, `.htm`                                                                                                                                                                                       |
| `pdf`               | `.pdf`                                                                                                                                                                                                |
| `docx`              | `.docx`                                                                                                                                                                                               |
| `source_code`       | `.py`, `.ts`, `.tsx`, `.js`, `.jsx`, `.go`, `.rs`, `.java`, `.rb`, `.php`, `.cs`, `.kt`, `.swift`, `.c`, `.h`, `.cpp`, `.hpp`, `.sh`, `.sql`, `.yaml`, `.yml`, `.toml`, `.json`, `.proto`, `.graphql` |

A file with no extension is accepted only when it is named `README`, `LICENSE`, `NOTICE`, `AUTHORS`, `CHANGELOG` or `CONTRIBUTING` (read as `plain_text`), or `Dockerfile` or `Makefile` (read as `source_code`). Extensions and names are matched without regard to case. The type is decided by the filename alone, not by the file's content. Legacy `.doc` files, spreadsheets and slide decks are not supported.

Uploading requires a role that can manage knowledge. A read-only member receives `403`. A collection that is not in your organization returns `404`.

<ParamField body="collection_id" type="string" required>
  Form field. ID (a UUID) of the collection the files are for.
</ParamField>

<ParamField body="file" type="file" required>
  Form field. One file. Repeat the field to upload several files in one request.
</ParamField>

## Response

Returns `201`.

<ResponseField name="data" type="object[]" required>
  The accepted files, in the order they were sent.

  <Expandable title="properties">
    <ResponseField name="storage_key" type="string" required>
      Key the file is stored under. It only works in an upload source in the same organization.
    </ResponseField>

    <ResponseField name="filename" type="string" required>
      The filename as stored, after normalization.
    </ResponseField>

    <ResponseField name="content_type" type="string" required>
      Content type implied by the extension: `markdown`, `plain_text`, `restructured_text`,
      `source_code`, `html`, `pdf` or `docx`.
    </ResponseField>

    <ResponseField name="size_bytes" type="integer" required>
      Size of the file in bytes.
    </ResponseField>

    <ResponseField name="content_hash" type="string" required>
      SHA-256 of the file's bytes, as 64 lowercase hex characters.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total" type="integer" required>
  Number of files accepted.
</ResponseField>

<ResponseField name="configuration" type="object" required>
  Ready-made configuration for an `upload` source. Post it back unchanged.

  <Expandable title="properties">
    <ResponseField name="files" type="string" required>
      The manifest: one `storage_key|filename` entry per file, separated by newlines.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --request POST "$MITHUNAI_URL/arukz/api/v1/knowledge/uploads" \
    --header "Authorization: Bearer $MITHUNAI_API_KEY" \
    -F "collection_id=0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88" \
    -F "file=@handbook.pdf" \
    -F "file=@runbook.md"
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import os, requests

  with open("handbook.pdf", "rb") as handbook, open("runbook.md", "rb") as runbook:
      response = requests.post(
          f"{os.environ['MITHUNAI_URL']}/arukz/api/v1/knowledge/uploads",
          headers={"Authorization": f"Bearer {os.environ['MITHUNAI_API_KEY']}"},
          data={"collection_id": "0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88"},
          files=[
              ("file", ("handbook.pdf", handbook, "application/pdf")),
              ("file", ("runbook.md", runbook, "text/markdown")),
          ],
          timeout=300,
      )
  response.raise_for_status()
  upload = response.json()

  # Then create the upload source from the returned configuration.
  source = requests.post(
      f"{os.environ['MITHUNAI_URL']}/arukz/api/v1/knowledge/sources",
      headers={"Authorization": f"Bearer {os.environ['MITHUNAI_API_KEY']}"},
      json={
          "collection_id": "0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88",
          "source_type": "upload",
          "name": "Operations handbooks",
          "configuration": upload["configuration"],
      },
      timeout=60,
  )
  source.raise_for_status()
  print(source.json())
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { openAsBlob } from 'node:fs'

  const form = new FormData()
  form.append('collection_id', '0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88')
  form.append('file', await openAsBlob('handbook.pdf'), 'handbook.pdf')
  form.append('file', await openAsBlob('runbook.md'), 'runbook.md')

  const response = await fetch(`${process.env.MITHUNAI_URL}/arukz/api/v1/knowledge/uploads`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.MITHUNAI_API_KEY}` },
    body: form,
  })
  const upload = await response.json()
  console.log(upload)
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "data": [
      {
        "storage_key": "arukz/knowledge/uploads/3f8b2c6d-1e4a-4b9f-8c7d-2a5e6f0b1c93/b7e41f0c9a2d4e6f8a1b3c5d7e9f0a2b",
        "filename": "handbook.pdf",
        "content_type": "pdf",
        "size_bytes": 918273,
        "content_hash": "5f2b8c1e9d4a7f3b6e0c2d8a1f5b9e3c7d0a4f8b2e6c1d9a5f3b7e0c4d8a2f6b"
      },
      {
        "storage_key": "arukz/knowledge/uploads/3f8b2c6d-1e4a-4b9f-8c7d-2a5e6f0b1c93/0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f",
        "filename": "runbook.md",
        "content_type": "markdown",
        "size_bytes": 4211,
        "content_hash": "a3c7e1f5b9d2a6e0c4f8b2d6a0e4c8f2b6d0a4e8c2f6b0d4a8e2c6f0b4d8a2e6"
      }
    ],
    "total": 2,
    "configuration": {
      "files": "arukz/knowledge/uploads/3f8b2c6d-1e4a-4b9f-8c7d-2a5e6f0b1c93/b7e41f0c9a2d4e6f8a1b3c5d7e9f0a2b|handbook.pdf\narukz/knowledge/uploads/3f8b2c6d-1e4a-4b9f-8c7d-2a5e6f0b1c93/0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f|runbook.md"
    }
  }
  ```

  ```json 400 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "validation_error", "message": "That kind of file cannot be ingested." }
  ```

  ```json 403 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "authorization_error", "message": "You do not have permission to perform this action." }
  ```

  ```json 404 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "not_found", "message": "The knowledge collection was not found." }
  ```

  ```json 503 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "dependency_failure", "message": "The uploaded file could not be stored." }
  ```
</ResponseExample>
