Creates a knowledge base and starts ingesting its sources in the background. Attach it to an agent's LLM or flow through knowledge_base_ids so the agent can look up answers during calls and chats.
Embeddings use OpenAI text-embedding-3-small when OPENAI_API_KEY is set. Without it (or with TRIED_MOCK_PROVIDERS=1) Tried stores hash embeddings (mock-hash-256), so retrieval still works keyless with lower quality.
Request#
Send multipart/form-data to upload files, or application/json for texts and URLs only.
| Field | Type | Required | Description |
|---|---|---|---|
knowledge_base_name |
string | Yes | 1 to 40 characters. |
knowledge_base_texts |
array of objects | No | Text snippets, each { "title": string, "text": string }. |
knowledge_base_urls |
array of strings | No | Web pages to fetch. Each must be a valid URL. |
knowledge_base_files |
files | No | Documents to upload. Multipart only. See File types. |
enable_auto_refresh |
boolean | No | Re-fetch URL sources about once a day. Default false. |
chunk_size |
integer | No | Target characters per chunk. 200 to 4000, default 800. |
chunk_overlap |
integer | No | Characters shared between neighbouring chunks. 0 to 1000, default 100. Must be smaller than chunk_size when you send both. |
auto_add_pages |
boolean | No | On refresh, crawl crawl_roots and add pages that are not sources yet. Default false. |
crawl_roots |
array of strings | No | Up to 10 site URLs to crawl when auto_add_pages is on. |
In multipart requests, send arrays and objects as a JSON string (for example knowledge_base_texts=[{"title":"Hours","text":"..."}]) or repeat the field with a [] suffix (knowledge_base_urls[]=https://...). The strings true and false are read as booleans.
Limits#
- Per request: up to 25 files, each at most 50 MB.
- Per knowledge base: 25 files, 500 URLs and 50 text snippets. Synced sources do not count.
- JSON bodies: at most 25 MB.
File types#
| File | How the text is read |
|---|---|
| Text extraction with pdf.js. Scanned PDFs without a text layer give little or no text. | |
.docx |
Raw text of the document. |
.xlsx, .xls |
Each sheet as CSV under a ## <sheet name> heading. |
.pptx |
Text of each slide as Slide <n>: .... |
Images (png, jpg, gif, bmp, tiff, webp) |
OCR with tesseract when it is installed on the API host. Otherwise the source holds a placeholder line that names the file. |
Anything else (.txt, .md, .csv, .html, ...) |
Stored as UTF-8 text. |
How ingestion works#
- Each source is saved and the knowledge base
statusbecomesin_progress. The response returns at this point. - URL sources are fetched (15 second timeout). HTML is converted to text, with scripts and styles removed.
- Text is split into chunks of about
chunk_sizecharacters, preferring paragraph and sentence breaks, withchunk_overlapcharacters of overlap. - Chunks are embedded and stored. The knowledge base
statusbecomescomplete, orerrorif any source in the batch failed.
URL fetching#
URLs go through Tried's egress guard to prevent server-side request forgery:
- Only
httpandhttpsURLs are fetched. - Private, loopback, link-local, CGNAT, multicast, documentation and reserved addresses are refused. For IPv4 that is
0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,192.0.0.0/24,192.0.2.0/24,192.168.0.0/16,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,224.0.0.0/4and240.0.0.0/4. For IPv6 it is::,::1,fc00::/7,fe80::/10,ff00::/8,2001:db8::/32,64:ff9b::/96and100::/64. IPv4-mapped IPv6 addresses are checked against the IPv4 list. - The check runs on the address the socket actually connects to, so a hostname cannot pass the check and then resolve to a private address (DNS rebinding).
- Redirects are followed by Tried, up to 5 hops, and every hop is checked again.
A refused or failing URL marks that source as failed and the knowledge base status as error. Self-hosted installs can turn the address check off with EGRESS_ALLOW_PRIVATE=1 for local development only.
Example request#
curl -X POST https://api.tried.com.au/create-knowledge-base \
-H "Authorization: Bearer $TRIED_API_KEY" \
-F knowledge_base_name="Clinic FAQ" \
-F 'knowledge_base_texts=[{"title":"Opening hours","text":"We are open 8am to 6pm, Monday to Saturday."}]' \
-F 'knowledge_base_urls[]=https://example.com.au/pricing' \
-F knowledge_base_files=@price-list.pdfimport { readFile } from "node:fs/promises";
const form = new FormData();
form.append("knowledge_base_name", "Clinic FAQ");
form.append("knowledge_base_texts", JSON.stringify([
{ title: "Opening hours", text: "We are open 8am to 6pm, Monday to Saturday." },
]));
form.append("knowledge_base_urls[]", "https://example.com.au/pricing");
form.append("knowledge_base_files", new Blob([await readFile("price-list.pdf")], { type: "application/pdf" }), "price-list.pdf");
const res = await fetch("https://api.tried.com.au/create-knowledge-base", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.TRIED_API_KEY}` },
body: form,
});
const kb = await res.json();import json, os, requests
with open("price-list.pdf", "rb") as f:
res = requests.post(
"https://api.tried.com.au/create-knowledge-base",
headers={"Authorization": f"Bearer {os.environ['TRIED_API_KEY']}"},
data={
"knowledge_base_name": "Clinic FAQ",
"knowledge_base_texts": json.dumps([
{"title": "Opening hours", "text": "We are open 8am to 6pm, Monday to Saturday."}
]),
"knowledge_base_urls[]": "https://example.com.au/pricing",
},
files=[("knowledge_base_files", ("price-list.pdf", f, "application/pdf"))],
)
kb = res.json()Without files, a JSON body works too:
{
"knowledge_base_name": "Clinic FAQ",
"knowledge_base_texts": [{ "title": "Opening hours", "text": "We are open 8am to 6pm, Monday to Saturday." }],
"knowledge_base_urls": ["https://example.com.au/pricing"],
"enable_auto_refresh": true,
"chunk_size": 1200,
"chunk_overlap": 150
}Response#
201 Created
{
"knowledge_base_id": "knowledge_base_9f060d81a4d8df9b04ca6d12",
"knowledge_base_name": "Clinic FAQ",
"status": "in_progress",
"knowledge_base_sources": [
{
"source_id": "source_92055d142a2829e5c8e77a9f",
"type": "text",
"title": "Opening hours",
"url": null,
"content_url": "https://api.tried.com.au/get-knowledge-base-source-content/knowledge_base_9f060d81a4d8df9b04ca6d12/source_92055d142a2829e5c8e77a9f"
},
{
"source_id": "source_21ce463cc228d0ea2a439493",
"type": "url",
"title": "https://example.com.au/pricing",
"url": "https://example.com.au/pricing",
"content_url": "https://api.tried.com.au/get-knowledge-base-source-content/knowledge_base_9f060d81a4d8df9b04ca6d12/source_21ce463cc228d0ea2a439493"
},
{
"source_id": "source_5da4e4744cb7ea63832e856c",
"type": "document",
"title": "price-list.pdf",
"url": null,
"content_url": "https://api.tried.com.au/get-knowledge-base-source-content/knowledge_base_9f060d81a4d8df9b04ca6d12/source_5da4e4744cb7ea63832e856c"
}
],
"enable_auto_refresh": false,
"last_refreshed_timestamp": null,
"chunk_size": 800,
"chunk_overlap": 100,
"auto_add_pages": false,
"crawl_roots": []
}The fields are described in Get knowledge base. Poll that endpoint until status is complete or error.
Errors#
| Status | When |
|---|---|
400 |
A field fails validation, chunk_overlap must be smaller than chunk_size, a per knowledge base limit is reached (A knowledge base holds at most 25 files, ... 500 URLs, ... 50 text snippets), or a file cannot be parsed (Could not read <filename>: ...). |
401 |
Missing or invalid API key. |
403 |
The key's IP allowlist or scopes reject the request (this route needs the kb scope), or the caller is a read-only dashboard member. |
413 |
A file is over 50 MB, the request has more than 25 files, or a JSON body is over 25 MB. |
429 |
Rate limit exceeded. See Rate limits. |
See Errors for the body format.
Retell compatibility#
Same path and method as Retell, and the same knowledge_base_name, knowledge_base_texts, knowledge_base_urls, knowledge_base_files and enable_auto_refresh fields. Chunking differs: Tried takes chunk_size and chunk_overlap instead of Retell's max_chunk_size and min_chunk_size. Source objects carry title, url and content_url for every type.