What you tell your agents — entries an agent can look up by meaning, written and assigned from your own systems.
When and why to call these: the knowledge guide.
Categories in use
Section titled “Categories in use”GET /v1/knowledge/categories
Every category your organisation has put on an entry. Not narrowed by any filter — a picker that only offers what survives the current selection empties itself as soon as it is used.
curl "https://api.lumavox.ai/v1/knowledge/categories" \ -H "Authorization: Bearer $API_KEY"Responses
| Status | Description |
|---|---|
200 |
application/json — Every category, alphabetically |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
data |
array of string | |
next_cursor |
string or null | Pass as cursor for the next page; null when this is the last. |
What the knowledge is used for
Section titled “What the knowledge is used for”GET /v1/knowledge/insights
Which entries your agents drew on, by how many replies, and the newest questions whose lookups found nothing — the gaps worth filling. Counted from what each reply recorded, over the last days (1-90, default 7). An agent with its knowledge in its prompt looks nothing up, so it does not appear here.
curl "https://api.lumavox.ai/v1/knowledge/insights" \ -H "Authorization: Bearer $API_KEY"Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
days | integer | 1–90. Default 7. |
Responses
| Status | Description |
|---|---|
200 |
application/json — Use per entry, and the unanswered questions |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
days |
integer | |
entries |
array of KnowledgeEntryUsage | |
entries[].entry_id |
string | |
entries[].replies |
integer | Replies that drew on this entry in the window. |
unanswered |
array of UnansweredQuestion | The newest fifty, newest first. |
unanswered[].agent_id |
string | |
unanswered[].agent_name |
string | |
unanswered[].asked |
string | What the person said. |
unanswered[].asked_at |
string (date-time) | |
unanswered[].outcome |
"none", "failed" |
none — nothing relevant enough; failed — the lookup could not be done. |
unanswered[].query |
string | What was searched for. |
unanswered[].session_id |
string | |
unanswered[].end_user_id |
string or null | Who asked, by eu_... id — the conversation is theirs, read per end user. Null once they are deleted. |
Test a question against the knowledge
Section titled “Test a question against the knowledge”POST /v1/knowledge/search
Runs one question exactly as an agent’s lookup would — the same embedding, the same index, the same relevance floor — and returns the pieces it would be handed, with their scores. Name agent_id to search what that agent has; omit it to search every entry. Costs nothing and changes nothing.
curl -X POST "https://api.lumavox.ai/v1/knowledge/search" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"Can I get a refund after 20 days?"}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string or null | Search what this agent can find. Omitted, every entry of the organisation that can be searched. | |
max_results | integer or null | 1–8. | |
query | string | Required | A question, as a person would ask it. 1–500 characters. |
Responses
| Status | Description |
|---|---|
200 |
application/json — What a lookup would find |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
agent_id is not one of yours. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
503 |
Knowledge search is not configured. |
200 response body
| Field | Type | Description |
|---|---|---|
embed_ms |
integer or null | How long the question’s embedding took; null unasked. |
hits |
array of KnowledgeSearchHit | |
hits[].entry_id |
string | |
hits[].score |
number | Cosine similarity, 0-1. Nothing under the platform’s agents.knowledge.min_score (GET /v1/platform/limits) is handed over. |
hits[].text |
string | The piece the agent would read. |
hits[].title |
string | |
mode |
"lookup", "prompt" or null |
When agent_id was given: prompt if that agent has its whole knowledge in its prompt and looks nothing up — the results show what a lookup would find all the same — or lookup. Null without one. |
outcome |
"found", "none", "failed" |
found, none (nothing relevant enough), or failed. |
query_ms |
integer or null | How long the index search took; null unasked. |
List or search knowledge
Section titled “List or search knowledge”GET /v1/knowledge
Every entry in your organisation, most recently changed first, with the agents each is assigned to, a page at a time.
q is a plain text match over title, summary and body — not the semantic search the agent uses. This is for finding an entry somebody wrote; the agent’s own lookup answers a question by meaning.
agent_id, category and status narrow further and combine. status is about the latest body: pending while it is indexed, ready once it is, failed with the reason in error. searchable says whether an agent can find the entry at all — an edited entry stays searchable at its previous version while the new one indexes, and keeps it if the new one fails.
curl "https://api.lumavox.ai/v1/knowledge" \ -H "Authorization: Bearer $API_KEY"Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
q | string | A plain text match over title, summary and body. At most 200 characters. | |
agent_id | string | Only the entries this agent has. | |
category | string | At most 120 characters. | |
status | "pending", "ready", "failed", "importing" | ||
limit | integer | How many to answer, 1 to 200. 1–200. Default 50. | |
cursor | string | The next_cursor of the page before. At least 1 characters. |
Responses
| Status | Description |
|---|---|
200 |
application/json — One page of entries |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
data |
array of KnowledgeInfo | |
data[].agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
data[].agents[].id |
string | |
data[].agents[].name |
string | |
data[].body |
string | |
data[].body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
data[].category |
string or null | |
data[].chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
data[].created_at |
string (date-time) | |
data[].error |
string or null | Why indexing failed, when it did. |
data[].id |
string | |
data[].refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
data[].refreshed_at |
string (date-time) or null | When the page or file was last read. |
data[].searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
data[].source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
data[].source_name |
string or null | The file’s name, or the page’s address. |
data[].status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
data[].summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
data[].summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
data[].title |
string | |
data[].updated_at |
string (date-time) | |
data[].version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
next_cursor |
string or null | Pass as cursor for the next page; null when this is the last. |
Write a knowledge entry
Section titled “Write a knowledge entry”POST /v1/knowledge
Needs agent:write. Counts against your rate limit like a generation, because indexing is model work.
Creates an entry for your organisation and queues it for indexing. It comes back pending and no agent can find it yet — poll GET /v1/knowledge/{id} until status is ready, or failed with the reason in error.
agent_ids assigns it to any number of agents at once and may be empty — an entry nobody has yet is a draft, not a fault. Assigning later is PUT /v1/knowledge/{id}/agents. Ids that are not yours are skipped rather than failing the write.
summary is normally left out: the indexer writes a one-line summary for the agent’s prompt, which is how it knows a lookup is worth making.
curl -X POST "https://api.lumavox.ai/v1/knowledge" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"body":"...","title":"..."}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
agent_ids | array of string | At most 200 items. | |
body | string | Required | 1–100000 characters. |
category | string or null | At most 120 characters. | |
summary | string or null | At most 200 characters. | |
title | string | Required | 1–255 characters. |
Responses
| Status | Description |
|---|---|
201 |
application/json — The entry, pending until indexed |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
409 |
The organisation already has as many entries as the platform allows. Delete one before adding another. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
201 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Delete, assign or unassign many entries at once
Section titled “Delete, assign or unassign many entries at once”POST /v1/knowledge/bulk
Needs agent:write.
delete removes up to 200 entries for every agent that had them. assign and unassign add the named agents to each entry, or take them away, and leave each entry’s other agents alone — unlike PUT /v1/knowledge/{entry_id}/agents, which replaces one entry’s set. Ids that are not yours come back in skipped.
curl -X POST "https://api.lumavox.ai/v1/knowledge/bulk" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"action":"delete","entry_ids":[]}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
action | "delete", "assign", "unassign" | Required | |
agent_ids | array of string | For assign and unassign: the agents, up to 100. At most 100 items. | |
entry_ids | array of string | Required | Up to 200 entries. Ids not yours come back in skipped. 1–200 items. |
Responses
| Status | Description |
|---|---|
200 |
application/json — How many entries were acted on, and what changed |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
changed |
integer | Links added or removed, or entries deleted — what the action changed. |
entries |
integer | Entries the action was applied to. |
skipped |
array of string | Ids sent that are not this organisation’s, ignored. |
Make a knowledge entry from a web page
Section titled “Make a knowledge entry from a web page”POST /v1/knowledge/urls
Needs agent:write. The page is fetched and read into text, then indexed; it comes back importing. A PDF, Word or text file online works the same way. refresh reads it again daily or weekly and re-indexes it when it has changed. Only the public internet is reached.
curl -X POST "https://api.lumavox.ai/v1/knowledge/urls" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com/refunds"}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
agent_ids | array of string | At most 200 items. | |
category | string or null | At most 120 characters. | |
crawl | boolean | Also import the pages this one links to and the site’s sitemap lists, in the same part of the site — https://shop.test/help/ finds /help/… and nothing else — each as its own entry with the same agents, category and refresh. One level, robots.txt honoured, pages already imported skipped. The response is this page’s entry; the others appear as they are found. Default false. | |
max_pages | integer | With crawl: the most entries made, this page included. 2–100. Default 25. | |
refresh | "off", "daily", "weekly" or null | Read the page again on this schedule; when it has changed, the entry is re-indexed. off by default. | |
title | string or null | At most 255 characters. | |
url | string | Required | A public http(s) address: a web page, or a PDF, Word, CSV or text file online. 1–2048 characters. |
Responses
| Status | Description |
|---|---|
202 |
application/json — The entry, importing |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
409 |
The organisation’s entry limit. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
202 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Ask where to upload a file for knowledge
Section titled “Ask where to upload a file for knowledge”POST /v1/knowledge/upload-url
Needs agent:write. Returns a short-lived URL to PUT the file to, the Content-Type to send with it, and the upload_id to make the entry with afterwards (POST /v1/knowledge/files). The bytes never pass through this API. Up to 10 MB: PDF, Word, Excel, OpenDocument, CSV, HTML, Markdown or plain text. A CSV with question and answer columns becomes one question-and-answer pair per row.
curl -X POST "https://api.lumavox.ai/v1/knowledge/upload-url" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"filename":"LX-900 manual.pdf"}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
filename | string | Required | The file’s name; its extension says how it is read: .pdf, .docx, .xlsx, .xls, .ods, .odt, .csv, .html, .htm, .txt, .md. 1–255 characters. |
size_bytes | integer or null | The file’s size, when known. At most 10 MB. At least 0. | |
content_type | string or null | The Content-Type the PUT will carry, signed into the URL. Unset: the type of the file’s extension, which the ticket names. At most 255 characters. |
Responses
| Status | Description |
|---|---|
200 |
application/json — Where to PUT the file |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
413 |
Larger than 10 MB. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
expires_at |
string (date-time) | When url stops accepting the upload. |
upload_id |
string | |
url |
string | PUT the file’s bytes here, with content_type as the Content-Type and no authentication headers, before expires_at. |
content_type |
string | The Content-Type the PUT must carry: it is signed into the URL, and R2 refuses any other. |
Make a knowledge entry from an uploaded file
Section titled “Make a knowledge entry from an uploaded file”POST /v1/knowledge/files
Needs agent:write. After the PUT to the upload URL succeeded: the entry comes back importing while the file is read into text, then pending while it is indexed, then ready. The text is the entry’s body, editable like any other. Without a title the file’s own is used.
curl -X POST "https://api.lumavox.ai/v1/knowledge/files" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"filename":"...","upload_id":"..."}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
agent_ids | array of string | At most 200 items. | |
category | string or null | At most 120 characters. | |
filename | string | Required | 1–255 characters. |
title | string or null | At most 255 characters. | |
upload_id | string | Required |
Responses
| Status | Description |
|---|---|
202 |
application/json — The entry, importing |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Nothing was uploaded for that upload_id. |
409 |
The organisation’s entry limit. |
413 |
Larger than 10 MB. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
202 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Read a knowledge entry
Section titled “Read a knowledge entry”GET /v1/knowledge/{entry_id}
The entry, how many chunks it indexed to, and which agents have it. This is what to poll after a write until status is ready.
curl "https://api.lumavox.ai/v1/knowledge/$ENTRY_ID" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
entry_id | string | Required |
Responses
| Status | Description |
|---|---|
200 |
application/json — The entry |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown knowledge entry id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Edit a knowledge entry
Section titled “Edit a knowledge entry”PATCH /v1/knowledge/{entry_id}
Needs agent:write. Counts against your rate limit like a generation.
A partial update: fields you omit are left alone. An explicit null clears category; on summary it hands the line back to the indexer, which writes a new one. A summary you write is kept through every later edit; one the indexer wrote is rewritten with each new body.
Only a changed body costs anything. It re-indexes: status returns to pending, and the previous version stays searchable until the new one is ready, so no agent loses the entry meanwhile. Renaming or recategorising is immediate.
One edit reaches every agent the entry is assigned to.
curl -X PATCH "https://api.lumavox.ai/v1/knowledge/$ENTRY_ID" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{}'Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
entry_id | string | Required |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
agent_ids | array of string | The agents that have this entry, as a complete set — set in the same transaction as the rest of the edit, so a save cannot land half. Omitted, the assignments are left alone. At most 1000 items. | |
body | string | 1–1000000 characters. | |
category | string or null | At most 120 characters. | |
expected_version | integer | The version the editor started from. If the entry has been edited since, the edit is refused with 409 rather than writing over somebody else’s. | |
refresh | "off", "daily", "weekly" | For an entry made from a web page: how often it is read again. | |
summary | string or null | At most 200 characters. | |
title | string | 1–255 characters. |
Responses
| Status | Description |
|---|---|
200 |
application/json — The entry as it now stands |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown knowledge entry id |
409 |
expected_version is older than the entry: somebody changed it since. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Delete a knowledge entry
Section titled “Delete a knowledge entry”DELETE /v1/knowledge/{entry_id}
Needs agent:write.
Removes the entry, everything indexed from it, and every assignment to it. This affects every agent that had it — unassigning one agent is PUT /v1/agents/{agent_id}/knowledge and leaves the entry alone.
curl -X DELETE "https://api.lumavox.ai/v1/knowledge/$ENTRY_ID" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
entry_id | string | Required |
Responses
| Status | Description |
|---|---|
204 |
The entry is gone, for every agent that had it |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown knowledge entry id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
Index a knowledge entry again
Section titled “Index a knowledge entry again”POST /v1/knowledge/{entry_id}/reindex
Needs agent:write. Counts against your rate limit like a generation.
Queues the entry for indexing as it stands — for one that failed, say while a provider was down. status returns to pending; whatever version was searchable stays searchable until the new one is ready. Entries stuck pending are retried on their own after half an hour.
curl -X POST "https://api.lumavox.ai/v1/knowledge/$ENTRY_ID/reindex" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
entry_id | string | Required |
Responses
| Status | Description |
|---|---|
202 |
application/json — The entry, pending until indexed |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown knowledge entry id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
202 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Read a page-made entry again now
Section titled “Read a page-made entry again now”POST /v1/knowledge/{entry_id}/refresh
Needs agent:write. For an entry made from a web page: fetches it now rather than at its next scheduled refresh, and re-indexes it if it changed.
curl -X POST "https://api.lumavox.ai/v1/knowledge/$ENTRY_ID/refresh" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
entry_id | string | Required |
Responses
| Status | Description |
|---|---|
202 |
application/json — The entry |
401 |
Missing or invalid credentials |
402 |
Out of credits, or the organisation is suspended — nothing was charged |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown knowledge entry id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
202 response body
| Field | Type | Description |
|---|---|---|
agents |
array of AssignedAgent | Which agents have been given this entry. Empty means nobody has yet — the entry exists and no agent can look it up, which is a draft rather than a fault. |
agents[].id |
string | |
agents[].name |
string | |
body |
string | |
body_chars |
integer | How long body is in full, in characters. A listing on the console sends the first few hundred characters only; read the entry for all of it. |
category |
string or null | |
chunks |
integer | How many pieces the body was split into for searching. Most hand-written entries are one. |
created_at |
string (date-time) | |
error |
string or null | Why indexing failed, when it did. |
id |
string | |
refresh |
"off", "daily", "weekly" or null |
For an entry made from a web page: how often it is read again. Null for the rest. |
refreshed_at |
string (date-time) or null | When the page or file was last read. |
searchable |
boolean | Whether an agent given this entry can find it now. False only until its first indexing succeeds; an edit or a failed re-index leaves the previous version searchable. |
source_kind |
"text", "file", "url" |
Where the text came from: text (written or pasted), file or url. |
source_name |
string or null | The file’s name, or the page’s address. |
status |
"pending", "ready", "failed", "importing" |
How the latest body went: importing while a file or a page is read into text, pending while it is indexed, ready once it is done, failed with the reason in error. Indexing takes seconds to a few minutes: an entry is ready once the search index returns it. Whether an agent can find the entry is searchable: an edited entry stays searchable at its previous version while the new one indexes. |
summary |
string or null | One line saying what this entry answers, written by the model when the entry is saved and editable afterwards. This is what the agent sees on every turn — the body is fetched only when it chooses to look something up — so a vague line here makes the agent less likely to consult a perfectly good entry. |
summary_source |
"model", "customer" or null |
Who wrote summary: model (rewritten with every new body) or customer (kept through every edit). Null while there is none. |
title |
string | |
updated_at |
string (date-time) | |
version |
integer | Moves with every edit. Send it back as expected_version to refuse an edit made over somebody else’s. |
Set which agents have an entry
Section titled “Set which agents have an entry”PUT /v1/knowledge/{entry_id}/agents
Needs agent:write.
Replaces the entry’s assignments with exactly the agents named — the mirror of PUT /v1/agents/{agent_id}/knowledge, which edits the same links from the other end. Declarative: the list is the complete set, and an empty one unassigns it from everybody without deleting anything. Ids that are not yours come back in skipped.
curl -X PUT "https://api.lumavox.ai/v1/knowledge/$ENTRY_ID/agents" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"agent_ids":[]}'Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
entry_id | string | Required |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
agent_ids | array of string | Required | The complete set. An empty list clears every assignment, which is a real thing to want and not an error. At most 1000 items. |
Responses
| Status | Description |
|---|---|
200 |
application/json — How many links were added and removed |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown knowledge entry id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
added |
integer | |
removed |
integer | |
skipped |
array of string | Ids sent that are not this organisation’s, ignored. |