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

# Upload knowledge documents

> Send files of up to 50 MB to a workspace Knowledge Base through a presigned S3 upload, follow their processing, and add text and URL documents.

This guide shows you how to add documents to a workspace Knowledge Base, the private knowledge Semji uses to ground its AI generations. Files go straight from your machine to storage through a presigned S3 form, so their bytes never travel through the Semji API. The flow has three steps:

1. **Create the document.** Semji creates it in `pending` status and returns the S3 form.
2. **Send the file** to S3 with that form. Processing starts by itself.
3. **Poll the document** until it is `success` or `failed`.

## Prerequisites

* An API key with access to the workspace. See [Authentication](https://help.semji.com/api-reference/authentication.md).
* The **workspace ID**. Get it from [`GET /v1/workspaces`](https://help.semji.com/api-reference/workspaces/list-workspaces).
* A user who is an owner or a member of the workspace, on a workspace with the V2 knowledge base and direct upload enabled. Guest writers cannot manage knowledge base files. Otherwise creating a file document returns a `403`.

## 1. Create the document

Call `POST /v1/workspaces/{workspaceId}/knowledge-documents` with the display name, the original file name (extension included) and the size in bytes. `knowledgeFolderId` and `replaces` are optional. Only PDF, CSV, TXT, XLSX and DOCX files are accepted: any other extension returns `400` with the `knowledge_document_upload_type_not_allowed` error code. An invalid `replaces` (a document that is not live, or from another workspace or page) returns `422` with `knowledge_document_replacement_invalid`.

```bash cURL theme={null}
curl -X POST https://api.semji.com/v1/workspaces/$WORKSPACE_ID/knowledge-documents \
  -H "Authorization: Bearer $SEMJI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Brand guidelines 2026",
    "fileName": "brand-guidelines.pdf",
    "size": 4823911
  }'
```

The response is `201 Created`:

```json title="201 Created" theme={null}
{
  "id": "7c4a1f08b29d",
  "name": "Brand guidelines 2026",
  "type": "file",
  "status": "pending",
  "failureReason": null,
  "knowledgeFolderId": null,
  "pageId": null,
  "summary": null,
  "url": null,
  "createdAt": "2026-11-05T09:12:44+00:00",
  "workspace": { "id": "e19b3a7d4c60", "name": "Product" },
  "upload": {
    "url": "https://s3.eu-west-1.amazonaws.com/example-bucket",
    "fields": {
      "key": "e19b3a7d4c60/7c4a1f08b29d/brand-guidelines.pdf",
      "policy": "eyJleHBpcmF0aW9uIjoi...",
      "x-amz-signature": "9f2c..."
    }
  }
}
```

To attach the document to a page instead of the workspace, use `POST /v1/pages/{pageId}/knowledge-documents`. It takes the same body without `knowledgeFolderId`.

Keep `id`: the next steps use it as `{knowledgeDocumentId}`.

## 2. Send the file to S3

Send a `multipart/form-data` POST to `upload.url`. The form is valid for **15 minutes** and the file can weigh up to **50 MB**.

> [!WARNING]
>
> Send every entry of `upload.fields` **first**, in the order returned, and the file **last**, as the `file` field. S3 ignores every field that follows the file, which makes the upload fail.

```bash cURL theme={null}
curl -sS -f -X POST "$UPLOAD_URL" \
  --form-string "key=$FIELD_KEY" \
  --form-string "policy=$FIELD_POLICY" \
  --form-string "x-amz-signature=$FIELD_SIGNATURE" \
  -F "file=@brand-guidelines.pdf"
```

Use `--form-string` for the fields: `-F` reads a value starting with `@` or `<` as a file. This request goes to S3, not to Semji, so it carries no `Authorization` header. A success is an empty `2xx` or `3xx` response from S3.

Once S3 accepts the file, Semji starts processing it: there is nothing else to call.

If the 15 minutes elapse before the file arrives, the document ends up `failed` with `upload_expired`. Create the document again.

## 3. Poll until the document is ready

Processing is asynchronous. Poll [`GET /v1/knowledge-documents/{knowledgeDocumentId}`](https://help.semji.com/api-reference/knowledge-documents/get-knowledge-document-details) every few seconds until `status` is `success` or `failed`:

```bash cURL theme={null}
curl https://api.semji.com/v1/knowledge-documents/$KNOWLEDGE_DOCUMENT_ID \
  -H "Authorization: Bearer $SEMJI_API_KEY"
```

| `status` | Meaning |
| - | - |
| `pending` | The document is waiting for its file or being processed |
| `success` | The document is indexed and usable by AI generations |
| `failed` | Processing failed, read `failureReason` |

## When processing fails

A `failed` document carries a `failureReason`. To try again, create the document again and list the failed one in `replaces`, so it is removed once the new one is ready.

| `failureReason` | Meaning | What to do |
| - | - | - |
| `optimization_timeout` | Processing took too long | Create the document again |
| `kb_failed` | The knowledge base failed on this document without a more specific reason | Create the document again |
| `upload_expired` | The file did not arrive within 15 minutes of the creation | Create the document again and send the file within 15 minutes |
| `unsupported_type` | The file type is not supported | Upload a supported file (PDF, TXT, DOCX, XLSX or CSV) |
| `encrypted` | The file is password-protected | Upload an unprotected copy |
| `page_too_large` | A page of the file is too large to be read | Upload a lighter file |
| `too_large` | The file is too large or has too many pages to be processed | Split it and upload the parts |
| `processing_error` | Semji could not process the file | Upload a new file, and contact support if it keeps failing |

## Add text and URL documents

Documents that are not files need no upload. Use the same route with **exactly one** of `content` (text) or `url` (a web page that Semji crawls) instead of `fileName` and `size`:

```bash cURL theme={null}
curl -X POST https://api.semji.com/v1/workspaces/$WORKSPACE_ID/knowledge-documents \
  -H "Authorization: Bearer $SEMJI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pricing page",
    "url": "https://example.com/pricing"
  }'
```

The document starts `pending` and follows step 3 like a file. `POST /v1/pages/{pageId}/knowledge-documents` attaches it to a page.

## Organize and review documents

### Folders

Workspace documents can be filed in knowledge folders. List the folders of a workspace:

```bash cURL theme={null}
curl https://api.semji.com/v1/workspaces/$WORKSPACE_ID/knowledge-folders \
  -H "Authorization: Bearer $SEMJI_API_KEY"
```

```json title="200 OK" theme={null}
{
  "data": [
    { "id": "3b8e5d1a7f24", "name": "Brand", "nbAssociatedDocuments": 12, "parentFolderId": null }
  ]
}
```

Pass a folder `id` as `knowledgeFolderId` when you create the document. Without it, the document goes to the General folder, which is not part of this list. Page documents have no folder.

Manage folders with these calls:

| Call | Effect |
| - | - |
| `POST /v1/workspaces/{workspaceId}/knowledge-folders` | Create a folder, at the root or under `parentFolderId`. Folders nest up to 3 levels |
| `PUT /v1/knowledge-folders/{knowledgeFolderId}` | Rename a folder, or move it under another parent (`parentFolderId`, `null` for the root) |
| `DELETE /v1/knowledge-folders/{knowledgeFolderId}` | Delete a folder and its subfolders. Their documents go back to General |
| `PUT /v1/workspaces/{workspaceId}/knowledge-folders` | File documents in a folder |

To file documents, send up to 100 IDs. `knowledgeFolderId: null` files them in General. Page documents and unknown IDs are skipped.

```bash cURL theme={null}
curl -X PUT https://api.semji.com/v1/workspaces/$WORKSPACE_ID/knowledge-folders \
  -H "Authorization: Bearer $SEMJI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "knowledgeDocumentIds": ["7c4a1f08b29d"],
    "knowledgeFolderId": "3b8e5d1a7f24"
  }'
```

### Filter, sort and paginate the list

`GET /v1/workspaces/{workspaceId}/knowledge-documents` paginates at 25 documents by default (`page`, and `limit` up to 100). It accepts these filters and sort:

| Parameter | Effect |
| - | - |
| `search` | Match on the document name |
| `status` | Keep documents in any of the given statuses |
| `reviewStatus` | `to_validate` or `up_to_date` |
| `knowledgeFolderId` | Documents of a folder. `null` means General |
| `minCitations`, `maxCitations` | Bounds on the number of citations |
| `sort` | `name`, `createdAt`, `nbCitations` or `createdBy`. Prefix with `-` for descending. Default `-createdAt` |

Filters, sort and pagination need the V2 knowledge base. Without it, the full list comes back and they are ignored.

The `nbCitations` field is `null` for a document created before citations were counted (13 October 2026) and never cited since.

### Review a document

A document becomes `to_validate` once it is 18 months old and has not been validated in the last 3 months. Mark a document as reviewed with `POST /v1/knowledge-documents/{knowledgeDocumentId}/validation`: its `reviewStatus` becomes `up_to_date` for 3 months. Only a processed document (`status` is `success`) can be validated, otherwise the call returns `409` with the `conflict` code. It needs the V2 knowledge base.

`GET /v1/knowledge-documents/{knowledgeDocumentId}` returns the document with its indexed `content`.

### Preview a document

`GET /v1/knowledge-documents/{knowledgeDocumentId}/preview` returns the document as the app previews it: the original file for a file document, Markdown for a text or URL document. It needs the V2 knowledge base.

### Update a document

`PUT /v1/knowledge-documents/{knowledgeDocumentId}` changes the name, the summary or, for a text document, its content. An update of a document that is still `pending`, or that is `failed`, returns `409` with the `conflict` code: wait for it to succeed, or replace a failed one by creating a new document with `replaces`.

## Next steps

* Replace outdated documents by listing their IDs in `replaces` when you create the new one. They must be live documents of the same workspace and page, otherwise the call returns `422` with `knowledge_document_replacement_invalid`. The new document does not inherit the folder of the one it replaces: pass its `knowledgeFolderId` to keep it there.
* See [Errors](https://help.semji.com/api-reference/errors.md) for the error format, including `error.errorCode`.
* Working from an AI assistant? The [MCP tools](https://help.semji.com/en/connect-your-tools/mcp-tools.md) add text and web page documents; files go through this API or the Semji app.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.