Knowledge Bases

A knowledge base is a set of documents an AI agent searches while it talks: hours, prices, service areas, policies. Agents answer from it instead of guessing.

Knowledge bases use the agent scopes.

Route Scope
GET /document/public/knowledge-bases agents:read
POST /document/public/knowledge-bases agents:write
GET /document/public/knowledge-bases/{knowledge_base_id} agents:read
POST /document/public/knowledge-bases/{knowledge_base_id} agents:write
DELETE /document/public/knowledge-bases/{knowledge_base_id} agents:write
GET /document/public/knowledge-bases/{knowledge_base_id}/documents agents:read
POST /document/public/knowledge-bases/{knowledge_base_id}/documents agents:write
POST /document/public/knowledge-bases/{knowledge_base_id}/documents/{knowledge_document_id}/uploaded agents:write
POST /document/public/knowledge-bases/{knowledge_base_id}/documents/{knowledge_document_id}/upload-url agents:write
DELETE /document/public/knowledge-bases/{knowledge_base_id}/documents/{knowledge_document_id} agents:write
POST /document/public/knowledge-bases/{knowledge_base_id}/query agents:read

An id that belongs to another account answers 404, the same as one that does not exist.

Create a knowledge base

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"
  }'
{
  "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" }
}
Field Notes
name Required, up to 200 characters.
description Up to 2000 characters.
ai_audience public for content that may be read to customers. internal for agents that help your staff.

Update with POST .../{knowledge_base_id}, sending only the fields to change. You can also set status to archived or back to active. Timestamps are epoch milliseconds.

Add documents

One route, three ways. The fields you send pick the method.

Inline markdown

Best for FAQs you already have as text. Up to 1 MB.

{
  "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

{
  "source_url": "https://www.example.com/files/price-list.pdf",
  "title": "Price list"
}

DropCowboy downloads the file. It must be on a public http(s) address and no larger than 25 MB. If the server labels the file with a supported type, that type wins; otherwise send mime_type. URL problems are listed under Errors.

Upload a file

Three steps, for files up to 25 MB.

1. Ask for an upload ticket:

{
  "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
    }
  }
}

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, get a new one (see Get a new upload URL).

curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @price-list.pdf

3. Confirm:

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"

A stored file over 25 MB answers 413, and a file identical to one already in the knowledge base answers 409 (duplicate-knowledge-document). Either one marks the document failed, with ingest_error set to Uploaded file exceeds the 26214400 byte limit or This document is already in this knowledge base. To recover, PUT different bytes to upload.url (or a new upload URL) and confirm again; a successful confirm clears ingest_error. Otherwise delete the document with the document DELETE route and start over.

Confirming twice answers 409 (already-confirmed), and confirming before the file was uploaded answers 400. Neither changes the document, which stays pending until a confirm succeeds.

Get a new upload URL

If the upload URL expired before you used it, or a confirm was refused and you want to send different bytes, ask for a new one:

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 response is 200 with the same shape as the upload ticket in step 1. The new URL writes the same file with the same Content-Type, so PUT and confirm exactly as before. A document that was marked failed by a refused confirm goes back to pending, with ingest_error cleared.

Each call signs another URL and changes nothing else, so it is safe to retry. Earlier URLs keep working until they expire. It only works before a confirm succeeds: once the upload is confirmed, the route answers 409 (already-confirmed), whether the document is still processing, ready or failed during processing. To replace a confirmed document, delete it and add it again. Markdown and URL documents are confirmed when they are created, so they answer 409 too.

Supported types

The file extension must match mime_type.

Type Extensions
PDF .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

Anything else answers 415.

Adding a file identical to one already in the knowledge base answers 409, with the existing document's id in details.existing_knowledge_document_id.

Wait for processing

Every document is processed in the background into searchable passages. Poll the document list and check ingest_status:

ingest_status Meaning
pending Queued, or waiting for the upload to be confirmed.
processing Being converted.
ready Searchable. chunk_count shows how many passages it produced.
failed Could not be processed. ingest_error says why.

The knowledge base's stats roll up the same counts. Only ready documents are searched.

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"

Lists page with limit (default 25, maximum 100) and offset. The totals are in meta.total.

Test it

Before attaching a knowledge base, ask it the questions callers will ask:

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
      }
    ]
  }
}

These are the passages an agent would retrieve, best first. query is limited to 1000 characters. If the right passage is missing, improve the document before attaching it.

Attach to an agent

Set think.knowledge on the agent, then publish:

{
  "think": {
    "prompt_spec": { "role": "Receptionist for Example Dental" },
    "knowledge": {
      "mode": "selected",
      "knowledge_base_ids": ["9d3f6a2c-4e8b-4b1d-a7c5-2f6e9b3d8a14"],
      "audience": "public"
    }
  }
}
Key Notes
mode selected searches only knowledge_base_ids (the default when ids are given). all searches every knowledge base on the account with the matching audience.
knowledge_base_ids The knowledge bases to search.
audience public (default) or internal. 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. AI agents has a complete example.

Publishing checks selected knowledge bases. It fails with 400 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.

Delete

  • DELETE .../documents/{knowledge_document_id} removes one document.
  • DELETE /document/public/knowledge-bases/{knowledge_base_id} removes the knowledge base. Take its id out of any agent's think.knowledge too.

Errors

Knowledge routes use the standard error body (see errors and limits):

Status type ends with Cause
400 missing-parameters, invalid-parameters, invalid-filename, invalid-request A field is missing or out of range.
400 url-required, url-invalid, url-protocol-blocked, url-credentials source_url is empty, malformed, not http(s), or has a user name or password in it.
400 url-private-host, url-unresolvable The host is a private address, or does not resolve.
400 empty-file The file or download was empty.
403 insufficient-scope The credentials lack the scope in the table above.
403 forbidden A signed-in user's role does not allow it. See below.
404 not-found Unknown knowledge base or document.
409 duplicate-knowledge-document, already-confirmed See above.
413 payload-too-large, url-too-large Over 1 MB of markdown or 25 MB of file.
415 unsupported-media-type Type not supported, or extension mismatch.
502 url-fetch-failed, url-too-many-redirects The download failed.
504 url-timeout The download took too long.

A token issued for a signed-in user whose role cannot use or manage AI agents gets a 403 with type ending forbidden. detail is 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 cannot be looked up, the answer is 500 (server-error). API keys are never limited by role.

Code samples

The requests from this page, ready to copy. Set DC_KEY and DC_SECRET to your API key pair first.

Create a knowledge base
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"
  }'
Upload a file
curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @price-list.pdf
Upload a file (2)
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"
Get a new upload URL
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"
Wait for processing
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"
Test it
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?" }'