> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/en/api-integration/api_knowledge.md).

# Presigned File Upload Mode

There are currently two places on MaiAgent that require file uploads:

1. Knowledge Base Document Upload
2. Message Attachment Upload

## Presigned Upload Mode

MaiAgent supports **Presigned Upload Mode**, which allows clients to upload files directly to cloud storage (such as S3) without going through the server as a relay. In this mode, the server is only responsible for generating time-limited and secure Presigned URLs, and clients use these URLs to upload files directly.

#### Differences from Traditional Upload Mode:

1. **Data Flow**
   * Traditional Mode: Files must pass through the server, which then uploads them to cloud storage.
   * Presigned Mode: Files are uploaded directly from the client to cloud storage, avoiding the server's file processing overhead.
2. **Performance and Cost**
   * Traditional mode can lead to excessive server resource consumption and increased data transfer costs.
   * Presigned mode reduces server load, improves performance, and lowers transfer costs.
3. **Security**
   * Presigned URLs include signatures and expiration times, ensuring upload requests are limited to authorized users within specified timeframes.

This mode is particularly suitable for large files or high-frequency upload scenarios, improving efficiency while maintaining high security.

#### Presigned Upload Flow Diagram

```mermaid
sequenceDiagram
    participant Client as Client
    participant Server as Sever
    participant S3 as Storage(S3)

    Note over Client,S3: Presigned Upload Flow
    
    Client->>+Server: 1. Request Presigned URL
    Note right of Client: POST /api/v1/upload-presigned-url/<br/>Include filename, type, size
    
    Server->>Server: 2. Validate Request Parameters
    Server->>Server: 3. Generate Presigned Parameters
    Note right of Server: Set expiration, permissions, etc.
    Server-->>-Client: 4. Return Presigned URL and Parameters
    
    Client->>+S3: 5. Upload Directly Using Presigned URL
    Note right of Client: POST to Presigned URL<br/>Include all fields + file binary
    S3-->>-Client: 6. Upload Complete Response (HTTP 204)
    
    opt Knowledge Base Scenario: Register File to Knowledge Base
        Client->>+Server: 7. Register Using fields.key
        Note right of Client: POST /api/v1/knowledge-bases/{KB_ID}/files/<br/>body includes file = fields.key
        Server->>Server: 8. Create KnowledgeBaseFile and Schedule Parsing
        Server-->>-Client: 9. Return file record
    end
```

#### Traditional Upload Flow Diagram

```mermaid
sequenceDiagram
    participant Client as Client
    participant Server as Server
    participant S3 as Storage(S3)

    Note over Client,S3: Traditional Upload Flow
    
    Client->>+Server: 1. Upload File Request
    Note right of Client: POST /api/v1/upload<br/>File content included in request
    
    Server->>Server: 2. Validate File<br/>(size, type, format)
    Server->>Server: 3. Store Temporary File
    
    Server->>+S3: 4. Upload File to S3
    Note right of Server: Use AWS SDK<br/>Upload temp file
    S3-->>-Server: 5. Upload Success Response
    
    Server->>Server: 6. Clean Up Temp File
    Server-->>-Client: 7. Return Upload Result
    Note left of Server: Return S3 file location
```

***

#### Presigned File Upload

The following describes how to complete the Presigned file upload process using MaiAgent's API. **Knowledge base scenarios** require all 3 steps (Step 1 → 2 → 3), while message attachment scenarios only require Step 1 → 2.

#### 1. **Get Presigned URL**

**Endpoint**

`POST https://api.maiagent.ai/api/v1/upload-presigned-url/`

**Description**

The client sends a request to the server to obtain a Presigned URL for directly uploading files to cloud storage.

**Request Parameters**

| Parameter   | Type      | Required | Description                                                                                                                                                         |
| ----------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filename`  | `string`  | Yes      | Name of the file to upload                                                                                                                                          |
| `modelName` | `string`  | Yes      | <p>Module name for categorizing file usage<br></p><p>Use <code>chatbot-file</code> for knowledge base</p><p>Use <code>attachment</code> for message attachments</p> |
| `fieldName` | `string`  | Yes      | File field name for identifying file purpose                                                                                                                        |
| `fileSize`  | `integer` | Yes      | File size (in bytes); must exactly match the actual binary size, otherwise Step 2 will be rejected by S3                                                            |

**Example Request**

```bash
curl --location 'https://api.maiagent.ai/api/v1/upload-presigned-url/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "filename": "document.pdf",
    "modelName": "chatbot-file",
    "fieldName": "file",
    "fileSize": 178329
}'
```

**Example Response**

```json
{
    "url": "https://s3.ap-northeast-1.amazonaws.com/whizchat-media-prod-django.playma.app",
    "fields": {
        "key": "media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf",
        "x-amz-algorithm": "AWS4-HMAC-SHA256",
        "x-amz-credential": "ASIATIVCN4X5XXXXXXXX/20260522/ap-northeast-1/s3/aws4_request",
        "x-amz-date": "20260522T020727Z",
        "x-amz-security-token": "IQoJb3JpZ2luX2VjEEoa...(long STS token)",
        "policy": "eyJleHBpcmF0aW9uIjog...(base64 policy)",
        "x-amz-signature": "617e10d1db034ab351d3735de5c56287ecc926dbfe5b5884d2ce67deed363004"
    }
}
```

{% hint style="warning" %}
**The production environment uses AWS STS short-lived credentials** (`x-amz-credential` starts with `ASIA`), and the response `fields` object **will include the `x-amz-security-token`** field. When uploading in Step 2, you must include every single `fields` field (including `x-amz-security-token`) without omission — missing any field will cause S3 to reject the request.

The Presigned URL **is valid for approximately 1 hour**. Use it as soon as possible after obtaining it.
{% endhint %}

***

#### 2. **Upload File to Cloud Storage**

**Description**

Use the `url` and `fields` obtained from the previous step to upload the file directly to cloud storage. **Every field in the `fields` object must be included** (including `x-amz-security-token`); do not hardcode the field list — dynamically assembling ensures no fields are missed if AWS adds new ones in the future.

**A successful S3 upload returns HTTP 204 No Content (empty body).**

**Example Request (curl, with STS token)**

```bash
curl --location 'https://s3.ap-northeast-1.amazonaws.com/whizchat-media-prod-django.playma.app' \
--form 'key="media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf"' \
--form 'x-amz-algorithm="AWS4-HMAC-SHA256"' \
--form 'x-amz-credential="ASIATIVCN4X5XXXXXXXX/20260522/ap-northeast-1/s3/aws4_request"' \
--form 'x-amz-date="20260522T020727Z"' \
--form 'x-amz-security-token="IQoJb3JpZ2luX2VjEEoa..."' \
--form 'policy="eyJleHBpcmF0aW9uIjog..."' \
--form 'x-amz-signature="617e10d1db034ab351d3735de5c56287ecc926dbfe5b5884d2ce67deed363004"' \
--form 'file=@"/path/to/document.pdf"'
```

**Example Request (Python, dynamic assembly, recommended approach)**

```python
import requests

# Step 1: Get Presigned URL
presign = requests.post(
    'https://api.maiagent.ai/api/v1/upload-presigned-url/',
    headers={'Authorization': 'Api-Key YOUR_API_KEY'},
    json={
        'modelName': 'chatbot-file',
        'fieldName': 'file',
        'filename': 'document.pdf',
        'fileSize': 178329,
    },
).json()

# Step 2: Pass all fields — all fields (including x-amz-security-token) are automatically included
with open('/path/to/document.pdf', 'rb') as f:
    s3_response = requests.post(
        presign['url'],
        data=presign['fields'],
        files={'file': f},
    )
assert s3_response.status_code == 204, s3_response.text

file_key = presign['fields']['key']  # Value needed for Step 3
```

***

#### 3. **Register File to Knowledge Base (Knowledge Base Scenario Only)**

**Endpoint**

`POST https://api.maiagent.ai/api/v1/knowledge-bases/{knowledgeBasePk}/files/`

**Description**

After Step 2 is complete, the file binary is already in S3, but **it has not yet been added to the knowledge base**. You need to call this API to register the upload result as a `KnowledgeBaseFile`, and only then will the system begin parsing, chunking, and building the index.

{% hint style="warning" %}
**The `file` field must contain the `fields.key` from the Step 1 response (the MaiAgent S3 internal relative path), not an arbitrary external URL**. If you have an external URL (e.g., a download link from a partner system), first download the file using `GET`, then follow Step 1 → 2 → 3.
{% endhint %}

**Path Parameters**

| Parameter         | Type     | Description                                    |
| ----------------- | -------- | ---------------------------------------------- |
| `knowledgeBasePk` | `string` | Unique identifier (UUID) of the knowledge base |

**Request Parameters**

| Parameter                       | Type     | Required | Description                                                              |
| ------------------------------- | -------- | -------- | ------------------------------------------------------------------------ |
| `files`                         | `array`  | Yes      | List of files to create; multiple files can be batch-created at once     |
| `files[].filename`              | `string` | Yes      | Original filename                                                        |
| `files[].file`                  | `string` | Yes      | `fields.key` from the Step 1 response (relative path, **not a URL**)     |
| `files[].parser`                | `string` | No       | UUID of the specified parser; uses the knowledge base default if omitted |
| `files[].labels`                | `array`  | No       | File labels ({id, name} object array)                                    |
| `files[].rawUserDefineMetadata` | `object` | No       | User-defined metadata                                                    |

**Example Request**

```bash
curl --location 'https://api.maiagent.ai/api/v1/knowledge-bases/86401a64-ad89-4847-a709-f4ccfa0af7b9/files/' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
    "files": [
        {
            "filename": "document.pdf",
            "file": "media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf"
        }
    ]
}'
```

**Example Response**

```json
[
    {
        "id": "86572e41-16ba-45dd-b049-28c69f77ffb0",
        "filename": "document.pdf",
        "file": "https://media.maiagent.ai/media/chatbots/chatbot-file/86572e41-16ba-45dd-b049-28c69f77ffb0.pdf",
        "fileType": "pdf",
        "knowledgeBase": {
            "id": "86401a64-ad89-4847-a709-f4ccfa0af7b9",
            "name": "My Knowledge Base"
        },
        "size": 178329,
        "status": "initial",
        "parser": {
            "id": "535c5b86-0534-4d0a-abfe-82f3d37e769a",
            "name": "MaiAgent Parser",
            "provider": "maiagent",
            "order": 0,
            "supportsDiarization": false
        },
        "labels": [],
        "rawUserDefineMetadata": {},
        "createdAt": "1779425272000"
    }
]
```

**File Status (`status`)**

| Value        | Description                                     |
| ------------ | ----------------------------------------------- |
| `initial`    | Just created, awaiting processing               |
| `processing` | Currently parsing, chunking, and building index |
| `done`       | Processing complete, available for retrieval    |
| `failed`     | Processing failed                               |

***

## Common Errors and Troubleshooting

**Q: S3 returns `The AWS Access Key Id you provided does not exist in our records`**

A: Step 2 is missing the `x-amz-security-token`, or you hardcoded an expired access key / S3 endpoint. Always use the `url` and `fields` returned in real-time from Step 1, and pass the entire `fields` object as-is to Step 2.

**Q: S3 returns `Policy Condition failed`**

A: This is usually because the `fileSize` in Step 1 does not match the actual binary size. Recalculate the actual file size and re-run Step 1.

**Q: Step 3 returns 400, saying the `file` field is invalid**

A: The `file` field must contain the `fields.key` from the Step 1 response (e.g., `media/chatbots/chatbot-file/xxx.pdf`), not the full URL from the Step 3 response, nor any arbitrary external URL.

**Q: I already have an external URL (e.g., a download link from a partner system). Can I pass it directly to Step 3?**

A: No. The `file` field in Step 3 is a relative key within MaiAgent's own S3. First download the file from that external URL using `GET`, then follow Step 1 → 2 → 3.

**Q: After Step 3, the file stays in `status: processing`**

A: Parsing time depends on file size and type; large files may take several minutes. You can poll the status using `GET /api/v1/knowledge-bases/{KB_ID}/files/{file_id}/` and wait until it reaches `done` before the AI assistant can retrieve it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.maiagent.ai/tech/en/api-integration/api_knowledge.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
