ListDocuments
Lists the documents in a knowledge base, one page at a time.
Documents are returned in a stable order that does not change between pages. Filters combine
with AND. Unlike v2, an unknown filter is refused with 400 UNKNOWN_FILTER instead of being
ignored, and the list can be sorted by name, updatedAt (when the document last changed) and
byteSize on the server; v2 sorts on the client only.
Not yet available. The SDKs have this operation, but no Caitlyn release serves it yet: a call gets 404. This page describes how it will work. GetAgentVersion is the one v3 operation served today.
| HTTP | GET /api/v3/knowledge-bases/{knowledgeBaseId}/documents |
| TypeScript | caitlyn.documents.list({ … }) |
| Python | caitlyn.documents.list(…) |
| Go | client.Documents.List(ctx, &caitlyn.ListDocumentsInput{…}) |
| Rust | client.documents().list(ListDocumentsInput { … }) |
| Stability | Generally available |
| Since | Caitlyn 5.115.0 |
| Retried by the SDKs | Yes, on 429, 503 and connection failures (Retries) |
Request
Section titled “Request”Member names are shown as they are in TypeScript and on the wire. Python and Rust use snake case (knowledge_base_id) and Go uses exported names (KnowledgeBaseID).
| Member | Type | Sent in | Description |
|---|---|---|---|
knowledgeBaseId (required) |
string | path | The knowledge base to list. 1 to 128 characters. |
limit |
integer | query limit |
The most documents to return, from 1 to 100. Defaults to 20. |
cursor |
string | query cursor |
The nextCursor from the previous page. Absent for the first page. 1 to 2048 characters. |
sort |
string | query sort |
The order: name, updatedAt or byteSize, comma-separated, with a leading - for descending (for example -updatedAt). Any other field is refused with 400 UNKNOWN_SORT. 1 to 256 characters. |
folder |
string | query filter[folder][eq] |
Only documents directly in this folder. 1 to 1024 characters. Pattern `^(/ |
status |
DocumentStatus | query filter[status][eq] |
Only documents with this status. |
namePrefix |
string | query filter[name][startsWith] |
Only documents whose name starts with this text. 1 to 1024 characters. |
Filters combine with AND. A filter or operator not listed here is refused with 400 UNKNOWN_FILTER.
Sortable by name, updatedAt, byteSize. Any other field is refused with 400 UNKNOWN_SORT.
The SDKs page through the list for you: iterate the result and each page is fetched as it is needed (Pagination).
Response
Section titled “Response”Status 200. One page of results; the SDKs return the items across every page.
| Member | Type | Sent in | Description |
|---|---|---|---|
requestId |
string | header Caitlyn-Request-Id |
The request's identifier. The same value is in every error body; quote it to support. 1 to 128 characters. |
apiVersion |
string | header Caitlyn-Api-Version |
The server's model version, for example 5.120.0. The SDKs keep the latest value they have seen and refuse to call operations or send members newer than it. 5 to 32 characters. Pattern ^[0-9]+\.[0-9]+\.[0-9]+$. |
items (required) |
list of Document | body | This page of documents. |
nextCursor |
string | body | The cursor for the next page. Absent on the last page. 1 to 2048 characters. |
Every result also keeps the JSON it was read from, unknown fields included, as raw, and the response metadata (HTTP status, request ID, ETag).
Permissions
Section titled “Permissions”- Permission:
files.list. - Checked on: the knowledge bas named by
knowledgeBaseId, which needs thereadgrant. - Listed: only what the caller's knowledge bas grants allow.
- Who can call it: people (signed in, or a personal access token), API clients (machine sign-in).
The checks run in this order: who is calling, the workspace and the permission (each refused with 403), then the record (404, which is also what a record the caller may not see returns). Errors explains why.
Errors
Section titled “Errors”The SDKs raise the error class for the response's HTTP status, whether or not the operation lists it (Errors). The statuses this operation returns:
| Status | Error | Codes | When |
|---|---|---|---|
| 400 | ValidationError |
VALIDATION_FAILED, UNKNOWN_FIELD, UNKNOWN_FILTER, UNKNOWN_SORT, INVALID_CURSOR |
The request is malformed: a member is missing, the wrong type or out of range; it has a member, filter or sort field the operation does not define; or its cursor is not valid for this list. |
| 401 | UnauthenticatedError |
UNAUTHENTICATED |
No credential was sent, or it is expired or not valid. |
| 403 | PermissionDeniedError |
PERMISSION_DENIED, NOT_AVAILABLE_TO_CREDENTIALS, PRIMARY_WORKSPACE_REQUIRED |
The caller may not call this operation: it lacks the permission, its credential type may not call it, or the operation is served only on the organisation's primary workspace. Decided before any record is read. A record the caller may not see is 404 instead. |
| 404 | NotFoundError |
NOT_FOUND |
The record does not exist, or the caller may not see it. The two are deliberately the same, so identifiers cannot be probed. |
| 429 | TooManyRequestsError |
RATE_LIMITED |
Too many requests; wait for Retry-After and try again. Retried by the SDKs. |
| 500 | InternalError |
INTERNAL |
An unexpected server failure. Not retried by the SDKs. |
| 503 | ServiceUnavailableError |
SERVICE_UNAVAILABLE |
A dependency is briefly unavailable, or the request's audit event could not be recorded; wait for Retry-After and try again. Retried by the SDKs. |
Examples
Section titled “Examples”List the HR folder's documents that finished indexing
Section titled “List the HR folder's documents that finished indexing”import { Caitlyn } from '@caitlyn-ai/sdk';
const caitlyn = new Caitlyn({ workspace: 'your-company.getcaitlyn.ai', auth: { mode: 'token', token: process.env.CAITLYN_TOKEN! } });
for await (const item of caitlyn.documents.list({ knowledgeBaseId: 'KB4T7YQ2WL', limit: 2, folder: 'hr/', status: 'indexed',})) { console.log(item);}import os
from caitlyn import Caitlynfrom caitlyn.auth import Token
caitlyn = Caitlyn(workspace="your-company.getcaitlyn.ai", auth=Token(os.environ["CAITLYN_TOKEN"]))
for item in caitlyn.documents.list( knowledge_base_id="KB4T7YQ2WL", limit=2, folder="hr/", status="indexed",): print(item)package main
import ( "context" "encoding/json" "fmt" "log" "os"
caitlyn "github.com/caitlyn-ai/caitlyn-go")
func main() { ctx := context.Background() client, err := caitlyn.NewClient("your-company.getcaitlyn.ai", caitlyn.Token(os.Getenv("CAITLYN_TOKEN"))) if err != nil { log.Fatal(err) } for item, err := range client.Documents.List(ctx, &caitlyn.ListDocumentsInput{ KnowledgeBaseID: "KB4T7YQ2WL", Limit: caitlyn.Ptr[int32](2), Folder: caitlyn.Ptr("hr/"), Status: caitlyn.Ptr(caitlyn.DocumentStatusIndexed), }) { if err != nil { log.Fatal(err) } out, _ := json.Marshal(item) fmt.Println(string(out)) }}use caitlyn_sdk::{Auth, Client, DocumentStatus, ListDocumentsInput};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::new("your-company.getcaitlyn.ai", Auth::token(std::env::var("CAITLYN_TOKEN")?))?; let mut items = client .documents() .list(ListDocumentsInput { knowledge_base_id: "KB4T7YQ2WL".into(), limit: Some(2), folder: Some("hr/".into()), status: Some(DocumentStatus::Indexed), ..Default::default() }); while let Some(item) = items.next().await { println!("{:#?}", item?); } Ok(())}The HTTP exchange:
GET /api/v3/knowledge-bases/KB4T7YQ2WL/documents?limit=2&filter[folder][eq]=hr%2F&filter[status][eq]=indexed HTTP/1.1Host: your-company.getcaitlyn.aiAuthorization: Bearer <token>HTTP/1.1 200Content-Type: application/json
{ "items": [ { "id": "doc-3b7f9e21", "knowledgeBaseId": "KB4T7YQ2WL", "name": "Leave policy", "folder": "hr/", "status": "indexed", "contentType": "application/pdf", "byteSize": 482113, "createdAt": "2026-10-01T22:10:00.000Z", "updatedAt": "2026-10-01T22:14:31.000Z" } ], "nextCursor": "c1.MEUCIQDl8z"}A knowledge base that does not exist, or that the caller may not see
Section titled “A knowledge base that does not exist, or that the caller may not see”import { Caitlyn, NotFoundError } from '@caitlyn-ai/sdk';
const caitlyn = new Caitlyn({ workspace: 'your-company.getcaitlyn.ai', auth: { mode: 'token', token: process.env.CAITLYN_TOKEN! } });
try { for await (const item of caitlyn.documents.list({ knowledgeBaseId: 'KBNOPE0000' })) { console.log(item); }} catch (err) { if (!(err instanceof NotFoundError)) throw err; console.error(err.status, err.code, err.detail, err.requestId);}import os
from caitlyn import Caitlyn, NotFoundErrorfrom caitlyn.auth import Token
caitlyn = Caitlyn(workspace="your-company.getcaitlyn.ai", auth=Token(os.environ["CAITLYN_TOKEN"]))
try: for item in caitlyn.documents.list(knowledge_base_id="KBNOPE0000"): print(item)except NotFoundError as err: print(err.status, err.code, err.detail, err.request_id)package main
import ( "context" "encoding/json" "errors" "fmt" "log" "os"
caitlyn "github.com/caitlyn-ai/caitlyn-go")
func main() { ctx := context.Background() client, err := caitlyn.NewClient("your-company.getcaitlyn.ai", caitlyn.Token(os.Getenv("CAITLYN_TOKEN"))) if err != nil { log.Fatal(err) } for item, err := range client.Documents.List(ctx, &caitlyn.ListDocumentsInput{KnowledgeBaseID: "KBNOPE0000"}) { if err != nil { var apiErr *caitlyn.NotFoundError if errors.As(err, &apiErr) { fmt.Println(apiErr.Status, apiErr.Code, apiErr.Detail, apiErr.RequestID) return } log.Fatal(err) } out, _ := json.Marshal(item) fmt.Println(string(out)) }}use caitlyn_sdk::{Auth, Client, ListDocumentsInput};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::new("your-company.getcaitlyn.ai", Auth::token(std::env::var("CAITLYN_TOKEN")?))?; let outcome: Result<(), caitlyn_sdk::Error> = async { let mut items = client .documents() .list(ListDocumentsInput { knowledge_base_id: "KBNOPE0000".into(), ..Default::default() }); while let Some(item) = items.next().await { println!("{:#?}", item?); } Ok(()) } .await; match outcome { Err(caitlyn_sdk::Error::NotFound(err)) => eprintln!("{} {} {:?} {:?}", err.status, err.code, err.detail, err.request_id), other => other?, } Ok(())}The HTTP exchange:
GET /api/v3/knowledge-bases/KBNOPE0000/documents HTTP/1.1Host: your-company.getcaitlyn.aiAuthorization: Bearer <token>HTTP/1.1 404Content-Type: application/problem+json
{ "type": "https://build.caitlyn.ai/errors/NOT_FOUND", "title": "Not found", "status": 404, "code": "NOT_FOUND", "requestId": "01JC9Q2W7R4M0H2T6N1S8V3B63"}
