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

# Knowledge Base File Upload via the Wolfia API

> Add documents to your Wolfia knowledge base programmatically in three steps: prepare uploads, POST files to returned URLs, and confirm to kick off processing.

## Overview

Add documents to your Wolfia knowledge base programmatically. The upload flow has three steps: prepare the upload, upload the files using the returned URLs, and confirm to start processing.

## How it works

<Steps>
  <Step title="Prepare">
    Send file metadata to get upload URLs for each file.
  </Step>

  <Step title="Upload">
    Upload each file to the returned URL using a multipart form POST.
  </Step>

  <Step title="Confirm">
    Send the source IDs back to confirm and start processing.
  </Step>
</Steps>

## Prepare upload

**URL:** `POST https://api.wolfia.com/v1/content-source/prepare-upload`

**Authentication:** API key required (see [API overview](/how-to/api-overview) for setup)

```bash theme={null}
curl -X POST https://api.wolfia.com/v1/content-source/prepare-upload \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "files": [
      {
        "name": "security-policy.pdf",
        "size": 245760,
        "type": "application/pdf",
        "last_modified": 1700000000
      }
    ]
  }'
```

### Request parameters

| Parameter                    | Type           | Required | Description                                      |
| ---------------------------- | -------------- | -------- | ------------------------------------------------ |
| `files`                      | array          | Yes      | Array of file metadata objects                   |
| `files[].name`               | string         | Yes      | Filename including extension                     |
| `files[].size`               | integer        | Yes      | File size in bytes                               |
| `files[].type`               | string         | Yes      | MIME type                                        |
| `files[].last_modified`      | integer        | Yes      | Last modified timestamp (Unix seconds)           |
| `files[].context`            | string         | No       | Description to help AI understand the document   |
| `files[].is_public_override` | boolean        | No       | Set `true` to make the document publicly visible |
| `files[].tag_ids`            | array of UUIDs | No       | Tags to associate with the file                  |

### Supported file types

| Extension | MIME type (`files[].type`)                                                  |
| --------- | --------------------------------------------------------------------------- |
| `.pdf`    | `application/pdf`                                                           |
| `.docx`   | `application/vnd.openxmlformats-officedocument.wordprocessingml.document`   |
| `.doc`    | `application/msword`                                                        |
| `.md`     | `text/markdown`                                                             |
| `.pptx`   | `application/vnd.openxmlformats-officedocument.presentationml.presentation` |
| `.xlsx`   | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`         |
| `.xlsm`   | `application/vnd.ms-excel.sheet.macroenabled.12`                            |
| `.xls`    | `application/vnd.ms-excel`                                                  |
| `.csv`    | `text/csv`                                                                  |
| `.txt`    | `text/plain`                                                                |
| `.json`   | `application/json`                                                          |
| `.html`   | `text/html`                                                                 |
| `.png`    | `image/png`                                                                 |
| `.jpg`    | `image/jpeg`                                                                |
| `.webp`   | `image/webp`                                                                |
| `.gif`    | `image/gif`                                                                 |
| `.eml`    | `message/rfc822`                                                            |

### Response (200 OK)

Returns an upload URL and form fields for each file. Use these to upload the file content directly.

```json theme={null}
{
  "upload_urls": [
    {
      "source_id": "28c262e0-cfa4-442b-b193-21f94f200298",
      "upload_url": "https://storage.wolfia.com/",
      "fields": {
        "Content-Type": "application/pdf",
        "key": "...",
        "AWSAccessKeyId": "...",
        "policy": "...",
        "signature": "..."
      }
    }
  ]
}
```

| Field                      | Type          | Description                                 |
| -------------------------- | ------------- | ------------------------------------------- |
| `upload_urls[].source_id`  | string (UUID) | ID to use in the confirm step               |
| `upload_urls[].upload_url` | string        | URL to POST the file to                     |
| `upload_urls[].fields`     | object        | Form fields to include with the file upload |

## Upload files

Use the `upload_url` and `fields` from the prepare response. Include all `fields` as form fields, then append the file as the last field.

```bash theme={null}
curl -X POST "UPLOAD_URL_FROM_PREPARE_RESPONSE" \
  -F "Content-Type=application/pdf" \
  -F "key=..." \
  -F "AWSAccessKeyId=..." \
  -F "policy=..." \
  -F "signature=..." \
  -F "file=@security-policy.pdf"
```

<Warning>
  The `file` field must be the **last field** in the multipart form data.
</Warning>

## Confirm upload

**URL:** `POST https://api.wolfia.com/v1/content-source/confirm-upload`

**Authentication:** API key required (see [API overview](/how-to/api-overview) for setup)

```bash theme={null}
curl -X POST https://api.wolfia.com/v1/content-source/confirm-upload \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "source_ids": ["28c262e0-cfa4-442b-b193-21f94f200298"]
  }'
```

### Request parameters

| Parameter    | Type             | Required | Description                      |
| ------------ | ---------------- | -------- | -------------------------------- |
| `source_ids` | array of strings | Yes      | Source IDs from the prepare step |

### Response (200 OK)

```json theme={null}
{
  "message": "1 file(s) have been confirmed and queued for processing."
}
```

## Integration example

### Python: Upload a directory of documents

```python theme={null}
import os
import mimetypes
from pathlib import Path

import requests

WOLFIA_API_KEY = os.environ["WOLFIA_API_KEY"]
BASE_URL = "https://api.wolfia.com/v1"
HEADERS = {"X-API-Key": WOLFIA_API_KEY, "Content-Type": "application/json"}

SUPPORTED = {".pdf", ".docx", ".doc", ".md", ".pptx", ".ppt", ".xlsx", ".png", ".jpg", ".webp", ".gif"}


def upload_documents(directory: str):
    files = [f for f in Path(directory).iterdir() if f.suffix.lower() in SUPPORTED]
    if not files:
        print("No supported files found.")
        return

    # 1. Prepare
    metadata = [
        {
            "name": f.name,
            "size": f.stat().st_size,
            "type": mimetypes.guess_type(f.name)[0] or "application/octet-stream",
            "last_modified": int(f.stat().st_mtime),
        }
        for f in files
    ]

    resp = requests.post(f"{BASE_URL}/content-source/prepare-upload", headers=HEADERS, json={"files": metadata}, timeout=30)
    resp.raise_for_status()
    upload_urls = resp.json()["upload_urls"]

    # 2. Upload each file
    confirmed_ids = []
    for file_path, upload in zip(files, upload_urls):
        with open(file_path, "rb") as fh:
            form_fields = list(upload["fields"].items()) + [("file", (file_path.name, fh))]
            r = requests.post(upload["upload_url"], files=form_fields, timeout=120)

        if r.status_code in (200, 204):
            confirmed_ids.append(upload["source_id"])
            print(f"  Uploaded {file_path.name}")
        else:
            print(f"  Failed {file_path.name}: HTTP {r.status_code}")

    if not confirmed_ids:
        print("No files uploaded successfully.")
        return

    # 3. Confirm
    resp = requests.post(f"{BASE_URL}/content-source/confirm-upload", headers=HEADERS, json={"source_ids": confirmed_ids}, timeout=30)
    resp.raise_for_status()
    print(resp.json()["message"])


if __name__ == "__main__":
    upload_documents("/path/to/documents")
```

## Error responses

| Status Code | What it means                                 | How to fix                                                                                      |
| ----------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| 400         | No files provided or no valid files confirmed | Check your request body has a non-empty `files` array (prepare) or valid `source_ids` (confirm) |
| 403         | Insufficient permissions                      | API key owner needs Expert or Admin role                                                        |
| 422         | Invalid request parameters                    | Check required fields and data types                                                            |
| 500         | Internal error                                | Retry with exponential backoff                                                                  |

## Best practices

<AccordionGroup>
  <Accordion title="Batch files in a single request">
    Send multiple files in one prepare call to reduce round trips.
  </Accordion>

  <Accordion title="Handle partial failures">
    If some files fail during upload, confirm only the successful ones. The confirm endpoint processes each ID independently.
  </Accordion>

  <Accordion title="Add context for better results">
    Use the `context` field to describe what the document covers. This helps Wolfia extract more relevant knowledge.

    ```json theme={null}
    {
      "name": "soc2-report-2024.pdf",
      "size": 512000,
      "type": "application/pdf",
      "last_modified": 1700000000,
      "context": "SOC 2 Type II audit report for FY2024"
    }
    ```
  </Accordion>
</AccordionGroup>

## Getting help

* **Check permissions:** API key owner needs Expert or Admin role
* **Verify file types:** Use a supported extension and correct MIME type
* **Contact support:** Email [support@wolfia.com](mailto:support@wolfia.com)

## Next steps

<CardGroup cols={2}>
  <Card title="API overview" icon="code" href="/how-to/api-overview">
    Authentication, rate limits, and error handling
  </Card>

  <Card title="Questionnaires" icon="file-circle-question" href="/how-to/api-questionnaires">
    Upload, list, search, and track questionnaires
  </Card>
</CardGroup>
