Knowledge bases#
A knowledge base is a collection of type text, managed with your API key (dn_live_...) under https://api.decisionnode.com/v1/collectionshttps://api.decisionnode.com/ beside your face collections, or as a folder in the console's Knowledge section. A request reads one knowledge base, so keep the documents a decision needs together: a claims policy and its mission briefs in one, your moderation rules in another.
curl -X POST https://api.decisionnode.com/v1/collections \
-H "Authorization: Bearer $DECISIONNODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"text","name":"Claims policy","scope_key":"mission"}'scope_keyis optional: the metadata key that scopes a document to one mission, customer or site. It is fixed for the knowledge base's life. See Scope.chunksandtokenscount the passages a request can read now;status_countsthe documents by status;versionrises with every change a search can see.pinnedsays what pinned documents take of their cap:tokensagainstcap, and the scope value that takes the most. See Pinned documents.- Limits: knowledge bases count with your face collections, 100 collections and 1,000,000 items a workspace, and a workspace stores up to 200,000 passages; write to us to raise them.
Writing a document through the API#
Send the document's text as Markdown (the default) or plain text. Markdown is cut at its headings, so each passage carries the heading it sits under and the response can cite it; plain text is cut at its paragraphs. A document is processed in the background: the call answers 202 with the document queued, or, with ?wait=true, waits up to 10 seconds and answers 201 once it is ready.
curl -X POST https://api.decisionnode.com/v1/collections/col_3f6c1a9e0b2d4c7f8a1e5d9b2c4f6a80/items?wait=true \
-H "Authorization: Bearer $DECISIONNODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Claims policy 2026","text":"# Claims policy 2026\n\n## Receipts\n\nReject any claim over 500 SEK without a receipt.\n\n## Photos\n\nPhotos of damage must show the date they were taken.","format":"markdown","reference":"policy-2026","pinned":true}'The document
titlestringrequired- 1 to 200 characters. Shown with every passage and cited in the response.
textstringrequired- UTF-8, up to 512 KiB once stored. A text with a NUL character is
422invalid_text; over the size,413item_too_large. formatstringdefault"markdown"markdownortext.referencestring- Your own key for the document, up to 64 characters of letters, digits and
_ . : -, unique in the knowledge base. Cited with each passage. Left out, the document's ownitm_id is its reference. pinnedbooleandefaultfalse- Give this document to every request of the knowledge base. See Pinned documents.
pin_rankintegerdefault0- 0 to 1,000: the order of pinned documents, lowest first.
labelstring- Up to 128 characters of your own.
metadataobject- Up to 20 flat fields of your own, such as
regionorproduct, that a request can filter on. On a scoped knowledge base, the scope key's value goes here.
- Stored as sent, tidied once. Line endings become
\nand invisible control characters are removed. The result is the stored text that?include=textreturns and that every citation's byte offsets point into. - Change a document with
PUT: a new text or title is processed again while the old version keeps answering requests, then replaced at once. Sending the same text again changes nothing and costs nothing. - Pin, rename the reference, or change metadata with
PATCH: applied at once, never processed again, never billed. - Retry safely with an
Idempotency-Keyheader, as for every collection write. - Find documents: the documents list takes
?q=(1 to 200 characters of a title, file name or reference, any case) and?sort=newest(the default),oldestortitle. - Indexing limits. A workspace's documents are processed 2 at a time. Writes of new text (adding or replacing a document, or a new title) are refused with
429rate_limit_erroronce 1,000 documents are waiting (details.reasoningest_queue_full) or 2,000,000 new tokens were indexed within the hour (ingest_tokens_per_hour); waitRetry-Afterand send it again.
Adding files#
Files go in through the API or the console's Knowledge section, where you drop them into a folder. Each file becomes a document. Files other than text and Markdown are turned into Markdown first, keeping their headings, lists and tables, so their passages cite headings and, for a PDF, pages.
| Format | Extensions | How it is read |
|---|---|---|
| Plain text | .txt | As it is, cut at its paragraphs |
| Markdown | .md, .markdown | As it is, cut at its headings |
.pdf | Turned into Markdown, page by page; up to 100 pages | |
| Word | .docx | Turned into Markdown |
| PowerPoint | .pptx | Turned into Markdown, slide by slide |
| Excel | .xlsx | Turned into Markdown tables |
| HTML | .html, .htm | Turned into Markdown |
| Images | .png, .jpg, .jpeg, .tif, .tiff, .webp | The text they show, turned into Markdown |
- Upload limits: a file up to 50 MB, a PDF up to 100 pages, and up to 512 KiB of text once it is read. A file's type and extension must agree.
- Protected or broken files fail with a reason you can read in the folder, and a file that holds no text fails as empty. A scanned page is read like an image.
- The original file is not kept. An uploaded file is deleted once its text has been read successfully: we keep the extracted text and its passages, not the file. When reading a file fails for good, the file is deleted too and the document stays listed with its reason, counting toward nothing, until you delete it: add the file again to try once more.
- Through the API, a file takes three calls: presign it with
POST /v1/uploadsand"purpose": "knowledge",PUTthe bytes to the URL it answers, then add it with{"upload_id": "upl_..."}in place oftext(atitleis optional; it defaults to the file name without its extension). An upload makes one document, within the hour.PUTwith a newupload_idreplaces a file document's file. See Add a document.
A document's status#
status | What it means |
|---|---|
uploading | A file added in the console whose upload has not arrived yet; never a document written through the API |
queued | Accepted and waiting its turn |
processing | Being read, cut into passages and indexed; stage says which step (extracting for a file, chunking, embedding) and progress how far |
ready | Its newest version answers requests |
failed | Its newest version could not be processed; error says why. An earlier version, if any, keeps answering. A document that never got ready counts toward nothing (no limit, count, passage or storage) and stays listed until you delete it, one at a time or all together with DELETE .../ |
servingsays which version answers requests:current(the newest),previous(an older one, while the newest is processed or after it failed) ornone.delayedistruewhen a document has waited more than 15 minutes because indexing is unavailable on our side. It is never a failure: the document goes on as soon as it can, and nothing is billed twice.erroron a failed document:pinned_budget_exceeded(the pinned documents are over their cap),chunks_limit_reached(the workspace's passages are at their limit) orembedding_rejected(the text could not be indexed). In the console, a file's reading can also fail as an unsupported type, too many pages, an unreadable or protected file, no text found, or text over the size; the console offers Retry where a second try can help.warnings:possible_secret(the text looks like it holds a key or a password) andpossible_instruction(it reads like an instruction to the model). Both are shown to you and change nothing: a passage is always read as text, never followed.- In the console, a document's progress shows each step: uploading, pending (
queued), extracting (files only), chunking and embedding, with the passages done so far.
Pinned documents#
A pinned document is given to every request that names its knowledge base, whatever the request asks, before any passage that matched. Pin the rules that must always apply: a limit, an exclusion, a definition the decision cannot do without. Leave the rest unpinned; they are found when a request needs them.
- A cap keeps requests fast. Pinned documents take at most 512 tokens a request (about 350 words) and 64 KiB of text. A pin or a pinned text that would pass it is
409pinned_budget_exceeded. For rules that need more room, write to us: we can raise a knowledge base's cap up to 4,096 tokens, at some added latency on every request that names it. - Long policy, short core. For a three-page policy, pin a short document with the rules that must always hold and leave the full policy unpinned beside it.
- Order pinned documents with
pin_rank, lowest first. - Filters apply to pins too. A pinned document that a request's
matchorfilterexcludes is left out and counted in the response'spinned_filteredwarning. See When a rule does not appear.
Scope: one knowledge base, many missions#
A knowledge base created with a scope_key, say mission, holds global documents (no mission in their metadata) and scoped ones ("metadata": {"mission": "M-42"}). A request names the mission in the booster's scope option and reads the global documents and that mission's, never another mission's.
curl -X POST https://api.decisionnode.com/v1/collections/col_3f6c1a9e0b2d4c7f8a1e5d9b2c4f6a80/items \
-H "Authorization: Bearer $DECISIONNODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Mission M-42 brief","text":"## Special instructions\n\nPhotos of damage taken after 2026-09-01 count as current.","reference":"m-42-brief","metadata":{"mission":"M-42","region":"SE"}}'| Knowledge base | The request | It reads |
|---|---|---|
| No scope key | No scope | Every document |
Scope key mission | "scope": "M-42" | Global documents and M-42's, pinned ones included; never M-41's |
Scope key mission | No scope | Global documents only, with the warning scope_not_named |
- The scope key is set only through
scope. Naming it inmatchorfilteris400, so no filter can reach another mission's documents. - Pinned documents count per scope: the cap holds for the global pins plus those of any one mission.
- To change a scope key, create a new knowledge base, copy the documents into it (read each with
?include=text), switch your requests over and delete the old one.