> 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.

# Upload a knowledge document

POST https://api.jelliu.co/api/agents/{agentId}/knowledge
Content-Type: multipart/form-data

Uploads one file as a knowledge source for the agent. Send it as `multipart/form-data` in a
part named `file`; the original file name (sanitized, at most 255 characters) becomes the
document `name`.

Checks run in this order: plan file-count cap, rate limit, then the file itself. The
extension must be one of `.pdf`, `.txt`, `.docx`, `.html`, `.epub`, `.md`, `.csv`, `.json`;
the part's `Content-Type` must be one of `application/pdf`, `text/plain`, `text/csv`,
`text/markdown`, `text/x-markdown`, `application/json`,
`application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `text/html`,
`application/epub+zip`; and the bytes must match the extension (`%PDF-` header for PDF, a ZIP
container for DOCX/EPUB, `<html` or `<!DOCTYPE` in the first 512 bytes for HTML, no NUL byte
in the first 4 KB for text formats). Text formats (`.txt`, `.csv`, `.html`, `.md`, `.json`) are
then screened for prompt-injection and fraud content; PDF, DOCX and EPUB are not screened.
Then the workspace's total knowledge size is checked with this file included.

By default the call is synchronous and answers `201` once the file is attached to the agent;
indexing continues in the background (`status: processing`) — poll
`GET /api/agents/{agentId}/knowledge` for `ready`. With `?async=true` the validated file is
queued and the call answers `202` with a `jobId` right away; poll
`GET /api/agents/{agentId}/knowledge/jobs/{jobId}` (kept for 1 hour). In async mode, agent
lookup, provisioning and the size cap are checked by the worker, so those failures appear as
a `failed` job instead of an HTTP error.

**Side effects.** Uploads the file to the voice engine and attaches it to the
agent, starts RAG indexing, turns retrieval on for the agent, and extracts the text for the
text channels. If the local save fails (for example the plan cap is hit concurrently), the
provider upload is rolled back. Writes an `agent.knowledge.upload` audit entry. If the agent
is not yet provisioned, its provisioning job is re-enqueued.

**Idempotency.** Not idempotent. There is no de-duplication by name or content: retrying after
a timeout can attach the same file twice (and count twice against the plan caps). List the
documents before retrying.

**Webhook events.** `audit.log_recorded` for the audit entry, if you subscribe to it. See [Webhooks](/webhooks).

**Access**

* **Required scope:** `write`. Any workspace role.
* **Rate limit:** Configuration mutations — 10 requests/min per workspace (shared with other agent configuration changes), in addition to the General API limit (120–600/min by plan). See [Rate limits](/rate-limits).
* **Plan:** Available on every plan, within the plan's document caps: Starter 5 files / 10 MB, Growth 25 / 50 MB, Business 100 / 200 MB, Enterprise unlimited size. A workspace without an active plan cannot upload.

Reference: https://developer.jelliu.co/api-reference/knowledge/post-agents-by-agent-id-knowledge

## Authentication

- `Authorization` header (bearer token, required) — Workspace API key: `jl_` followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (**Settings → API Keys**) and sent as `Authorization: Bearer jl_...`. The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys. | Scope | GET / HEAD | POST / PUT / PATCH / DELETE | Admin-only routes | | --- | --- | --- | --- | | `read` | Yes | No | No | | `write` | Yes | Yes | No | | `full` | Yes | Yes | Yes | Operations restricted to admins or owners reject keys without the `full` scope with `403`, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See [Authentication](/authentication).

## Request

### Path parameters

- `agentId` (string, required) — UUID of the agent. It must belong to the authenticated workspace; an id from another workspace answers `404`, exactly like an unknown one. Returned as `id` by `GET /api/agents`.

### Query parameters

- `async` (enum, optional) — Send `true` to queue the upload and receive `202` with a job id. Any other value (or omitting it) processes the upload synchronously and answers `201`.
  - Allowed values: `true`

### Body (multipart/form-data)

This endpoint expects a multipart form containing a file.

- `file` (file, required) — The document, at most 20 MB (20971520 bytes). The part's `Content-Type` must be one of the accepted MIME types and its file name must carry an accepted extension.

## Response

### 201

Uploaded and attached; indexing continues in the background (`status` = `processing`).

- `data` (object, required) — A document in an agent's knowledge base. The extracted text and provider ids are never returned.
  - `id` (string, optional) — Unique identifier of the document.
  - `agent_id` (string, optional) — The agent the document belongs to.
  - `name` (string, optional) — The uploaded file name, sanitized (path components and NUL bytes removed, at most 255 characters).
  - `size` (integer, optional) — File size in bytes. Counts toward the plan's total knowledge size.
  - `status` (enum, optional) — Summary status derived from indexing: `processing` (index pending or in progress), `ready` (indexed, or permanently not indexable such as `document_too_small`, in which case the agent still reads the whole document) or `failed` (retryable indexing failure).
    - Allowed values: `processing`, `ready`, `failed`
  - `rag_index_status` (enum, optional) — Raw retrieval-index state reported by the voice engine — `pending`, `processing`, `succeeded` or `failed`.
    - Allowed values: `pending`, `processing`, `succeeded`, `failed`
  - `rag_index_error` (string, optional, nullable) — Why indexing failed, `null` otherwise. `document_too_small`, `rag_limit_exceeded` and `cannot_index_folder` are permanent (the document then reports `status: ready`); any other text is a provider error that will be retried.
  - `created_at` (datetime, optional) — When the document was uploaded.
  - `updated_at` (datetime, optional) — Last change to the document row.

### 202

Upload queued (`?async=true`). Poll `GET /api/agents/{agentId}/knowledge/jobs/{jobId}`; when the job is `completed` its `result` holds the same document object as the `201` response, and when it is `failed` its `error` holds the reason.

- `data` (object, optional) — The queued job.
  - `jobId` (string, optional) — Id to poll the job with.
  - `status` (enum, optional) — Always `pending` when accepted.
    - Allowed values: `pending`

## Errors

### 400 Bad Request Error

Invalid `agentId`, no file, a disallowed extension or MIME type, content that does not match the extension, or content rejected by screening. No `details` are returned.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 401 Unauthorized Error

No usable credential. Either the `Authorization` header is missing or is not a well-formed `Bearer jl_…` key, or the key is unknown, revoked or expired. Do not retry with the same key. See [Authentication](/authentication#401-unauthorized).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 403 Forbidden Error

A plan cap was reached (`BILLING_ERROR`, with `metadata.limit`, `metadata.current` and `metadata.tier`; sizes in MB), the API key lacks `write`, or the workspace is suspended.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 404 Not Found Error

The agent does not exist in this workspace (synchronous mode only).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 409 Conflict Error

The agent is still being provisioned in the voice engine. Retry after the `Retry-After` delay.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 429 Too Many Requests Error

Configuration-mutation (10/min) or General API rate limit exceeded.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 500 Internal Server Error

The file exceeds 20 MB or was sent under a part name other than `file`. The upload parser rejects these before validation runs, and they currently surface as a generic 500.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 502 Bad Gateway Error

The voice engine rejected or did not answer the upload. Nothing was saved; retry later.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

## Examples

### Knowledge_postAgentsByAgentIdKnowledge_example

**Request**

```json
{}
```

**Response**

```json
{
  "data": {
    "id": "5c4b3a29-1807-4f6e-9d5c-4b3a29180716",
    "agent_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "name": "lista-de-precios-septiembre-2026.pdf",
    "size": 482113,
    "status": "processing",
    "rag_index_status": "pending",
    "rag_index_error": null,
    "created_at": "2026-09-14T15:04:05.000Z",
    "updated_at": "2026-09-14T15:04:05.000Z"
  }
}
```

**SDK Code**

```python Knowledge_postAgentsByAgentIdKnowledge_example
import requests

url = "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge"

querystring = {"async":"true"}

headers = {"Authorization": "Bearer <token>"}

response = requests.post(url, headers=headers, params=querystring)

print(response.json())
```

```javascript Knowledge_postAgentsByAgentIdKnowledge_example
const url = 'https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true';
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Knowledge_postAgentsByAgentIdKnowledge_example
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Knowledge_postAgentsByAgentIdKnowledge_example
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java Knowledge_postAgentsByAgentIdKnowledge_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php Knowledge_postAgentsByAgentIdKnowledge_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp Knowledge_postAgentsByAgentIdKnowledge_example
using RestSharp;

var client = new RestClient("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift Knowledge_postAgentsByAgentIdKnowledge_example
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Knowledge_postAgentsByAgentIdKnowledge_example

**Request**

```json
{}
```

**Response**

```json
{
  "data": {
    "status": "pending",
    "jobId": "7b2e4c91-5f3a-4d8e-b6c0-19a8f2d7e354"
  }
}
```

**SDK Code**

```python Knowledge_postAgentsByAgentIdKnowledge_example
import requests

url = "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge"

querystring = {"async":"true"}

headers = {"Authorization": "Bearer <token>"}

response = requests.post(url, headers=headers, params=querystring)

print(response.json())
```

```javascript Knowledge_postAgentsByAgentIdKnowledge_example
const url = 'https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true';
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Knowledge_postAgentsByAgentIdKnowledge_example
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Knowledge_postAgentsByAgentIdKnowledge_example
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java Knowledge_postAgentsByAgentIdKnowledge_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php Knowledge_postAgentsByAgentIdKnowledge_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp Knowledge_postAgentsByAgentIdKnowledge_example
using RestSharp;

var client = new RestClient("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift Knowledge_postAgentsByAgentIdKnowledge_example
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### PDF price list

**Request**

```json
{
  "file": "<file: lista-de-precios-septiembre-2026.pdf>"
}
```

**Response**

```json
{
  "data": {
    "id": "5c4b3a29-1807-4f6e-9d5c-4b3a29180716",
    "agent_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "name": "lista-de-precios-septiembre-2026.pdf",
    "size": 482113,
    "status": "processing",
    "rag_index_status": "pending",
    "rag_index_error": null,
    "created_at": "2026-09-14T15:04:05.000Z",
    "updated_at": "2026-09-14T15:04:05.000Z"
  }
}
```

**SDK Code**

```python PDF price list
import requests

url = "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge"

querystring = {"async":"true"}

files = { "file": "open('lista-de-precios-septiembre-2026.pdf', 'rb')" }
headers = {"Authorization": "Bearer <token>"}

response = requests.post(url, files=files, headers=headers, params=querystring)

print(response.json())
```

```javascript PDF price list
const url = 'https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true';
const form = new FormData();
form.append('file', 'lista-de-precios-septiembre-2026.pdf');

const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};

options.body = form;

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go PDF price list
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"lista-de-precios-septiembre-2026.pdf\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001--\r\n")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby PDF price list
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"lista-de-precios-septiembre-2026.pdf\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001--\r\n"

response = http.request(request)
puts response.read_body
```

```java PDF price list
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")
  .header("Authorization", "Bearer <token>")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"lista-de-precios-septiembre-2026.pdf\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001--\r\n")
  .asString();
```

```php PDF price list
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true', [
  'multipart' => [
    [
        'name' => 'file',
        'filename' => 'lista-de-precios-septiembre-2026.pdf',
        'contents' => null
    ]
  ]
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp PDF price list
using RestSharp;

var client = new RestClient("https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddParameter("undefined", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"lista-de-precios-septiembre-2026.pdf\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift PDF price list
import Foundation

let headers = ["Authorization": "Bearer <token>"]
let parameters = [
  [
    "name": "file",
    "fileName": "lista-de-precios-septiembre-2026.pdf"
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/api/agents/2f2d8b4d-aa6f-41e0-9d3e-4d61e1b07a11/knowledge?async=true")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```