Skip to main content

Knowledge Base

Verified against backend/internal/publicapi/builder_dto.go, knowledge_handler.go, and routes.go.

MethodPathScope
GET/agents/:id/knowledgeknowledge:read
POST/agents/:id/knowledgeknowledge:write
POST/agents/:id/knowledge/filesknowledge:write
PUT/agents/:id/knowledge/stagesknowledge:write
POST/agents/:id/knowledge/queryknowledge:read
DELETE/agents/:id/knowledge/*titleknowledge:write

There is no "knowledge base" object

This is worth stating plainly because every competitor's docs model a KB as a resource with an ID you create once and attach files to. This API doesn't, because the underlying table doesn't either: knowledge_base is a chunk table — one row per chunk, with a vector(1536) embedding inline. There's nothing to create ahead of time and no KB id to hold onto. Knowledge attaches directly to the agent.

Add a source

bash
curl -X POST https://api.voicematrix.ai/api/v1/ext/agents/<agent-id>/knowledge \
  -H "X-API-Key: vm_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "source_type": "url",
    "url": "https://docs.acme.com/pricing",
    "title": "Pricing page"
  }'
FieldRequiredNotes
source_typeyestext or url
contentrequired if source_type: text
urlrequired if source_type: urlFetched server-side — SSRF-validated
titleno, max 200Labels the source for listing/deletion
json
{ "agent_id": "...", "title": "Pricing page", "chunks": 14 }

Content safety on ingestion

Both text and url ingestion pass through the same multilingual (English + Hebrew) content guard used elsewhere in the platform, in enforcement mode — a flagged document is hard-blocked, not merely logged. This matters because ingested content goes straight into an agent's system context: on a Hebrew-first product, an injection attempt written in Hebrew has to be caught by the same gate as one written in English, not fall through to an English-only pattern list.

If ingestion returns 503

Embedding requires Azure OpenAI credentials to be configured. If they aren't, this endpoint answers 503 rather than accepting your document and silently storing it without an embedding — a document that "saved successfully" but is never retrievable would be a much worse failure mode.

Upload a file

bash
curl -X POST https://api.voicematrix.ai/api/v1/ext/agents/<agent-id>/knowledge/files   -H "X-API-Key: vm_live_..."   -F "file=@מחירון.txt"

201

json
{ "message": "File uploaded successfully", "filename": "מחירון.txt", "chunks": 1, "characters": 196, "warnings": null }

This is the dashboard's Knowledge-tab upload: .pdf, .txt, .docx, .csv, .json or .md, up to 10 MB, within the organization's knowledge quota (5,000 chunks; over it, 429 RATE_LIMIT_EXCEEDED). The upload is synchronous: it answers once every chunk is embedded, so allow time for a large file.

  • The file's name is its title: the document is listed, limited to stages and deleted by that name.
  • A name that already exists for the agent is refused (409 KNOWLEDGE_DOCUMENT_EXISTS), as is content that is already stored under another name. To update a document — a new price list, say — delete it, then upload the new version; nothing is embedded twice and the old text cannot stay live.
  • Text files must be UTF-8. A .txt saved as Windows-1255 (common for Hebrew on Windows) is refused with 400 INVALID_FILE_ENCODING; save it again as UTF-8. Hebrew sources should be UTF-8 .txt, not PDF: PDF text extraction reverses right-to-left text, and retrieval returns noise. warnings: ["hebrew_pdf_reversed"] flags a PDF where that happened.
  • PDF and DOCX are read in a separate, sandboxed process with a time and memory budget. A PDF over 1,000 pages, or a document over 25 MB of text, is refused with 400 FILE_TOO_LARGE; one that runs past the budget with 400 DOCUMENT_TOO_COMPLEX.
  • With an Idempotency-Key the whole request must fit in 1 MiB.

Limit a source to some stages

For an agent with a stage graph (stageGraph in /config):

bash
curl -X PUT https://api.voicematrix.ai/api/v1/ext/agents/<agent-id>/knowledge/stages   -H "X-API-Key: vm_live_..." -H "Content-Type: application/json"   -d '{"filename": "מחירון.txt", "stages": ["pricing", "close"]}'
json
{ "filename": "מחירון.txt", "stages": ["pricing", "close"], "chunks": 1 }

Stage ids must exist in the agent's stage graph (up to 12). An empty list makes the source visible in every stage. An unknown file name answers 404.

Search the knowledge

The Knowledge tab's test search: the chunks nearest to a question, as the agent retrieves them.

bash
curl -X POST https://api.voicematrix.ai/api/v1/ext/agents/<agent-id>/knowledge/query   -H "X-API-Key: vm_live_..." -H "Content-Type: application/json"   -d '{"query": "כמה עולה חבילת עסקים?", "limit": 3}'
json
{
  "query": "כמה עולה חבילת עסקים?",
  "count": 2,
  "results": [
    { "filename": "מחירון.txt", "chunk": "1/1", "content": "מחירון רעים אהובים:
חבילת בסיס — 49 שקלים לחודש…
חבילת עסקים — 149 שקלים לחודש…" },
    { "filename": "מדיניות החזרות", "chunk": "1/1", "content": "ניתן לבטל חבילה בכל עת. ביטול בתוך 14 יום מזכה בהחזר מלא." }
  ]
}

limit defaults to 5. There is no relevance cut-off: the nearest chunks always come back, so judge them by their content. For an agent with a stage graph, add "stage": "<stage id>" to search as that stage does; without it, only sources visible in every stage are searched.

List sources

text
GET /agents/:id/knowledge
json
{
  "data": [
    { "title": "מדיניות החזרות", "chunks": 1, "created_at": "2026-10-07T01:20:47.959107Z" },
    { "title": "מחירון.txt", "chunks": 1, "created_at": "2026-10-07T01:20:47.843958Z" }
  ],
  "pagination": { "total": 2, "limit": 2, "offset": 0, "has_more": false }
}

Uploaded files, text and URL sources are listed together, newest first.

Delete a source

text
DELETE /agents/:id/knowledge/*title

Note the *title wildcard, not :title. A source's title can legitimately contain a / — a url-sourced title derives from host/path (e.g. docs.acme.com/pricing), and a caller-supplied text title might too. A single-segment path parameter can't carry that; the wildcard captures the full remainder and the handler strips the leading slash. If you're constructing this URL yourself, don't URL-encode the slashes in the title — pass it through as the DELETE path directly.

Next

All pages