> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.jelliu.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.jelliu.co/_mcp/server.

# Knowledge base

A knowledge document is a file you upload to one agent so it can answer from your own material instead of general knowledge: a product catalog, a price list, a returns policy, an FAQ. Documents are managed per agent under `/api/agents/{agentId}/knowledge`. Uploading a document **attaches it to that agent** in the same request, and the agent's retrieval is switched on for you. There is no separate attach step.

## How it works

```mermaid
flowchart TD
    A["POST /api/agents/{agentId}/knowledge<br />multipart field: file"] --> B{"Extension, MIME type,<br />file content, screening"}
    B -- rejected --> X["400 VALIDATION_FAILED"]
    B -- ok --> C{"Plan: file count<br />and total size"}
    C -- over the cap --> Y["403 BILLING_ERROR"]
    C -- ok --> D["Stored and attached to the agent<br />retrieval switched on"]
    D --> R["201 Created<br />status: processing"]
    D --> I["Indexing runs in the background"]
    I --> OK["status: ready"]
    I --> KO["status: failed"]
    KO -. "retried automatically" .-> I
```

1. **Validation.** The file must have an allowed extension, be sent with an allowed MIME type, and its bytes must match the extension (a `.pdf` must start with `%PDF-`, a `.docx` must be a ZIP container, and so on). Text formats are also screened for prompt-injection and fraud content.
2. **Plan check.** The workspace plan caps both the **number** of knowledge files and their **total size**, counted across every agent in the workspace.
3. **Attach.** The document is stored, attached to the agent, and the agent's retrieval is enabled. The response returns immediately with `status: "processing"`.
4. **Indexing.** Indexing continues in the background. A background reconciler re-checks documents that are still `processing` or `failed` every few minutes, so a document can take several minutes to show `ready` even when indexing finished sooner.
5. **Retrieval.** From then on, the agent retrieves relevant passages from its documents while it talks. Knowledge is attached to the agent, not to a channel, so the same documents serve calls and text conversations.
6. **Removal.** Deleting a document detaches it from the agent and removes it. When an agent's last document is deleted, retrieval is switched off again.

The API accepts **files only**. There is no endpoint that ingests a URL or a raw text string. To use a web page or a snippet of text, save it as `.html`, `.md` or `.txt` and upload that file.

### Document status

`status` is the value to branch on. It is derived from the indexing state (`rag_index_status`) and the indexing error:

| `rag_index_status` | `rag_index_error`                                                   | `status`     | Meaning                                                                                               |
| ------------------ | ------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |
| `pending`          | `null`                                                              | `processing` | Accepted; indexing has not started yet. This is what the upload response returns.                     |
| `processing`       | `null`                                                              | `processing` | Indexing is running.                                                                                  |
| `succeeded`        | `null`                                                              | `ready`      | Indexed and retrievable.                                                                              |
| `failed`           | `document_too_small`, `rag_limit_exceeded` or `cannot_index_folder` | `ready`      | The document cannot be indexed and retrying will not change that. It **stays attached** to the agent. |
| `failed`           | any other value                                                     | `failed`     | Indexing failed. It is retried automatically in the background.                                       |

A document under about 500 bytes is reported as `document_too_small`: it stays attached and shows `ready`, but it is not indexed. When you test retrieval, use a document larger than that.

## The knowledge document object

Returned by the upload, by the list, and inside `result` of a completed upload job.

| Field              | Type              | Nullable | Description                                                                                                        |
| ------------------ | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `id`               | string (uuid)     | No       | Document ID. Use it to delete the document.                                                                        |
| `agent_id`         | string (uuid)     | No       | The agent the document is attached to. A document belongs to exactly one agent.                                    |
| `name`             | string            | No       | The uploaded file name, stripped of any directory part and truncated to 255 characters.                            |
| `size`             | integer           | No       | File size in bytes. Counts toward the plan's total knowledge size.                                                 |
| `status`           | string            | No       | `processing`, `ready` or `failed`. See [Document status](#document-status).                                        |
| `rag_index_status` | string            | No       | Raw indexing state: `pending`, `processing`, `succeeded` or `failed`.                                              |
| `rag_index_error`  | string            | Yes      | Why indexing did not succeed, for example `document_too_small`. `null` while pending, processing or after success. |
| `created_at`       | string (ISO 8601) | No       | When the document was uploaded. Lists are sorted by this field, newest first.                                      |
| `updated_at`       | string (ISO 8601) | No       | Last change to the document record.                                                                                |

```json
{
  "id": "4b7e2c91-3d5a-4f08-9a6e-1c2d3e4f5a6b",
  "agent_id": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
  "name": "lista-de-precios-2026.pdf",
  "size": 482311,
  "status": "ready",
  "rag_index_status": "succeeded",
  "rag_index_error": null,
  "created_at": "2026-09-14T15:02:11.204Z",
  "updated_at": "2026-09-14T15:02:11.204Z"
}
```

## Supported files

| Extension | Send with MIME type                                                       | Content check                                                              |
| --------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `.pdf`    | `application/pdf`                                                         | Starts with `%PDF-`.                                                       |
| `.docx`   | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | A ZIP container.                                                           |
| `.epub`   | `application/epub+zip`                                                    | A ZIP container.                                                           |
| `.html`   | `text/html`                                                               | Contains `<html` or `<!doctype` in the first 512 bytes.                    |
| `.txt`    | `text/plain`                                                              | No null bytes in the first 4 KB.                                           |
| `.md`     | `text/markdown` or `text/x-markdown`                                      | No null bytes in the first 4 KB.                                           |
| `.csv`    | `text/csv` or `text/plain`                                                | No null bytes in the first 4 KB.                                           |
| `.json`   | `application/json` or `text/plain`                                        | No null bytes in the first 4 KB. JSON is not parsed; any text is accepted. |

The accepted MIME types are exactly `application/pdf`, `text/plain`, `text/csv`, `text/markdown`, `text/x-markdown`, `application/json`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `text/html` and `application/epub+zip`. The extension is read from the file name you send, case-insensitively. Files must be at least 4 bytes and at most **20 MB**.

**Always set the MIME type of the file part explicitly.** Many HTTP clients send `application/octet-stream` when they cannot guess a type (a `Blob` created without `type`, `curl -F` for `.md`, `.csv`, `.json`, `.docx` or `.epub`), and that is rejected with `400`:
`MIME type not allowed: application/octet-stream. Allowed: ...`.
Also pass a **file name** with the part: without one, clients send a name such as `blob`, which fails the extension check with `File type not allowed`.

## Common tasks

### Upload a document

#### Send the file as multipart/form-data

The multipart field must be named `file`. Do not set the `Content-Type` header of the request yourself; let your client generate the multipart boundary.

**`curl`**

```bash title="curl"
curl -sS -X POST "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/knowledge" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -F "file=@lista-de-precios-2026.pdf;type=application/pdf"
```

**`Node.js`**

```javascript title="Node.js"
import { readFile } from 'node:fs/promises';

const agentId = '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4';
const bytes = await readFile('lista-de-precios-2026.pdf');

const form = new FormData();
// Set both the MIME type and the file name.
form.append('file', new Blob([bytes], { type: 'application/pdf' }), 'lista-de-precios-2026.pdf');

const res = await fetch(`https://api.jelliu.co/api/agents/${agentId}/knowledge`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.JELLIU_API_KEY}` },
  body: form,
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error?.code}: ${body.error?.message}`);

console.log(body.data.id, body.data.status); // "processing"
```

**`Python`**

```python title="Python"
import os
import requests

agent_id = "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"

with open("lista-de-precios-2026.pdf", "rb") as f:
    res = requests.post(
        f"https://api.jelliu.co/api/agents/{agent_id}/knowledge",
        headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
        files={"file": ("lista-de-precios-2026.pdf", f, "application/pdf")},
        timeout=120,
    )

body = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {body['error']['code']}: {body['error']['message']}")

print(body["data"]["id"], body["data"]["status"])  # "processing"
```

#### Read the 201 response

The document is already attached to the agent. Indexing has not started yet:

```json
{
  "data": {
    "id": "4b7e2c91-3d5a-4f08-9a6e-1c2d3e4f5a6b",
    "agent_id": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
    "name": "lista-de-precios-2026.pdf",
    "size": 482311,
    "status": "processing",
    "rag_index_status": "pending",
    "rag_index_error": null,
    "created_at": "2026-09-14T15:02:11.204Z",
    "updated_at": "2026-09-14T15:02:11.204Z"
  }
}
```

#### Wait for the document to be ready

There is no single-document endpoint and no webhook for indexing. List the agent's documents and look for your `id` until `status` is `ready` or `failed`. Indexing is re-checked every few minutes, so poll about once a minute rather than in a tight loop.

**`curl`**

```bash title="curl"
curl -sS "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/knowledge?limit=100" \
  -H "Authorization: Bearer $JELLIU_API_KEY"
```

**`Node.js`**

```javascript title="Node.js"
async function waitUntilIndexed(agentId, docId, { intervalMs = 60_000, attempts = 20 } = {}) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(
      `https://api.jelliu.co/api/agents/${agentId}/knowledge?limit=100`,
      { headers: { Authorization: `Bearer ${process.env.JELLIU_API_KEY}` } },
    );
    const { data } = await res.json();
    const doc = data.find((d) => d.id === docId);
    if (!doc) throw new Error('Document not found on this page');
    if (doc.status !== 'processing') return doc; // "ready" or "failed"
    await new Promise((resolve) => setTimeout(resolve, intervalMs));
  }
  throw new Error('Still processing');
}
```

**`Python`**

```python title="Python"
import os
import time
import requests

def wait_until_indexed(agent_id, doc_id, interval=60, attempts=20):
    for _ in range(attempts):
        res = requests.get(
            f"https://api.jelliu.co/api/agents/{agent_id}/knowledge",
            params={"limit": 100},
            headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
            timeout=30,
        )
        doc = next((d for d in res.json()["data"] if d["id"] == doc_id), None)
        if doc is None:
            raise RuntimeError("Document not found on this page")
        if doc["status"] != "processing":
            return doc  # "ready" or "failed"
        time.sleep(interval)
    raise RuntimeError("Still processing")
```

### Upload in the background

Add `?async=true` to queue the upload instead of waiting for it. Validation of the extension, MIME type, content and screening, and the plan's file-count check, still run before the response; everything after that runs in a job.

#### Queue the upload

**`curl`**

```bash title="curl"
curl -sS -X POST "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/knowledge?async=true" \
  -H "Authorization: Bearer $JELLIU_API_KEY" \
  -F "file=@politica-de-devoluciones.md;type=text/markdown"
```

**`Node.js`**

```javascript title="Node.js"
import { readFile } from 'node:fs/promises';

const agentId = '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4';
const form = new FormData();
form.append(
  'file',
  new Blob([await readFile('politica-de-devoluciones.md')], { type: 'text/markdown' }),
  'politica-de-devoluciones.md',
);

const res = await fetch(`https://api.jelliu.co/api/agents/${agentId}/knowledge?async=true`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.JELLIU_API_KEY}` },
  body: form,
});
const { data } = await res.json(); // 202
console.log(data.jobId, data.status); // "pending"
```

**`Python`**

```python title="Python"
import os
import requests

agent_id = "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"

with open("politica-de-devoluciones.md", "rb") as f:
    res = requests.post(
        f"https://api.jelliu.co/api/agents/{agent_id}/knowledge",
        params={"async": "true"},
        headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
        files={"file": ("politica-de-devoluciones.md", f, "text/markdown")},
        timeout=120,
    )

job = res.json()["data"]  # 202
print(job["jobId"], job["status"])  # "pending"
```

```json
{
  "data": {
    "jobId": "3f1c9a52-8d7e-4b0a-9f39-2b8a4e6d1c77",
    "status": "pending"
  }
}
```

#### Poll the job

`GET /api/agents/{agentId}/knowledge/jobs/{jobId}` returns the job. `status` moves from `pending` to `processing` and ends in `completed` (with the document in `result`) or `failed` (with the reason in `error`).

```bash
curl -sS "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/knowledge/jobs/3f1c9a52-8d7e-4b0a-9f39-2b8a4e6d1c77" \
  -H "Authorization: Bearer $JELLIU_API_KEY"
```

```json
{
  "data": {
    "id": "3f1c9a52-8d7e-4b0a-9f39-2b8a4e6d1c77",
    "tenantId": "b2a4c6d8-1e3f-4a5b-8c7d-9e0f1a2b3c4d",
    "type": "knowledge-upload",
    "status": "completed",
    "result": {
      "id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
      "agent_id": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
      "name": "politica-de-devoluciones.md",
      "size": 18244,
      "status": "processing",
      "rag_index_status": "pending",
      "rag_index_error": null,
      "created_at": "2026-09-14T15:10:42.018Z",
      "updated_at": "2026-09-14T15:10:42.018Z"
    },
    "createdAt": "2026-09-14T15:10:41.550Z",
    "updatedAt": "2026-09-14T15:10:42.391Z"
  }
}
```

A job is kept for **one hour** after its last update, then the endpoint answers `404 NOT_FOUND`. Background uploads are attempted **once**: a failed job is not retried, so upload the file again. Errors that the synchronous upload returns as HTTP errors, such as the plan's total-size cap or an agent that is still provisioning, arrive here as `status: "failed"` with the message in `error`.

### List an agent's documents

`GET /api/agents/{agentId}/knowledge` uses [offset pagination](/pagination#offset):

**`limit`** `integer` — default: 50

1 to 100.

---

**`offset`** `integer` — default: 0

0 to 100000.

---

Documents come newest first as `{ "data": [ ... ] }`, with no total. Stop when a page returns fewer than `limit` items. Deleted documents are never listed.

**`curl`**

```bash title="curl"
curl -sS "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/knowledge?limit=50&offset=0" \
  -H "Authorization: Bearer $JELLIU_API_KEY"
```

**`Node.js`**

```javascript title="Node.js"
const agentId = '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4';
const res = await fetch(
  `https://api.jelliu.co/api/agents/${agentId}/knowledge?limit=50&offset=0`,
  { headers: { Authorization: `Bearer ${process.env.JELLIU_API_KEY}` } },
);
const { data } = await res.json();
for (const doc of data) console.log(doc.name, doc.status, doc.size);
```

**`Python`**

```python title="Python"
import os
import requests

agent_id = "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"
res = requests.get(
    f"https://api.jelliu.co/api/agents/{agent_id}/knowledge",
    params={"limit": 50, "offset": 0},
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    timeout=30,
)
for doc in res.json()["data"]:
    print(doc["name"], doc["status"], doc["size"])
```

```json
{
  "data": [
    {
      "id": "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a",
      "agent_id": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
      "name": "politica-de-devoluciones.md",
      "size": 18244,
      "status": "processing",
      "rag_index_status": "processing",
      "rag_index_error": null,
      "created_at": "2026-09-14T15:10:42.018Z",
      "updated_at": "2026-09-14T15:10:42.018Z"
    },
    {
      "id": "4b7e2c91-3d5a-4f08-9a6e-1c2d3e4f5a6b",
      "agent_id": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
      "name": "lista-de-precios-2026.pdf",
      "size": 482311,
      "status": "ready",
      "rag_index_status": "succeeded",
      "rag_index_error": null,
      "created_at": "2026-09-14T15:02:11.204Z",
      "updated_at": "2026-09-14T15:02:11.204Z"
    }
  ]
}
```

### Replace or delete a document

Documents cannot be edited. To update one, upload the new version, then delete the old one. Uploading first means the agent is never left without the content; deleting first frees plan capacity if you are at the cap.

**`curl`**

```bash title="curl"
curl -sS -X DELETE \
  "https://api.jelliu.co/api/agents/7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4/knowledge/4b7e2c91-3d5a-4f08-9a6e-1c2d3e4f5a6b" \
  -H "Authorization: Bearer $JELLIU_API_KEY"
```

**`Node.js`**

```javascript title="Node.js"
const agentId = '7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4';
const docId = '4b7e2c91-3d5a-4f08-9a6e-1c2d3e4f5a6b';

const res = await fetch(`https://api.jelliu.co/api/agents/${agentId}/knowledge/${docId}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${process.env.JELLIU_API_KEY}` },
});
if (res.status !== 204) {
  const body = await res.json();
  throw new Error(`${res.status} ${body.error?.code}`);
}
```

**`Python`**

```python title="Python"
import os
import requests

agent_id = "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4"
doc_id = "4b7e2c91-3d5a-4f08-9a6e-1c2d3e4f5a6b"

res = requests.delete(
    f"https://api.jelliu.co/api/agents/{agent_id}/knowledge/{doc_id}",
    headers={"Authorization": f"Bearer {os.environ['JELLIU_API_KEY']}"},
    timeout=30,
)
if res.status_code != 204:
    raise RuntimeError(f"{res.status_code} {res.json()['error']['code']}")
```

A successful delete returns `204 No Content` with an empty body. The document is removed from the voice runtime first; if that fails, the document stays in your list and the request can be retried safely. The `agentId` in the path must be the agent the document belongs to.

A document belongs to one agent. To give two agents the same material, upload the file to each of them. Each copy counts toward the plan's file count and total size.

## Errors

| Code                  | Status | When                                                                                                                                       |
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `VALIDATION_FAILED`   | 400    | `Invalid agent ID` or `Invalid agent ID or document ID`: a path ID is not a UUID.                                                          |
| `VALIDATION_FAILED`   | 400    | `No file uploaded`: the request has no multipart part named `file`.                                                                        |
| `VALIDATION_FAILED`   | 400    | `File type not allowed. Allowed: ...`: the file name's extension is not supported, or the part has no file name.                           |
| `VALIDATION_FAILED`   | 400    | `MIME type not allowed: ...`: the part's MIME type is not in the accepted list, typically `application/octet-stream`.                      |
| `VALIDATION_FAILED`   | 400    | `File content does not match extension ...`: for example `not a PDF (missing %PDF- header)`, `null byte in text file` or `file too small`. |
| `VALIDATION_FAILED`   | 400    | `Document rejected by content screening: ...`: a text document was flagged as prompt injection or fraud.                                   |
| `VALIDATION_FAILED`   | 400    | `Document does not belong to this agent`: the delete path names a different agent.                                                         |
| `VALIDATION_FAILED`   | 400    | `Request validation failed`: `limit`, `offset` or `jobId` is out of range or malformed. `details` lists the issues.                        |
| `UNAUTHORIZED`        | 401    | Missing or invalid API key. See [Authentication](/authentication).                                                                         |
| `FORBIDDEN`           | 403    | A `read` key attempted an upload or delete.                                                                                                |
| `BILLING_ERROR`       | 403    | `Knowledge file limit reached (5 files on the starter plan). ...`: the workspace is at its file count.                                     |
| `BILLING_ERROR`       | 403    | `Knowledge base size limit reached (10 MB on the starter plan). ...`: this file would take the workspace past its total size.              |
| `BILLING_ERROR`       | 403    | `Your free trial has ended (or no plan is active). ...`: the workspace has no active plan.                                                 |
| `AGENT_NOT_FOUND`     | 404    | The agent does not exist, was deleted, or belongs to another workspace.                                                                    |
| `NOT_FOUND`           | 404    | `Document not found` on delete, or `Job not found` when the job ID is unknown, belongs to another workspace, or expired.                   |
| `AGENT_PROVISIONING`  | 409    | `Agent is still being configured — try uploading again in a few seconds`. Retry after `Retry-After` (5 seconds).                           |
| `RATE_LIMIT_EXCEEDED` | 429    | `Too many agent operations, please slow down`. See [Limits](#limits).                                                                      |
| `INTERNAL_ERROR`      | 500    | The file is larger than 20 MB, or the file was sent in a field other than `file`.                                                          |
| `VOICE_AI_ERROR`      | 502    | The voice runtime refused the upload or the delete. The message is the generic `Internal server error`. Safe to retry.                     |

`BILLING_ERROR` responses carry `metadata` with `limit`, `current` and `tier`. For the file-count cap `limit` and `current` are file counts; for the size cap they are megabytes.

An oversized file and a misnamed multipart field are rejected before validation runs, so they surface as `500 INTERNAL_ERROR` rather than `400 VALIDATION_FAILED`. Check the size (20 MB) and the field name (`file`) on your side before uploading.

See [Errors](/errors) for the envelope and retry guidance.

## Limits

**Rate limits.** Uploads and deletes count against the shared **10 per minute** budget for agent, campaign and webhook configuration changes, in addition to the plan's general API limit. Listing documents and polling jobs use only the general limit. See [Rate limits](/rate-limits).

**Scopes.** Listing documents and polling jobs need a `read` key. Uploading and deleting need `write`. See [Authentication](/authentication).

**Plan limits.** Both caps apply to the whole workspace, across all agents:

| Plan           | Knowledge files | Total knowledge size |
| -------------- | --------------- | -------------------- |
| No active plan | 0               | 0                    |
| Starter        | 5               | 10 MB                |
| Growth         | 25              | 50 MB                |
| Business       | 100             | 200 MB               |
| Enterprise     | 9,999           | Unlimited            |

Deleted documents free their slot and their size immediately.

**Per request.**

| Limit                | Value                        |
| -------------------- | ---------------------------- |
| File size            | 20 MB per file               |
| Files per request    | 1                            |
| File name            | Truncated to 255 characters  |
| List page size       | 1 to 100 documents           |
| Upload job retention | 1 hour after its last update |

## Webhooks

No webhook events are emitted for knowledge documents. To follow indexing, list the agent's documents as shown in [Wait for the document to be ready](#upload-a-document), or poll the job when you upload with `?async=true`. See [Webhooks](/webhooks) for the events Jelliu does send.

## Related

#### [Agents](/resources/agents)

Create and configure the agents that documents attach to.

#### [Conversations](/resources/conversations)

Read the text conversations your agents answer from their knowledge.

#### [Calls](/resources/calls)

Place and inspect the voice calls that retrieve from the same documents.

#### [Rate limits](/rate-limits)

The shared budget for configuration changes.

#### [API reference](/api-reference)

Every knowledge endpoint, parameter and response.