Manage

Buckets

Create, list, and provision the named collections your agents search — including create-on-first-use and membership management.

What buckets are

A bucket is a named, durable collection of knowledge. Agents search buckets, ingestion writes into buckets, and the bucket slug is the human-readable handle (support-handbook, pricing, legal). Bucket ids are stable machine handles; slugs are unique per team.

List buckets

GET /v1/buckets
Authorization: Bearer sk-...

Optional: ?include_archived=true. The response is scoped to your key's team and returns, per bucket: id, slug, name, status, member counts, and store readiness. Use returned ids or slugs anywhere a {bucket} path segment is accepted.

Create a bucket

POST /v1/buckets
Authorization: Bearer sk-...
Content-Type: application/json
{
  "name": "Support Handbook",
  "slug": "support-handbook",
  "description": "Canonical support articles"
}

Requires the knowledge:bucket:create capability. The slug is the idempotency identity: a collision with an Idempotency-Key replays the existing bucket (200 + Idempotent-Replayed: true); without one it is 409 bucket_slug_exists.

Create-on-first-use

PUT /v1/buckets/{slug}
Authorization: Bearer sk-...

Idempotent ensure-by-slug: 201 when created, 200 ("created": false) when it already exists. This is the recommended way for scripts to provision destinations before uploading — explicit, idempotent, and separate from the upload call.

{ "id": "bucket_abc", "slug": "support-handbook", "status": "active", "created": true }

Concurrency is a success case, not an error: when two callers ensure the same slug at the same moment, the one that loses the race re-reads and returns the winner's bucket with "created": false. Parallel pipeline runs can safely call this on every run.

The legacy pattern — create_missing_buckets: true on upload bodies — still works on the alias routes, but provisioning inside an upload call hides an important side effect. Prefer PUT /v1/buckets/{slug}.

Read one bucket

GET /v1/buckets/{bucket}

Id or slug; archived buckets are readable (a read never hides a bucket the delete surface can still address). Same payload shape as one item of the bucket list, including member counts and store readiness.

Membership

GET    /v1/buckets/{bucket}/sources          List members: id, type, title, status, ready
DELETE /v1/buckets/{bucket}/sources/{id}     Detach a source from this bucket only

Detaching removes the membership and the bucket's search-index copy; the canonical source, its storage, and its other bucket memberships are untouched.

Canonical routes and aliases

/v1/buckets is the canonical Manage surface — list, create, ensure-by-slug, read, membership, and delete all live there. The permanent alias surface is exactly two routes: GET /v1/knowledge/buckets (list) and POST /v1/knowledge/buckets (create). Read, ensure-by-slug, membership, and delete have no alias and live only under /v1/buckets.

Alias responses advertise the successor via Deprecation: true and Link: </v1/buckets>; rel="successor-version" headers, with no sunset date — the alias is not scheduled for removal, and integrations calling it (including the MCP server and the dashboard) keep working unchanged.

Deleting sources globally

DELETE /v1/sources/{id}
POST   /v1/knowledge/files:delete            Bulk (up to 100 ids)

The bulk body is {"file_ids": ["...", ...], "force": true} — the field is file_ids (max 100); force behaves per item exactly like the single delete.

Global delete removes the source everywhere: storage, search-index copies, and every bucket membership. Without force=true it is blocked (409 delete_blocked) when dependents exist — bucket memberships count as dependents, as do agent policies, widgets, and workflows referencing the source; details.dependencies enumerates them.

Delete a bucket

Deleting a bucket cascades over its member files — storage, search-index copies, and policy references are all removed. It requires the explicit-only knowledge:bucket:delete capability and force=true:

DELETE /v1/buckets/{bucket}?force=true
Authorization: Bearer sk-...

{bucket} accepts an id or slug; archived buckets are deletable too. Without force=true the call is refused with 400 force_required.

The response is a cascade manifest, not a bare boolean:

{
  "object": "bucket_deleted",
  "ok": true,
  "bucket_id": "bucket_abc",
  "deleted_bucket": true,
  "requested_files": 3,
  "deleted_files": 3,
  "already_missing_files": 0,
  "failed_files": 0,
  "items": [ { "file_id": "file_1", "status": "deleted" } ],
  "bucket_status": "deleted",
  "request_id": "req_..."
}

bucket_status is "deleted" on success and "restored_partial_failure" when a member failed and the bucket was restored for retry. While a cascade is running, a second delete answers 409 delete_in_progress (retryable). What a later retry sees depends on how the cascade ended: after a fully successful cascade the bucket is gone and retries return 404; after a partial failure the bucket is restored with its surviving members, and a retry returns 200 and re-runs the cascade (see the retryable semantics above).

Semantics to rely on:

  • No soft-delete. Once the delete completes (deleted_bucket: true), a retry returns 404 bucket_not_found.
  • Partial failure keeps evidence — and stays retryable. If any member fails to delete, the bucket and its store are retained with the failed members (ok: false, deleted_bucket: false); inspect items[]. A retry returns 200 and re-runs the cascade: already-deleted members are counted as already_missing_files, and once every member succeeds the bucket itself is removed. Only after that does the endpoint start returning 404.
  • One capability authorizes the whole cascade. knowledge:bucket:delete alone covers the member-file deletions — knowledge:file:delete is not additionally required (bucket delete is the strictly stronger grant). Detaching a single file from the bucket without deleting it (DELETE /v1/buckets/{bucket}/sources/{id}) is the operation that requires knowledge:file:delete.
  • Detaching a file from one bucket without deleting it stays DELETE /v1/buckets/{bucket}/sources/{id} (see Membership above).

Connecting buckets to agents

An agent's saved policy names the buckets it searches. calypso-agent uses the workspace default policy; named agents (calypso-agent:{agent_id}) can each point at different buckets. See Agents.

Next steps