Manage the knowledge base
The assistant answers callers from the business’s knowledge base: text notes,
uploaded files and imported websites, the same documents the owner sees under
Documenti in Talki. /v1/documents reads them under knowledge:read and
writes them under knowledge:write, an opt-in scope. Neither scope includes the
other, so an integration that writes documents and then follows their status
needs both; see Scopes.
Whatever a document says, the assistant tells every caller who asks. The owner can edit or delete what you write, and nothing asks for their approval first: agree with the business what you add.
| Task | Request |
|---|---|
| List documents, of every location or one | GET /v1/documents |
Read one, with its status |
GET /v1/documents/{document_id} |
| Read the text the assistant searches | GET /v1/documents/{document_id}/text |
| Download an uploaded file | GET /v1/documents/{document_id}/file |
| List a website’s pages | GET /v1/documents/{document_id}/pages |
| Add a note, a file or a website | POST /v1/documents |
| Find a site’s pages before importing it | POST /v1/documents/discover |
| Replace a note or a file, import a website again | PUT /v1/documents/{document_id} |
| Rename a document, switch it off or on | PATCH /v1/documents/{document_id} |
| Delete a document | DELETE /v1/documents/{document_id} |
| Drop one page of a website | DELETE /v1/documents/{document_id}/pages/{page_id} |
Choose a source
Section titled “Choose a source”- Text note: content you already hold as text, such as an answer, a price list or a policy. The assistant reads exactly what you send, title included, and you replace it in place. For a FAQ you manage, one short note per question lets you replace, switch off or delete each entry on its own.
- File: a PDF, DOCX, XLSX, TXT or CSV the business already has. Its text is
read after the upload, and the file is kept for download when
file_availablesays so. - Website: pages of the business’s site. Their text is read from the pages you choose; import the site again when it changes.
Add a text note
Section titled “Add a text note”curl --fail-with-body -X POST https://api.talki.it/v1/documents \ -H "Authorization: Bearer $TALKI_API_KEY" \ -H "Idempotency-Key: note-bridal-sundays" \ -H "Content-Type: application/json" \ --data '{"title":"Bridal hair","body":"Bridal hair is booked on the first Sunday of each month, by appointment only."}'The answer is 202, with the document still processing:
{ "id": "3f6b1c9e-8d27-4a5f-b0e4-6c2d9a7f1e83", "location_id": "0b7d4e19-8a2c-4f61-9d3e-5c1a7b2f8e40", "title": "Bridal hair", "source": "text", "mime_type": "text/markdown", "size_bytes": 77, "chunk_count": 0, "file_available": false, "status": "processing", "status_error": null, "enabled": true, "faq": false, "copy_group_id": null, "created_at": "2026-11-02T09:30:12Z", "updated_at": "2026-11-02T09:30:12Z"}Idempotency-Keyis required onPOST /v1/documents. A create retried without it would add a second document, which the assistant reads beside the first.?location_id=names the location whose assistant uses the document; without it, the main location’s.location_idsin the body adds a copy at each other location named; see Copies at other locations.
Upload a file
Section titled “Upload a file”curl --fail-with-body -X POST https://api.talki.it/v1/documents \ -H "Authorization: Bearer $TALKI_API_KEY" \ -H "Idempotency-Key: upload-price-list-2026-11" \ -F "file=@price-list.pdf;type=application/pdf"- Send the file as
multipart/form-data, in a field namedfile. Its name becomes the document’stitle.location_idsgoes as one form field per location. - A part sent with no type, or as
application/octet-stream, is typed by its file extension. Any other type that is not one of the accepted ones, or content that does not match its type, is refused with415 unsupported_file_type. - The
202carries the document, and its copies exist from then too, before the file is read. - Reading can still fail after the
202: a file with no readable text, an encrypted DOCX or XLSX, or a file over one of the reading limits in Limits. The document then turnsfailed, withstatus_error.
Import a website
Section titled “Import a website”-
POST /v1/documents/discoverlooks for the pages of the site aturl, from its sitemap or its links, and returns them with the plan’smax_pages. It imports nothing.Terminal window curl --fail-with-body -X POST https://api.talki.it/v1/documents/discover \-H "Authorization: Bearer $TALKI_API_KEY" \-H "Content-Type: application/json" \--data '{"url":"https://salone-esempio.it"}' -
POST /v1/documentswithsource: "website", theurland thepagesyou chose. Withoutpages, only the page aturlis read.Terminal window curl --fail-with-body -X POST https://api.talki.it/v1/documents \-H "Authorization: Bearer $TALKI_API_KEY" \-H "Idempotency-Key: site-salone-esempio" \-H "Content-Type: application/json" \--data '{"source":"website","url":"https://salone-esempio.it","pages":["https://salone-esempio.it","https://salone-esempio.it/prezzi"]}' -
Once the document is
readyorfailed,GET /v1/documents/{document_id}/pageslists each page with itsstatus, which says whether the page was read and is searched; see List a website’s pages. -
DELETE /v1/documents/{document_id}/pages/{page_id}drops a page and what it said; the assistant stops using it once the document isreadyagain. Dropping the last page that was read deletes the document, withdocument.deleted.
- A site already imported at a location is refused there with
409 conflict, and the message names its document: import that one again withPUT. - An address that is not a public
httporhttpsaddress is refused with400 invalid_parameter, as is apageslist longer than the plan allows. - Discovery that cannot read the site answers
502 api_error: check the address, or retry later. - While the site is being imported,
PUTand page deletes are refused with409 document_not_ready: retry once the document isreadyorfailed. - A page delete that would leave the site with no text to search, while other
pages are still listed as read, is refused with
409 conflict. A retry gets the same answer: import the site again with thepagesto keep, or delete the document. - Page requests on a document that is not a website, listing or deleting, are
refused with
400 invalid_parameter. - A website’s
titleis its address, and cannot be renamed.
Wait for status
Section titled “Wait for status”The assistant uses a document only once it is ready. Every write that changes
what a document holds answers before indexing, with the document processing;
that includes a note’s new title.
- Each change of
statussends adocument.updated. Without webhooks, pollGET /v1/documents/{document_id}. failedcomes withstatus_error, a sentence for people: show it, and do not branch on its text. Send the content again withPUT: a newPOSTadds a second document, or for a website answers409 conflict.- A document that already had content keeps answering with it while it is
processingagain, and after afailed. - A document still
processingsix hours after its last change turnsfailed. - A file upload or a website import that failed before holding anything is
deleted seven days after it failed, with
document.deleted. GET /v1/documents/{document_id}/textreturns a note’s text as last written, before it is indexed. For a file or a website it is empty until the document is firstready.
Replace a document
Section titled “Replace a document”PUT /v1/documents/{document_id} keeps the document’s id:
- A note takes JSON with its new
body, andtitleto rename it in the same request. It can be replaced at any time. - A file takes
multipart/form-datawith the newfile, under the same rules as an upload. The document’stitleandsize_bytesbecome the new file’s once it isready. - A website takes JSON with the
pagesto read, or{}to read again the pages it holds. A site whose import failed before any page was read holds none, and{}reads only its address: sendpagesagain.
The document turns processing, and keeps answering with its previous content
until the new one is ready.
- A note is never refused for its
status. - A file can be replaced again while its new version is still being read: the
newer file takes its place. Until the last file sent is in place, a
replacement is refused with
409 document_not_ready: while the file’s first upload is stillprocessing, and for a moment while a new version that has been read replaces the previous one. Retry shortly. - A website is refused with
409 document_not_readywhile it is being imported: retry once it isreadyorfailed. For a moment while a new version switches in, areadysite is refused the same way: retry shortly. It is refused with409 conflictwhen an older document holds the same site at its location: import that one again, or delete one of the two. - With
?also=copies, thedocument_not_readyrefusals apply to each copy too, and the message names the copy that holds the request up: retry, or send it without?also=copies. - A body of the wrong kind for the document, or a JSON field the body does not
take, is refused with
400 invalid_parameter; for a field, the message names it.PUTtakes nosource, so a JSON create body sent again withPUTis refused.
Rename and switch off
Section titled “Rename and switch off”PATCH /v1/documents/{document_id} takes title, enabled or both. An omitted
field is left as it is, and null is refused.
enabled: falsekeeps the document, but the assistant stops using it untilenabled: true.- A note’s new title is indexed again, as its text is.
- A file’s title cannot change while its file is
processing, nor, with?also=copies, while a copy’s file is: the request is refused with409 document_not_ready. Rename it once the file isreadyorfailed, or without?also=copies. A note is never refused. - A
PATCHthat changes nothing sends nodocument.updated.
Copies at other locations
Section titled “Copies at other locations”A document belongs to one location. On an account with several, location_ids
on POST /v1/documents makes a copy at each location named: a document of its
own, with its own id, sharing copy_group_id. GET /v1/documents?copy_group_id= lists a group.
?also=copiesonPUT,PATCH,DELETEand a page delete applies the change to every copy too. Without it, only the document named changes, and its copies keep what they hold.- Each copy counts against the knowledge storage on its own.
409 request_conflictmeans another request changed the copies at the same moment: retry.
Download the original file
Section titled “Download the original file”GET /v1/documents/{document_id}/file returns an uploaded file as it was
received, with its type in Content-Type and its name in Content-Disposition,
when the document’s file_available is true.
file_availableturnstrueshortly after a file is firstready, and later for a copy at another location. While it isfalsethe download answers404 resource_not_found.- A note, a website, a file whose first upload failed and a file whose upload
was not kept have none to return. For a note, a website or a file that was
read,
GET /v1/documents/{document_id}/textreturns its text instead. - A file being replaced returns its previous version until the new one has been
read. It then has none until shortly after the new version is
ready, and returns the new version from then. 503 api_errormeans the file cannot be read right now: retry later.
Edit the FAQ note
Section titled “Edit the FAQ note”The answers taught with
POST /v1/unresolved_requests/{request_id}/teach
go to the location’s FAQ note, the document with faq: true; a location has one
at most. It is a text note like any other: PUT replaces its text, PATCH
switches it off, and after a DELETE the next answer taught there starts a new
one. The business edits it in Talki too, so read its text before you replace it.
Limits
Section titled “Limits”| What | Limit |
|---|---|
| Note | Text up to 100 KB in UTF-8; title up to 200 characters |
| File | PDF, DOCX, XLSX, TXT or CSV, up to 10 MB or the plan’s lower limit |
| What is read from a file | Up to 4 MB of text, 150 PDF pages, and 50,000 rows and 100 columns per spreadsheet, or the plan’s lower limits for pages and rows; a file over them, or an encrypted DOCX or XLSX, turns failed |
| Website import | Up to the plan’s pages per import, max_pages from discovery |
| Addresses | Public http or https, up to 2,048 characters |
| Knowledge storage | The plan’s, across every document of the account, each copy counted on its own |
| Sandbox and test accounts | 5 MB in all, 1 MB per file, 5 pages per import, 20 pages per PDF, or the plan’s lower limits |
| Rate | POST, PUT, PATCH and discovery share 60 document writes a minute, on top of the write limit; see Rate limits |
A write over the knowledge storage is refused with 413 payload_too_large; a
website import over it turns failed instead, with status_error.
Errors
Section titled “Errors”| HTTP | Code | When |
|---|---|---|
| 400 | idempotency_key_required |
POST /v1/documents without Idempotency-Key |
| 400 | invalid_parameter |
An empty title or body, a title over 200 characters, pages over the plan’s limit, an address that is not public http or https, a body of the wrong kind for the document, a JSON field the body does not take (the message names it), a website’s new title, listing or deleting pages of a document that is not a website, or null in a PATCH |
| 404 | resource_not_found |
An unknown document, page or location, or a document with no file to download |
| 409 | conflict |
The website is already imported at that location, or an older document holds it there (the message names that document); or a page delete would leave the site with no text to search. Not retryable as sent |
| 409 | document_not_ready |
A file replacement before the last file sent is in place (retry shortly), a file’s rename while its file or a copy’s file is processing, a website re-import or page delete during an import, or a website re-import while a new version switches in (retry shortly). With also=copies, the message names the copy. Never for a note |
| 409 | request_conflict |
Another request changed the document’s copies at the same moment |
| 413 | payload_too_large |
A file over the upload size, a note over 100 KB, or the knowledge storage full for the plan |
| 415 | unsupported_file_type |
A file of a type not accepted or not matching its content, source: "file" in JSON, or a body neither application/json nor multipart/form-data |
| 502 | api_error |
Discovery could not read the site |
| 503 | api_error |
The file, or the text of a file or website, cannot be read right now, including to drop a page |
Keep a copy in sync
Section titled “Keep a copy in sync”| Event | When | data.object |
|---|---|---|
document.created |
A document is added: a note, an upload or a website, by a person, an app or an answer taught; one event per document, each copy included | Document |
document.updated |
Its title, content or enabled changes, it turns processing again, or its indexing ends ready or failed |
Document |
document.deleted |
It is deleted, by a person or an app, with its last page that was read, or as a failed upload or import that held nothing | Document, as it was |
- The events need
knowledge:read. Receive them as a webhook or as app events. - They report every change, whoever made it: the owner in Talki, an app, an
answer taught, or the indexing, whose
readyorfailedhasorigin.sourcesystem. A location added in Talki can bring copies of another location’s documents, each with its owndocument.created. - Your own writes come back too, with
origin.sourceapiand your credential’s ID inorigin.credential_id. A response carries only the document named: apply the events for the copies a write makes withlocation_idsor changes with?also=copies, or list them with?copy_group_id=, rather than dropping your own. - Switching a document on or off, and some
statuschanges, leave itsupdated_atas it is (see Retrieve a document), so order its events by the envelope’screated_atand skip an older one, or read the document again on each event. GET /v1/documentsis a newest-first list, not a change feed. To rebuild a copy, or to find deletions whose event you missed, walk it in full and drop the ids it no longer returns.
