Add documents
Knovas searches the text you send it, so the way you prepare that text affects search quality.
How uploading works
- Prepare the document as plain text.
- Start an upload and say how many parts you will send. You get back an upload key.
- Send each part with that key. When the last part arrives, Knovas processes the document in the background.
Step 1: Prepare the text
Knovas does not open PDF or Word files for you. Your software extracts the text first. For the best results:
- Use simple Markdown. Mark headings with
#,##and###. Knovas uses the headings to understand the structure. There is no separate “chapter” field. - Remove repeated clutter such as page headers, footers and page numbers.
- Split at natural breaks, such as paragraphs or sections. About 500 characters per part works well.
Our free Python library knovas-extract (pip install knovas-extract) turns PDF, Word, HTML, RTF, email and text files into clean text and tables in exactly the format Knovas expects.
Step 2: Start the upload
curl -X POST https://api.knovas.ch:8443/secured/init_document_transmission \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{
"identifier": "reports/2025/q3.pdf",
"part_count": 3,
"title": "Q3 Financial Report 2025",
"description": "Quarterly results, revenue by region and outlook.",
"path": "/Reports/2025/q3.pdf"
}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
r = requests.post(f"{BASE}/secured/init_document_transmission", **AUTH, json={
"identifier": "reports/2025/q3.pdf",
"part_count": 3,
"title": "Q3 Financial Report 2025",
"description": "Quarterly results, revenue by region and outlook.",
"path": "/Reports/2025/q3.pdf",
})
r.raise_for_status()
key = r.json()["transmission_key_id"]| Field | Required? | What it does |
|---|---|---|
identifier | Yes | Your own name for the document (up to 1000 characters). Shown as pointer in results. |
part_count | Yes | How many parts you will send (1 to 10,000). |
title | No, but strongly recommended | The strongest signal for search. Up to 500 characters. |
description | No | A short description in your own words. Up to 2000 characters. |
path | No | Folder and file name. Knovas matches every search against the path and the title, so a search that names a document, such as Jahresabschluss Müller AG 2023, finds it by folder and file name. Put the client or company name and the type of document in the path or the title. |
access_groups | No | Who may see the document, e.g. ["HR"]. See Access control. |
fields | No | Typed values such as a date, an amount or a client. Only with Document fields enabled. See Typed fields. |
The answer (status 201) contains transmission_key_id. You need it in the next step.
Step 3: Send the parts
Send parts numbered 0, 1, 2 … up to part_count - 1.
curl -X POST https://api.knovas.ch:8443/secured/transmit_document_part \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{
"key": "<transmission_key_id>",
"part_number": 0,
"snippet": "# Q3 Financial Report\n\n## Highlights\n\nRevenue grew by 8% ...",
"page_number": 1
}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
parts = [
"# Q3 Financial Report\n\n## Highlights\n\nRevenue grew by 8% ...",
"## Revenue by region\n\nEMEA grew fastest ...",
"## Outlook\n\nWe expect ...",
]
for number, text in enumerate(parts):
r = requests.post(f"{BASE}/secured/transmit_document_part", **AUTH, json={
"key": key, # from step 2
"part_number": number,
"snippet": text,
})
r.raise_for_status()
print(r.json()["transmission_complete"]) # True after the last part| Field | Required? | What it does |
|---|---|---|
key | Yes | The transmission_key_id from step 2. |
part_number | Yes | The number of this part, starting at 0. |
snippet | Yes | The text of this part. |
page_number | No | The page this part starts on. Returned in search results. |
sentence_number | No | The number of the first sentence in this part. Returned in search results. |
tables | No | Tables found in this part. See Tables. |
The answer for the last part says "transmission_complete": true. Knovas then processes the document in the background, which usually takes seconds to a minute. After that it shows up in searches.
Updating a document
Upload it again with the same identifier. The new version replaces the old one. If the same content is uploaded under two different identifiers, Knovas stores it only once, and both identifiers keep working.
Page and sentence numbers
If you send page and sentence numbers, search results can point to the exact place in the document, so your users can jump straight to it.
- Page:
page_numberis the page a part starts on. If a part crosses into the next page, put a page-break character (\f) at the break. Most PDF tools add it automatically. Or simply send one page per part. - Sentence:
sentence_numberis the number of the first sentence in the part; Knovas counts on from there. If you send no sentence numbers at all, Knovas numbers the sentences for you. Either number all parts or none.
Tables
Send real tables as structured data next to the text. Knovas then keeps rows and columns together, so a question like “APAC revenue” finds the right cell.
curl -X POST https://api.knovas.ch:8443/secured/transmit_document_part \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{
"key": "<transmission_key_id>",
"part_number": 1,
"snippet": "## Revenue by region\n\nSee the table below.",
"tables": [
{
"client_table_hint": "revenue-by-region",
"title": "Revenue by region (Q3 2025)",
"headers": ["Region", "Revenue", "Change"],
"rows": [["EMEA", "12.4M", "+8%"], ["APAC", "9.1M", "+14%"]]
}
]
}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
r = requests.post(f"{BASE}/secured/transmit_document_part", **AUTH, json={
"key": key,
"part_number": 1,
"snippet": "## Revenue by region\n\nSee the table below.",
"tables": [{
"client_table_hint": "revenue-by-region",
"title": "Revenue by region (Q3 2025)",
"headers": ["Region", "Revenue", "Change"],
"rows": [["EMEA", "12.4M", "+8%"], ["APAC", "9.1M", "+14%"]],
}],
})
r.raise_for_status()Each row must have exactly as many cells as there are headers. Limits: 50 tables per part, 64 columns, 5000 rows and 1024 characters per cell.
Typed fields
Typed fields are part of Document fields, which is on for every account. Where it is off (an account Knovas took out, or an older release), Knovas ignores fields, fields_strict and fields_mode. The upload works exactly as before, and the answer has no fields section.
Send a document’s typed values when you start the upload: the date of a letter, the total of an invoice, a client or a case number. Knovas checks them right away and stores them once the document itself has been stored. An upload that fails stores no values. Later you can filter searches and list documents by these values.
curl -X POST https://api.knovas.ch:8443/secured/init_document_transmission \
--cert client_cert.pem --key client_key.pem --cacert ca_root_cert.pem \
-H "Content-Type: application/json" \
-d '{
"identifier": "Kreditoren/2024/RE-2024-0815.pdf",
"part_count": 2,
"title": "Rechnung Beispiel GmbH",
"path": "Kreditoren/2024/RE-2024-0815.pdf",
"fields": {
"Belegart": "Rechnung",
"Dokumentdatum": "15.03.2024",
"amount": "CHF 1'\''234.50",
"period": "GJ 2024",
"party": ["Beispiel GmbH"],
"reference": ["RE-2024-0815"],
"projekt": "Alpha"
}
}'import requests
BASE = "https://api.knovas.ch:8443"
AUTH = dict(
cert=("client_cert.pem", "client_key.pem"), # your certificate and private key
verify="ca_root_cert.pem", # Knovas' certificate
timeout=60,
)
r = requests.post(f"{BASE}/secured/init_document_transmission", **AUTH, json={
"identifier": "Kreditoren/2024/RE-2024-0815.pdf",
"part_count": 2,
"title": "Rechnung Beispiel GmbH",
"path": "Kreditoren/2024/RE-2024-0815.pdf",
"fields": {
"Belegart": "Rechnung", # another name for doc_type
"Dokumentdatum": "15.03.2024", # the German label of document_date
"amount": "CHF 1'234.50",
"period": "GJ 2024",
"party": ["Beispiel GmbH"],
"reference": ["RE-2024-0815"],
"projekt": "Alpha", # not a field in this account
},
})
r.raise_for_status()
answer = r.json()
key = answer["transmission_key_id"]
# No "fields" section means the fields were ignored (feature off or older server)
if "fields" not in answer:
print("Fields were not accepted; the document is uploaded without them.")
else:
print(answer["fields"]["staged"], answer["fields"]["warnings"])The answer confirms what Knovas understood:
{
"status": "success",
"message": "Transmission initialized",
"transmission_key_id": "550e8400-e29b-41d4-a716-446655440000",
"fields": {
"staged": 6,
"mapped_keys": {"Belegart": "doc_type", "Dokumentdatum": "document_date"},
"unknown_keys": ["projekt"],
"warnings": [{"key": "party", "path": "fields.party[0]", "code": "unresolved_entity"}]
}
}| Field | Meaning |
|---|---|
staged | How many keys kept at least one value. |
mapped_keys | Keys you sent as a label or another name, and the field each one means. |
unknown_keys | Keys that name no field in your account. They are ignored, unless your account is set up otherwise. |
suggest | Only when an unknown key is close to an existing one: the keys you may have meant. |
warnings | Values that were dropped or kept with a doubt. Each names the field, its JSON path and a code, never the value. |
Only treat the fields as sent when the answer contains fields.staged. An account without Document fields, or an older Knovas server, ignores fields without an error and answers without a fields section.
Keys and values
- Keys are a field’s key (
document_date), one of its other names (Datum), or its label in German, French, Italian or English (Dokumentdatum). Your account’s field list says which fields exist. A key that matches two fields is an error (400ambiguous_field). - Values are written the way your documents write them, Swiss formats included:
15.03.2024,März 2024,GJ 2024,CHF 1'234.50,4A 123/2024. See value formats. - A field that holds several values, such as
partyorkeywords, takes a list. - Send
title,descriptionandpathas top-level keys, as before. Insidefieldsthey are dropped with a warning. - Limits: 64 keys, 32 values per key and 16 KB of JSON in total.
Lenient or strict
By default, fields never stop an upload. Knovas drops a value that does not fit its field and keeps a value it has doubts about. Both are reported in warnings:
| Code | Meaning | Value |
|---|---|---|
invalid_value | The value cannot be read for this field. | Dropped |
type_mismatch | For example a list for a field that holds one value. | Dropped |
checksum_failed | A UID, IBAN or QR reference with a wrong check digit. | Dropped |
restricted_identifier | A Swiss social security number (AHV). Knovas never stores one. | Dropped |
cap_exceeded | More than 32 values for one key. | The extra values are dropped |
ambiguous_date | A slash date such as 03/04/2024, where day and month could be swapped. | Kept |
ambiguous_number | For example 1,234, read as 1.234. Write 1'234 for one thousand two hundred and thirty-four. | Kept |
unresolved_entity | A name that matches no knowledge-graph node. | Kept as a name |
ambiguous_entity | A name that matches more than one node. | Kept as a name |
If you prefer an error, send "fields_strict": true. Then any of these, an unknown key included, answers 422 invalid_fields with a list of {path, code}, and no upload is started.
Uploading again
- An upload with
fieldsreplaces all values the previous upload of this identifier sent. A key you no longer send disappears, and"fields": {}clears them all. - With
"fields_mode": "merge"only the keys you send are replaced (a key sent asnullor[]is cleared). Do not have two uploads of one identifier in progress at the same time when you use it. - An upload without
fieldskeeps the values of the previous upload. - Values you edit by hand always win over uploaded ones. See where values come from.
Errors
| Code | Error | Meaning |
|---|---|---|
400 | fields_too_large | More than 64 keys, 32 values per key or 16 KB. |
400 | invalid_fields | fields is not an object, fields_strict or fields_mode has a wrong value, or two keys name the same field. |
400 | ambiguous_field | A key or label names two fields. candidates lists them. |
422 | invalid_fields | With fields_strict: a value did not fit or a key is unknown. errors lists each one. |
422 | unknown_field | Your account is set to reject unknown keys. |
503 | doc_fields_ingest_unavailable, doc_fields_unavailable | Knovas cannot take fields right now. No upload was started; try again later. |
Fields from your folders (RemoteController)
If the Knovas RemoteController syncs your files (from your folders, or from OneDrive and SharePoint), it can fill fields for you. For each synced folder you can set:
- Fixed values for every document in the folder, such as
doc_type = invoicefor a folder of supplier invoices. - Path templates that read values from folder names. With a field
mandantin your account,{mandant}/{period}/**turnsMuster AG/GJ 2024/Rechnung_17.pdfintomandantMuster AG andperiodGJ 2024. - File properties you choose to use: the date of an e-mail as its document date, e-mails as document type E-Mail, the sender of an e-mail or the author of a PDF or Word file as author, and the language from the file’s properties.
Changing a folder’s field settings uploads all of its documents again, and each of these uploads is billed. For a value that does not depend on the folder structure, a folder default at Knovas is cheaper, because it needs no new upload. More in Document fields.
Upload checklist
- Always send a meaningful
title. Documents without titles are the most common cause of weak results. - Add a
pathif your documents live in folders, and name the client or company and the type of document in it or in the title, e.g./Clients/Müller AG/2023/Annual_accounts_2023.pdf. - Send clean text with headings, not a raw copy of the page layout.
- Use one identifier per document, and keep it stable over time.
- If you use access control, send
access_groupswith every upload. - If you send typed fields, check
fields.stagedin the answer.
Questions? Write to contact@knovas.ch.
This page describes Knovas 1.6.0. Last updated .