Knowledge bases

Knowledge bases API

Manage knowledge bases, the documents an agent answers from, and the agent attachments that turn on RAG. See Knowledge bases for the concepts and API overview for shared conventions.

All routes are organization‑scoped under /api/orgs/{orgId}/…. Authenticate with an API key (x-api-key: vai_live_…) or a user JWT (Authorization: Bearer … + X-Org-Id). Responses use the standard dashboard envelope:

{ "success": true, "data": { /* result */ } }

Roles

Endpoints enforce your workspace role:

OperationRequired role
List bases / list documentsAny member
Create base, add/upload/ingest document, delete document, attach a base to an agentowner, admin, or developer
Delete a baseowner or admin only
Detach a base from an agentany member (no role check)

A role that's too low returns 403 with { "success": false, "error": "this action requires one of: …" }.

Knowledge bases

MethodPathPurpose
GET/api/orgs/{orgId}/knowledge-basesList knowledge bases
POST/api/orgs/{orgId}/knowledge-basesCreate a knowledge base
DELETE/api/orgs/{orgId}/knowledge-bases/{kbId}Soft‑delete a knowledge base

Create a knowledge base

curl -X POST https://api.telenow.ai/api/orgs/{orgId}/knowledge-bases \
  -H "x-api-key: vai_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support FAQ", "description": "Refunds, shipping, returns" }'
FieldTypeRequiredNotes
namestringyesTrimmed; must be non‑empty
descriptionstringnoFree text

Returns 201 Created with the new base under data:

{
  "success": true,
  "data": {
    "id": "5b1f…",
    "org_id": "a02c…",
    "name": "Support FAQ",
    "description": "Refunds, shipping, returns",
    "embedding_model": "text-embedding-3-small",
    "created_by": "9d4e…",
    "created_at": "2026-06-13T10:00:00Z",
    "updated_at": "2026-06-13T10:00:00Z",
    "deleted_at": null
  }
}

List knowledge bases

GET /api/orgs/{orgId}/knowledge-bases returns the org's non‑deleted bases, newest first:

{
  "success": true,
  "data": {
    "knowledgeBases": [ { /* base */ } ],
    "total": 1
  }
}

Delete a knowledge base

DELETE /api/orgs/{orgId}/knowledge-bases/{kbId} soft‑deletes the base (sets deleted_at). It and all of its documents stop appearing and stop being searched. There is no recovery — re‑create the base if you delete it by mistake. Returns:

{ "success": true, "message": "knowledge base deleted" }

Deleting an already‑deleted or unknown base returns 404.

Documents

MethodPathPurpose
GET/api/orgs/{orgId}/knowledge-bases/{kbId}/documentsList documents
POST/api/orgs/{orgId}/knowledge-bases/{kbId}/documentsAdd a document from pasted text
POST/api/orgs/{orgId}/knowledge-bases/{kbId}/documents/uploadUpload a file (multipart)
POST/api/orgs/{orgId}/knowledge-bases/{kbId}/documents/urlIngest a public https:// page
DELETE/api/orgs/{orgId}/knowledge-bases/{kbId}/documents/{docId}Soft‑delete a document

Every add path returns 201 with the created document row. Embedding then runs in the background: the document is created with status: "pending" and flips to embedded (or failed) once chunking and embedding complete. The create call returns before that finishes — poll GET …/documents to watch the status.

Document object

FieldTypeNotes
iduuid
kb_iduuidOwning knowledge base
org_iduuid
titlestring
source_typestringinline (pasted text), upload (file), or url
source_uristring | nullOriginal file name for uploads, fetched URL for URL ingests, null for pasted text
bodystringExtracted plain text that was chunked and embedded
statusstringpending, embedded, or failed
errorstring | nullFailure reason when status is failed
created_attimestamp
updated_attimestamp
deleted_attimestamp | nullSet on soft delete

Add a document from text

curl -X POST https://api.telenow.ai/api/orgs/{orgId}/knowledge-bases/{kbId}/documents \
  -H "x-api-key: vai_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Refund policy", "body": "We refund within 30 days…" }'
FieldTypeRequiredNotes
titlestringyesTrimmed; non‑empty
bodystringyesTrimmed; non‑empty. Stored as source_type: "inline"

Upload a file

multipart/form-data with a file part and an optional title part:

curl -X POST https://api.telenow.ai/api/orgs/{orgId}/knowledge-bases/{kbId}/documents/upload \
  -H "x-api-key: vai_live_…" \
  -F "[email protected]" \
  -F "title=Refund policy"
PartRequiredNotes
fileyesThe document. See limits below
titlenoDefaults to the file's name without its extension
LimitValue
Accepted extensions.pdf, .docx, .txt, .md, .csv (content type is also sniffed as a fallback)
Maximum size20 MB
Stored source_typeupload; source_uri is the original file name

Text is extracted server‑side (PDFs via a text extractor, .docx by reading the document XML, text/markdown/CSV decoded as UTF‑8). Errors return 400:

  • unsupported file type — supported: .pdf, .docx, .txt, .md, .csv
  • could not extract text from the file (e.g. an image‑only/scanned PDF — OCR it first)
  • empty file / missing 'file' field

A file over the limit returns 413 Payload Too Large.

Ingest a URL

curl -X POST https://api.telenow.ai/api/orgs/{orgId}/knowledge-bases/{kbId}/documents/url \
  -H "x-api-key: vai_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/docs/refund-policy", "title": "Refund policy" }'
FieldTypeRequiredNotes
urlstringyesMust be a public https:// URL
titlestringnoDefaults to the page <title>, then the last URL path segment

The page is fetched once, stripped to readable text (scripts/styles/markup removed, entities decoded), and stored with source_type: "url" and source_uri set to the URL.

BehaviorDetail
Schemehttps:// only; private/internal addresses are blocked (SSRF protection)
RedirectsNot followed — a 30x response is treated as a fetch failure
Connection pinningThe host is resolved and the connection pinned to the vetted address, defeating DNS‑rebinding
Content typestext/html, text/plain, or text/markdown only
Maximum size5 MB of fetched body
Timeouts5 s connect, 10 s total

Errors return 400 — for example could not fetch URL: …, URL returned 404, an unsupported content-type (e.g. a PDF link — upload it instead), or could not extract text from the page. A body over 5 MB returns 413.

Delete a document

DELETE /api/orgs/{orgId}/knowledge-bases/{kbId}/documents/{docId} soft‑deletes the document (no recovery) and removes its chunks from retrieval:

{ "success": true, "message": "document deleted" }

An unknown document returns 404. To "update" a document, delete it and add the new version — there is no edit‑in‑place endpoint.

Attaching to an agent

A knowledge base only affects agents it's attached to. Attachment is a join — repeated attaches are idempotent.

MethodPathPurpose
GET/api/orgs/{orgId}/agents/{agentId}/knowledge-basesList the agent's bases + their document scopes
PUT/api/orgs/{orgId}/agents/{agentId}/knowledge-basesReplace the whole set in one transaction
POST/api/orgs/{orgId}/agents/{agentId}/knowledge-bases/{kbId}Attach a base to an agent
DELETE/api/orgs/{orgId}/agents/{agentId}/knowledge-bases/{kbId}Detach
curl -X POST https://api.telenow.ai/api/orgs/{orgId}/agents/{agentId}/knowledge-bases/{kbId} \
  -H "x-api-key: vai_live_…"
{ "success": true, "message": "knowledge base attached" }

Both the agent and the base must belong to orgId, or you get 404. Attach the same base to multiple agents, or several bases to one agent — at call time the agent searches across all attached bases together.

Scoping an agent to specific documents

By default an attachment covers the whole base, including documents added to it later. You can instead pin the agent to specific documents — useful when one base serves several agents and each should only speak from part of it.

Send documentIds on the attach:

curl -X POST https://api.telenow.ai/api/orgs/{orgId}/agents/{agentId}/knowledge-bases/{kbId} \
  -H "x-api-key: vai_live_…" -H "Content-Type: application/json" \
  -d '{ "documentIds": ["8f1c…", "b32a…"] }'

The scope is a three-state value, and GET returns it as document_ids on each row:

document_idsMeaning
nullThe whole base, including documents added later.
["…", "…"]Exactly those documents, and nothing else.
[]Scoped to nothing — the base stays attached but the agent never reads from it.

Rules worth knowing before you automate against this:

  • Omitting documentIds on POST leaves an existing scope alone. It does not reset the base to whole. A bare re-attach — which is what the SDK's attach() and every app install sends — can therefore never widen a scope somebody chose.
  • documentIds: [] is rejected with 400 on POST. To make a base contribute nothing, detach it. (The bulk PUT below does accept [], because it has to be able to re-send a state the server itself produced.)
  • Deleting a document removes it from every scope that pinned it. If it was the last one, the scope becomes [] rather than reverting to the whole base — retrieval narrows, it never silently widens.
  • A document id from another base is rejected with 400 document_not_in_base, naming the offending ids.

Replacing the whole set at once

PUT /api/orgs/{orgId}/agents/{agentId}/knowledge-bases states the agent's complete attachment set — bases and scopes — in a single transaction. Bases you leave out are detached.

curl -X PUT https://api.telenow.ai/api/orgs/{orgId}/agents/{agentId}/knowledge-bases \
  -H "x-api-key: vai_live_…" -H "Content-Type: application/json" \
  -d '{ "knowledgeBases": [
        { "kbId": "3f9e…" },
        { "kbId": "a71b…", "documentIds": ["8f1c…"] }
      ] }'
{ "success": true, "message": "knowledge bases updated" }

Every base and document id is validated before anything is written, so a bad id in the middle of the list can't leave a half-applied set. This is what the dashboard builders use: a partially-applied save is survivable for a plain attach list, but not when the point of the save is to exclude something.

How retrieval runs at call time

Once a base is attached, on every user turn:

  1. The caller's message is embedded with the org's embeddings model (text-embedding-3-small, 1536‑dim).
  2. The top 3 chunks across all attached bases are selected by cosine distance — restricted to the pinned documents on any base that has a scope.
  3. Any matches are injected into the model's context as a hidden system message immediately before the user turn, so the reply is grounded.

The per‑turn query embedding is metered into the session's usage whether or not it matches anything (see Billing & usage). Ingestion is metered once per document when it becomes searchable.

RAG degrades gracefully: if the vector store is unavailable in a deployment, the call still proceeds without grounding rather than failing. On Telenow Cloud the vector store is always available.

Notes

  • Embeddings provider. Documents and queries are embedded with OpenAI text-embedding-3-small (1536 dimensions). The model id is recorded on each base as embedding_model and surfaces on usage records.
  • Chunking. Documents are split on blank lines and merged into ~500‑token windows before embedding — no parameter to tune this over the API.
  • Background failures don't fail the request. If embedding fails (e.g. a missing provider key), the create call has already returned 201; the document's status becomes failed with an error. Delete and re‑add to retry.

See Knowledge bases for the dashboard walkthrough, Building agents for attaching bases in context, and API agents for managing the agents you attach them to.