{"slug":"files","title":"Files","path":"files.md","markdown":"# Files\n\nEvery knowledgebase has its own file tree. Paths are slash-separated and **relative to that knowledgebase’s root** (for example `docs/guide.md`). A leading `/` is stripped (so `/docs/a` becomes `docs/a`). Segments `.` and `..` are rejected.\n\nAll routes live under `/v1/knowledgebases/{kbId}/files…`. In the CLI, pass `-kb` with the knowledgebase id.\n\n## Tree\n\nRecursive snapshot of the tree.\n\n```bash\nkobold-cli files tree -kb kb_qc0rp0gz\n```\n\n```http\nGET /v1/knowledgebases/kb_qc0rp0gz/files/tree\nAuthorization: Bearer \u003capi_key\u003e\n```\n\nRoot node: `name` is `home`, `path` is `\"\"`. Children are directories (`type: \"dir\"`) or files (`type: \"file\"`). Files include `size`, `mime`, and `modified`.\n\n## Usage\n\nAggregate counts for one knowledgebase.\n\n```bash\nkobold-cli files usage -kb kb_qc0rp0gz\n```\n\n```http\nGET /v1/knowledgebases/kb_qc0rp0gz/files/usage\nAuthorization: Bearer \u003capi_key\u003e\n```\n\n```json\n{\n  \"totalFiles\": 3,\n  \"totalBytes\": 12000,\n  \"byType\": [\n    { \"mime\": \"text/markdown\", \"count\": 2, \"bytes\": 8000 },\n    { \"mime\": \"text/plain\", \"count\": 1, \"bytes\": 4000 }\n  ]\n}\n```\n\n## Mkdir\n\nCreate a directory (and any missing parents).\n\n```bash\nkobold-cli files mkdir -kb kb_qc0rp0gz -path docs/notes\n```\n\n```http\nPOST /v1/knowledgebases/kb_qc0rp0gz/files/mkdir\nAuthorization: Bearer \u003capi_key\u003e\nContent-Type: application/json\n\n{\n  \"path\": \"docs/notes\"\n}\n```\n\nSuccess: `{}` / CLI `{\"ok\": true}`. Bad paths → `400`.\n\n## Ingest\n\nUpload raw file bytes into the knowledgebase.\n\n```bash\nkobold-cli files ingest -kb kb_qc0rp0gz -file ./guide.md\nkobold-cli files ingest -kb kb_qc0rp0gz -filename guide.md -dest-dir docs -file ./guide.md\ncat guide.md | kobold-cli files ingest -kb kb_qc0rp0gz -filename guide.md -dest-dir docs -file -\n```\n\n```http\nPOST /v1/knowledgebases/kb_qc0rp0gz/files/ingest?filename=guide.md\u0026destDir=docs\nAuthorization: Bearer \u003capi_key\u003e\nContent-Type: application/octet-stream\n\n\u003craw bytes\u003e\n```\n\n| Param / flag | Required | Notes |\n| ------------ | -------- | ----- |\n| `filename` / `-filename` | yes (HTTP); CLI infers from `-file` path | Basename only |\n| `destDir` / `-dest-dir` | no | Relative directory; empty = knowledgebase root. Must already exist — create it with mkdir first. Missing `destDir` → `404`. |\n| body / `-file` | yes | File path, or `-` for stdin |\n\nOn name collision in the destination directory, a numeric suffix is added (`guide-1.md`, `guide-2.md`, …).\n\nSuccess `201`:\n\n```json\n{\n  \"homePath\": \"docs/guide.md\"\n}\n```\n\nBehavioral notes:\n\n- Request body size must stay within your plan’s per-file upload size (`maxApiUploadBytesPerFile` from `stats get`). Oversized bodies → `413`.\n- Successful uploads count toward your plan’s daily upload allowance (`maxApiUploadsPerDay`). Exhausted → `429`.\n- Exceeding storage for the account → `403`.\n- Ingest does not wait for indexing. Call [sync](index-and-search.md) when you want the index to catch up promptly.\n\n## Rename / move\n\n```bash\nkobold-cli files rename -kb kb_qc0rp0gz -from docs/old.md -to docs/new.md\nkobold-cli files rename -kb kb_qc0rp0gz -from docs/old.md -to archive/old.md\n```\n\n```http\nPATCH /v1/knowledgebases/kb_qc0rp0gz/files\nAuthorization: Bearer \u003capi_key\u003e\nContent-Type: application/json\n\n{\n  \"from\": \"docs/old.md\",\n  \"to\": \"docs/new.md\"\n}\n```\n\nBoth paths are relative to the knowledgebase root. Missing source → `404`.\n\n- Same directory: `to` is treated as a new basename in that directory (`docs/old.md` → `docs/new.md`).\n- Cross directory: `to` is the full destination path; parent directories are created as needed (`docs/old.md` → `archive/old.md`).\n\n## Delete\n\n```bash\nkobold-cli files delete -kb kb_qc0rp0gz -path docs/new.md\nkobold-cli files delete -kb kb_qc0rp0gz -path docs -recursive\n```\n\n```http\nDELETE /v1/knowledgebases/kb_qc0rp0gz/files?path=docs\u0026recursive=true\nAuthorization: Bearer \u003capi_key\u003e\n```\n\n| Query / flag | Required | Notes |\n| ------------ | -------- | ----- |\n| `path` / `-path` | yes | Relative path |\n| `recursive` / `-recursive` | no | Required to delete a non-empty directory |\n\nNon-empty directory without recursive → `400`.\n\n## Content (read bytes)\n\nThis is the endpoint for reading file payloads. Search never returns a full file.\n\n```bash\nkobold-cli files content -kb kb_qc0rp0gz -path docs/guide.md\nkobold-cli files content -kb kb_qc0rp0gz -path docs/guide.md -offset 0 -length 65536 -out ./part.bin\n```\n\n```http\nGET /v1/knowledgebases/kb_qc0rp0gz/files/content?path=docs/guide.md\u0026offset=0\u0026length=65536\nAuthorization: Bearer \u003capi_key\u003e\n```\n\n| Query / flag | Default | Notes |\n| ------------ | ------- | ----- |\n| `path` / `-path` | — | Required |\n| `offset` / `-offset` | `0` | Start byte |\n| `length` / `-length` | `65536` (64 KiB) | Max `1048576` (1 MiB) per request |\n| `-out` | (stdout) | CLI only: write bytes to a file |\n\nResponse body is raw file bytes. Useful response headers:\n\n| Header | Meaning |\n| ------ | ------- |\n| `Content-Type` | MIME type |\n| `X-Content-Offset` | Start offset of this slice |\n| `X-Content-Length` | Bytes in this response |\n| `X-Content-Total` | Full file size when known |\n\nTo read a large file, issue multiple ranged requests by advancing `offset`. Each successful content request counts as one download toward your plan’s daily download allowance (`maxApiDownloadsPerDay` from `stats get`).\n\nDirectories and bad ranges → `400`. Missing file → `404`.\n\n## Related\n\n- [Index and search](index-and-search.md) — index after ingest\n- [Errors](errors.md) — status codes\n"}
