> 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/maiagent-tech-en/api-integration/api_knowledge.md).

# Presigned File Upload Mode

There are two places where file uploads are required on MaiAgent:

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 (like S3) without going through the server. In this mode, the server only generates time-sensitive 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 before being uploaded to cloud storage.
   * Presigned Mode: Files are uploaded directly from client to cloud, avoiding server file processing overhead.
2. **Performance and Cost**
   * Traditional mode can lead to high 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 Using Presigned URL
    Note right of Client: POST to Presigned URL<br/>Send all fields entries + file binary
    S3-->>-Client: 6. Upload Complete Response (HTTP 204)
    
    opt Knowledge Base scenario: register file to KB
        Client->>+Server: 7. Register with fields.key
        Note right of Client: POST /api/v1/knowledge-bases/{KB_ID}/files/<br/>body: 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

Below describes how to complete the Presigned upload process using MaiAgent's provided API. **For the Knowledge Base scenario**, you need all 3 steps (Step 1 → 2 → 3). For message attachments, only Step 1 → 2 are needed.

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

**Endpoint**

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

**Description**

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

**Request Parameters**

| Parameter Name | Type      | Required | Description                                                                                                                                                         |
| -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filename`     | `string`  | Yes      | Name of the upload file                                                                                                                                             |
| `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 match actual binary size exactly, 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`). The `fields` object in the response \*\*includes the \*\***`x-amz-security-token`** field. When uploading in Step 2, you must pass every single `fields` entry (including `x-amz-security-token`) — missing any will cause S3 to reject the request.

**Presigned URLs expire after \~1 hour** — use them immediately after obtaining.
{% endhint %}

***

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

**Description**

Use the `url` and `fields` obtained from the previous step to upload files directly to cloud storage. **Every entry in the `fields` object must be sent** (including `x-amz-security-token`). Do not hardcode the field list — dynamic packing avoids missing newly-added fields in the future.

**On success, S3 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 packing, recommended)**

```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 the entire fields dict — all entries (including x-amz-security-token) auto-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 to use in Step 3
```

***

#### 3. **Register the File to a Knowledge Base (KB scenario only)**

**Endpoint**

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

**Description**

After Step 2, the file binary is in S3 but **not yet associated with a Knowledge Base**. You need to call this API to register the upload as a `KnowledgeBaseFile`, which triggers parsing, chunking, and indexing.

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

**Path Parameters**

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

**Request Parameters**

| Parameter Name                  | Type     | Required | Description                                                        |
| ------------------------------- | -------- | -------- | ------------------------------------------------------------------ |
| `files`                         | `array`  | Yes      | List of files to create; supports batch creation                   |
| `files[].filename`              | `string` | Yes      | Original file name                                                 |
| `files[].file`                  | `string` | Yes      | The `fields.key` from Step 1 response (relative path, **not URL**) |
| `files[].parser`                | `string` | No       | Parser UUID; uses KB default if omitted                            |
| `files[].labels`                | `array`  | No       | File labels (array of {id, name} objects)                          |
| `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, waiting to be processed        |
| `processing` | Parsing, chunking, and indexing in progress  |
| `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 `x-amz-security-token`, or you've hardcoded an expired access key / outdated S3 endpoint. Always use the `url` and `fields` returned by Step 1, and pass the entire `fields` object as-is to Step 2.

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

A: Usually the `fileSize` in Step 1 doesn't match the actual binary size. Recalculate the actual file size and re-run Step 1.

**Q: Step 3 returns 400 complaining about the `file` field**

A: The `file` field must be the `fields.key` returned by Step 1 (e.g., `media/chatbots/chatbot-file/xxx.pdf`), not Step 3's response full URL, nor an arbitrary external URL.

**Q: I already have an external URL (e.g., a partner's download link) — can I pass it directly to Step 3?**

A: No. Step 3's `file` field is a MaiAgent S3 relative key. First `GET` to download the file from that external URL, then go through Step 1 → 2 → 3.

**Q: File stays in `status: processing` after Step 3**

A: Parsing time depends on file size and type — large files may take several minutes. Poll with `GET /api/v1/knowledge-bases/{KB_ID}/files/{file_id}/` to check status; only files with `status: done` are retrievable by AI assistants.


---

# 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/maiagent-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.
