Knowledge Bases
Host your business knowledge on Oogam and ground voice agents on it — no tool call, no client-side orchestration. Upload documents once; the platform extracts the text and compiles it into the grounding context the model reads, so the agent answers from your facts during live calls. The compilation happens on the platform at set_context time — the model needs no retrieval tool, which is why this works with naad-sts-v1 today even while function calling is still in preview.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /kb | Create → {"kb_id": "kb_…"} |
GET | /kb | List, with document counts |
GET | /kb/{kb_id} | Detail, including per-document status |
DELETE | /kb/{kb_id} | Delete (documents and stored files included) |
POST | /kb/{kb_id}/documents | Upload — multipart file, {"url"}, or {"text"} |
GET | /kb/{kb_id}/documents | List with ingestion status |
GET | /kb/{kb_id}/documents/{doc_id} | Single-document status (the poll target) |
PUT | /kb/{kb_id}/documents/{doc_id} | Replace content in place — same doc_id, same compile order; body shapes as upload. Prefer this over delete + re-add when a document changes. |
DELETE | /kb/{kb_id}/documents/{doc_id} | Remove |
Detecting drift
Every ready document carries a content_hash (sha256 of its extracted text) and the knowledge base a combined kb_hash. The realtime context_ack reports the same kb_hash, so you can verify per session that the agent is grounded on your current content: hash your source, compare with the ack. The hash covers the source text — a knowledge base that is merely too big to fit whole reports truncation separately (kb_chars, kb_status), so "stale" and "didn't fit" can never be confused. When a document changes, replace it with PUT rather than delete + add: the doc_id and compile order stay stable.
Access & billing
Knowledge bases ship with the voice product: your key needs the naad permission and must belong to a workspace (org). CRUD operations are never billed — you only pay for the voice calls that use the knowledge. A key without the product gets 403 insufficient_permissions; a key with no workspace gets 403 org_required.
Create → ingest → poll
Ingestion is asynchronous: uploads return 202 with status pending, then move to ready or failed (with a human-readable error). Accepted sources: PDF, DOCX, TXT, MD, URL, raw text. Plain text is usually ready by the time the 202 lands; files and URLs within a few seconds.
# 1. Create
curl -X POST https://platform.oogam.ai/v1/kb \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Sharma Clinic", "description": "Hours, fees, address"}'
# → {"kb_id": "kb_37bd2418dd25431c9762", ...}
# 2. Add documents — a file…
curl -X POST https://platform.oogam.ai/v1/kb/kb_37bd…/documents \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-F "file=@pricelist.pdf"
# → 202 {"doc_id": "doc_…", "status": "pending"}
# …or a page…
curl -X POST https://platform.oogam.ai/v1/kb/kb_37bd…/documents \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://sharmaclinic.example/faq"}'
# …or plain text
curl -X POST https://platform.oogam.ai/v1/kb/kb_37bd…/documents \
-H "Authorization: Bearer $OOGAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Open Mon–Sat 9am–7pm. Sunday closed.", "name": "Hours"}'
# 3. Poll until every document is ready
curl https://platform.oogam.ai/v1/kb/kb_37bd… -H "Authorization: Bearer $OOGAM_API_KEY"Adding documents
POST /kb/{kb_id}/documents takes exactly one of three shapes, chosen by content type:
| Source | How to send | Notes |
|---|---|---|
| File | multipart/form-data with a file part | PDF, DOCX, TXT, MD. Up to 10 MB. Missing part → 400 missing_file; wrong type → 400 unsupported_file_type. |
| URL | JSON {"url": "https://…", "name"?: "…"} | The page is fetched and its text extracted. Must be a public http(s) URL — private/internal addresses are refused. Invalid → 400 invalid_url. |
| Text | JSON {"text": "…", "name"?: "…"} | Up to 200,000 characters. Ready almost immediately. Over the cap → 400 input_too_long. |
Each returns 202 {"doc_id": "doc_…", "status": "pending"}. Poll the document (or the KB detail) until status is ready. Creating a KB — POST /kb {"name", "description"?} — requires a 1–120 character name and returns 201 {"kb_id", "name", "created_at"}.
Object fields
A knowledge base, from GET /kb/{kb_id}:
| Field | Description |
|---|---|
kb_id | Stable id (kb_…). |
name / description | As set on create. |
created_at | ISO 8601 timestamp. |
document_count / documents_ready | Present in the list view — totals for quick status. |
documents | Present in the detail view — the array of document objects below. |
A document, from the documents list or single-document poll:
| Field | Description |
|---|---|
doc_id | Stable id (doc_…). |
name | File name, page host, or your supplied name. |
source_type | file, url, or text. |
status | pending → ready | failed. |
error | Present only when status is failed — a human-readable reason. |
bytes / chars | Source size and extracted-text length. |
created_at | ISO 8601 timestamp. |
{
"kb_id": "kb_37bd2418dd25431c9762",
"name": "Sharma Clinic",
"description": "Hours, fees, address",
"created_at": "2026-08-22T09:25:20.405Z",
"kb_hash": "3568f557d16c…",
"documents": [
{ "doc_id": "doc_5180…", "name": "Hours", "source_type": "text",
"status": "ready", "bytes": 54, "chars": 54,
"content_hash": "a0c20222921f…",
"created_at": "2026-08-22T09:25:31.014Z" },
{ "doc_id": "doc_95b4…", "name": "pricelist.pdf", "source_type": "file",
"status": "failed", "error": "No readable text found (is it a scan?).",
"bytes": 20481, "chars": 0,
"created_at": "2026-08-22T09:25:44.221Z" }
]
}DELETE on a KB or a document returns {"kb_id" | "doc_id": "…", "deleted": true} and removes the stored files too.
Grounding a live call
Pass the kb_id in set_context on the realtime WebSocket. The platform compiles the knowledge base's ready documents into the grounding context that answers business questions during the call — the knowledge is authoritative, and the agent says it doesn't have a detail rather than inventing one. The compiled context is capped at about 6,000 characters, so the highest-value documents come first.
// Ground a Live talk session on your knowledge base:
ws.send(JSON.stringify({
type: "set_context",
kb_id: "kb_37bd2418dd25431c9762",
system: SYSTEM_PROMPT, // persona and rules, as usual
}));Verify it loaded. The context_ack that comes back reports exactly what was compiled — assert kb_chars > 0 in your integration test rather than trusting that the attach worked:
{"type":"context_ack","kb_id":"kb_…","chars":239,
"kb_documents":1,"kb_chars":5005,"kb_status":"loaded","ok":true}An unknown or another workspace's kb_id is refused at set_context with kb_not_found, and a knowledge base whose documents are not yet ready with kb_empty — the session is never left silently ungrounded. If the compiled text had to be trimmed to fit alongside your own prompt, kb_status is loaded_truncated.
Limits
| Limit | Value |
|---|---|
| Knowledge bases per account | 20 |
| Documents per knowledge base | 50 |
| File size | 10 MB |
| Extracted text per document | 200,000 characters |
About tenant
tenant is your workspace (organization) id — the same scope that owns your API keys, knowledge bases and cloned voices. It is provisioned automatically with your account; you never create one explicitly. On the realtime socket it may appear as a ?tenant= query parameter (the dashboard sets it for you) and in voice_rejected errors when a voice belongs to a different workspace. Your own tenant value is shown on each of your cloned voices in GET /voices.
Endpoint errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | KB name missing or not 1–120 characters. |
| 400 | kb_limit_reached / kb_document_limit_reached | Account limits (20 KBs, 50 docs) reached. |
| 400 | missing_file / missing_input | No file part, and no url/text either. |
| 400 | unsupported_file_type / file_too_large / invalid_url / input_too_long | Document validation failures (type, 10 MB, URL, 200k chars). |
| 403 | insufficient_permissions / org_required | Key lacks the naad product, or is not attached to a workspace. |
| 404 | not_found | Unknown (or another workspace's) kb_id / doc_id. |