API reference / Voice and AI
Knowledge bases
A knowledge base is a set of documents an AI agent searches while it talks: hours, prices, service areas, policies. The agent answers from it instead of guessing.
An id that belongs to another account answers 404, the same as one that
doesn't exist.
Routes
| Method | Route | Scope | What it does |
|---|---|---|---|
GET |
/document/public/knowledge-bases |
agents:read |
List knowledge bases |
POST |
/document/public/knowledge-bases |
agents:write |
Create a knowledge base |
GET |
/document/public/knowledge-bases/{knowledge_base_id} |
agents:read |
Get a knowledge base |
POST |
/document/public/knowledge-bases/{knowledge_base_id} |
agents:write |
Update a knowledge base |
POST |
/document/public/knowledge-bases/{knowledge_base_id}/documents |
agents:write |
Add a document |
POST |
/document/public/knowledge-bases/{knowledge_base_id}/documents/{knowledge_document_id}/uploaded |
agents:write |
Confirm an upload |
POST |
/document/public/knowledge-bases/{knowledge_base_id}/documents/{knowledge_document_id}/upload-url |
agents:write |
Get a new upload URL |
GET |
/document/public/knowledge-bases/{knowledge_base_id}/documents |
agents:read |
List documents |
POST |
/document/public/knowledge-bases/{knowledge_base_id}/query |
agents:read |
Search a knowledge base |
DELETE |
/document/public/knowledge-bases/{knowledge_base_id}/documents/{knowledge_document_id} |
agents:write |
Delete a document |
DELETE |
/document/public/knowledge-bases/{knowledge_base_id} |
agents:write |
Delete a knowledge base |
List knowledge bases
| Field | Type | Required | Description |
|---|---|---|---|
search |
string | No | Matches the name. |
ai_audience |
string | No | public or internal. |
status |
string | No | active (the default) or archived. |
sort_by |
string | No | name, created_at or updated_at. Default updated_at. |
sort_order |
string | No | asc or desc. Default desc. |
limit |
integer | No | Page size, 1 to 100. Default 25. |
offset |
integer | No | Items to skip. Default 0. |
curl "https://api-v2.dropcowboy.com/document/public/knowledge-bases?search=faq" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
data is an array of knowledge bases, shaped as below. meta.total,
meta.limit and meta.offset describe the page (see
Pagination).
Create a knowledge base
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Up to 200 characters. |
description |
string | No | Up to 2000 characters. |
ai_audience |
string | No | public for content that may be read to callers. internal for agents that help your staff. Default internal. |
Set ai_audience to public for a knowledge base a receptionist or an AI call
answers from. Agents search public knowledge bases unless you tell them
otherwise (see Attach to an agent).
curl -X POST https://api-v2.dropcowboy.com/document/public/knowledge-bases \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
-H "content-type: application/json" \
-d '{
"name": "Customer FAQ",
"description": "Hours, pricing and service area for callers.",
"ai_audience": "public"
}'
The route answers 201:
{
"data": {
"knowledge_base_id": "9d3f6a2c-4e8b-4b1d-a7c5-2f6e9b3d8a14",
"name": "Customer FAQ",
"description": "Hours, pricing and service area for callers.",
"ai_audience": "public",
"status": "active",
"stats": { "document_count": 0, "ready_count": 0, "failed_count": 0, "chunk_count": 0 },
"created_at": 1774041600000,
"updated_at": 1774041600000
},
"meta": { "request_id": "8a2f6c1e-4d9b-4e7a-b3c5-6f1d8e2a9c47" }
}
Timestamps are epoch milliseconds. stats counts the documents and how many
are ready or failed.
Get a knowledge base
GET /document/public/knowledge-bases/{knowledge_base_id} returns the knowledge base, shaped as above.
Update a knowledge base
Send only the fields to change: name, description, ai_audience, or
status (active or archived). An archived knowledge base drops out of the
default list.
curl -X POST https://api-v2.dropcowboy.com/document/public/knowledge-bases/9d3f6a2c-4e8b-4b1d-a7c5-2f6e9b3d8a14 \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
-H "content-type: application/json" \
-d '{ "ai_audience": "public" }'
Add a document
One route, three ways. The fields you send pick the way. Each answers 201.
Inline markdown
Best for FAQs you already have as text. Send title and markdown, up to
1 MB.
curl -X POST "https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID/documents" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
-H "content-type: application/json" \
-d '{
"title": "Hours and location",
"markdown": "# Hours\n\nMonday to Friday, 8am to 6pm. Saturday, 9am to 1pm. Closed Sunday.\n\n# Location\n\n100 Example Street, Springfield."
}'
From a URL
Send source_url, a public http or https address up to 2048 characters, with
an optional title. Drop Cowboy® downloads the file, up to 25 MB.
{
"source_url": "https://www.example.com/files/price-list.pdf",
"title": "Price list"
}
If the server labels the file with a supported type, that
type wins. Otherwise send mime_type too.
Upload a file
Three steps, for files up to 25 MB.
1. Ask for an upload ticket:
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | A plain file name, up to 255 characters. Its extension must match mime_type. |
mime_type |
string | Yes | A supported type. |
byte_size |
integer | Yes | The file's size, up to 26214400 (25 MB). |
title |
string | No | Defaults from the file name. |
{
"title": "Price list",
"filename": "price-list.pdf",
"mime_type": "application/pdf",
"byte_size": 184320
}
{
"data": {
"knowledge_document_id": "5b8e1c4f-7a2d-4f9e-b3c6-8d1a5e2f7b40",
"knowledge_document": { "ingest_status": "pending", "upload_confirmed": false },
"upload": {
"method": "PUT",
"url": "https://uploads.example.com/knowledge/price-list.pdf?signature=example-signature",
"headers": { "Content-Type": "application/pdf" },
"max_bytes": 26214400
}
},
"meta": { "request_id": "4d6f8a1c-3e5b-4c7d-9f2a-6b8d1e3f5a70" }
}
2. PUT the file to upload.url with exactly the headers in
upload.headers. The URL is signed for that Content-Type and rejects any
other. It expires after 48 hours. If it has expired,
get a new upload URL.
curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @price-list.pdf
Supported types
The file extension must match mime_type. Anything else answers 415.
| Type | Extensions |
|---|---|
.pdf |
|
| Word | .doc, .docx |
| Excel | .xls, .xlsx |
| PowerPoint | .ppt, .pptx |
| Rich text, OpenDocument | .rtf, .odt |
| Web page | .html |
| Text, markdown, CSV, JSON | .txt, .log, .md, .csv, .json |
Confirm an upload
Confirm after the PUT succeeds. The route answers 200 and processing
starts.
curl -X POST \
"https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID/documents/$DOC_ID/uploaded" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
| Status | Cause | What to do |
|---|---|---|
400 |
The file hasn't been uploaded. | PUT the file, then confirm again. |
409 already-confirmed |
The upload is already confirmed. | Nothing. Watch ingest_status. |
409 duplicate-knowledge-document |
The same file is already in this knowledge base. details.existing_knowledge_document_id names it. |
Use the existing document, or upload different bytes and confirm again. |
413 |
The stored file is over 25 MB. | Upload a smaller file and confirm again. |
A refused duplicate or oversized file marks the document failed, with the
reason in ingest_error. A later successful confirm clears it. To give up
instead, delete the document.
Get a new upload URL
Use this when the upload URL expired before you used it, or when a confirm was refused and you want to send different bytes.
curl -X POST \
"https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID/documents/$DOC_ID/upload-url" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
The route answers 200 with the same shape as the upload ticket. PUT and
confirm exactly as before. A document marked failed by a refused confirm
goes back to pending.
The route is safe to retry, and earlier URLs keep working until they expire.
It works only until a confirm succeeds. After that, and for markdown and URL
documents, it answers 409 (already-confirmed). To replace a confirmed
document, delete it and add it again.
List documents
Every document is processed into searchable passages after it's added. Poll
this list and check ingest_status. Only ready documents are searched.
| Field | Type | Required | Description |
|---|---|---|---|
search |
string | No | Matches the title. |
limit |
integer | No | Page size, 1 to 100. Default 25. |
offset |
integer | No | Items to skip. Default 0. |
curl "https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID/documents?limit=100" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
Each document carries knowledge_document_id, knowledge_base_id, title,
filename, mime_type, byte_size, source, ai_audience,
upload_confirmed, ingest_status, ingest_error, chunk_count,
created_at and updated_at.
ingest_status |
Meaning | What to do |
|---|---|---|
pending |
Waiting to be processed, or for the upload to be confirmed. | Confirm the upload if you haven't. |
processing |
Being converted. | Poll again in a few seconds. |
ready |
Searchable. chunk_count shows how many passages it produced. |
Nothing. |
failed |
Couldn't be processed. ingest_error says why. |
Fix the file and add it again. |
Search a knowledge base
Before you attach a knowledge base, ask it the questions callers will ask.
query is up to 1000 characters.
curl -X POST "https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID/query" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
-H "content-type: application/json" \
-d '{ "query": "What are your hours on Saturday?" }'
{
"data": {
"query": "What are your hours on Saturday?",
"results": [
{
"chunk_id": "7f4c2a9e-3b1d-4e6a-8c5f-9d2b7e1a4c36",
"knowledge_base_id": "9d3f6a2c-4e8b-4b1d-a7c5-2f6e9b3d8a14",
"knowledge_document_id": "2c7a9e4b-1d6f-4a3e-8b5c-7f2d9a6e1c83",
"chunk_index": 0,
"text": "Monday to Friday, 8am to 6pm. Saturday, 9am to 1pm. Closed Sunday.",
"score": 0.87,
"heading": "Hours",
"section_path": "Hours",
"page": null
}
]
},
"meta": { "request_id": "6e8a1c3f-5b7d-4e9a-8c2f-1d3b5e7a9c42" }
}
These are the passages an agent would retrieve, best first. If the right passage is missing, improve the document before you attach it.
Delete a document
curl -X DELETE "https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID/documents/$DOC_ID" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
The route returns {"knowledge_document_id": "...", "deleted": true}.
Delete a knowledge base
curl -X DELETE "https://api-v2.dropcowboy.com/document/public/knowledge-bases/$KB_ID" \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
The route returns {"knowledge_base_id": "...", "deleted": true}. Take the id
out of any agent's think.knowledge and publish the agent again.
Attach to an agent
Set think.knowledge on the agent, then publish it:
{
"think": {
"prompt_spec": { "role": "Receptionist for Example Dental" },
"knowledge": {
"mode": "selected",
"knowledge_base_ids": ["9d3f6a2c-4e8b-4b1d-a7c5-2f6e9b3d8a14"],
"audience": "public"
}
}
}
| Key | Description |
|---|---|
mode |
selected searches only knowledge_base_ids, and is the default when you give ids. all searches every knowledge base on the account with the matching audience. |
knowledge_base_ids |
The knowledge bases to search. |
audience |
public (the default) or internal. It must match the knowledge bases' ai_audience, or nothing is found. |
Updating think replaces the whole object, so send the agent's existing
think with knowledge added. Update an agent
has a complete example.
Publishing checks the selected knowledge bases. It fails with 400
(unusable_knowledge_base) if their content reads like instructions for your
staff ("click Settings, then...") rather than answers for callers, or tells
callers to check an app for your hours without stating them. Write documents
the way you want the agent to speak.
Errors
The errors specific to knowledge bases are below. For the error format and everything else, see Responses, errors and limits.
| Status | type ends with |
Cause | What to do |
|---|---|---|---|
400 |
missing-parameters, invalid-parameters, invalid-filename, invalid-request |
A field is missing or out of range. | Fix the field named in detail. |
400 |
url-required, url-invalid, url-protocol-blocked, url-credentials |
source_url is empty, malformed, not http or https, or has a user name or password in it. |
Send a plain public URL. |
400 |
url-private-host, url-unresolvable |
The host is a private address, or doesn't resolve. | Host the file publicly, or upload it instead. |
400 |
empty-file |
The file or download was empty. | Check the file. |
403 |
forbidden |
A signed-in user's role doesn't allow it. See below. | Ask an account admin to change the role, or use an API key. |
404 |
not-found |
Unknown knowledge base or document. | Check the id. |
409 |
duplicate-knowledge-document, already-confirmed |
See Confirm an upload. | See there. |
413 |
payload-too-large, url-too-large |
Over 1 MB of markdown or 25 MB of file. | Split the document. |
415 |
unsupported-media-type |
Type not supported, or the extension doesn't match. | Convert to a supported type. |
502 |
url-fetch-failed, url-too-many-redirects |
The download failed. | Check the URL works without a login, or upload the file. |
504 |
url-timeout |
The download took too long. | Upload the file instead. |
A token issued for a signed-in user is checked against that user's role. API
keys never are. A denial has detail
You do not have permission to use Knowledge Bases. on reads and
You do not have permission to manage Knowledge Bases. on changes:
{
"type": "https://api-v2.dropcowboy.com/errors/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "You do not have permission to manage Knowledge Bases.",
"instance": "/document/public/knowledge-bases",
"request_id": "3c5e7a9b-1d2f-4a6c-8e0b-5f7d9a1c3e62"
}
If the role can't be looked up, the answer is 500. Retry.