Files
bookhoard/docs/developer/api/collections/list_collections.md
T
John O'Keefe a86bdac87e refactor(collections): retire the legacy rules surface — tree-only storage
The pre-tree rules machinery is deleted: no row ever carried a rules
array or a non-default match mode (verified in the dev DB), the web
never read the envelope keys, and the Android app consumes none of it.
Dead code gets no documentation.

Removed:
- PUT /api/collections/:id/match-mode — endpoint, route, and the
  UpdateCollectionMatchMode query. The tree's root mode owns any|all.
- auto_assign_rules / rule_match_mode from every envelope
  (create/list/detail/update), from the create/update request shapes,
  and from the schema: both columns DROPPED (schema.sql carries
  idempotent DROP COLUMN IF EXISTS heals for existing DBs; sqlc
  regenerated).
- The rules CRUD service layer (AddRule/SetRuleEnabled/RemoveRule +
  validateRule + UpdateCollectionRulesJSON), ParseRules /
  EnabledRules / IsSmartCollection, RulesToFilterTree (+ the SSR
  preload branch), the legacy branch of EvaluateSmartMembership, and
  SmartEvalOptions.MatchMode. IsCollectionSmart = tree presence;
  create-path isSmart = collection_type=smart OR a filter_tree.

Kept (live consumers): POST /api/collections/preview still evaluates
rules payloads in memory (the custom-section builder's preview), and
include_auto stays on the list (the Android app sends it).

The custom-section builder's SAVE was already broken — its
stringified auto_assign_rules payload failed request binding (probed
live: 400 invalid request). Create now rejects that payload loudly
with a 400 naming the retirement, instead of silently creating a
hollow static section; custom-section-builder.md carries the flag.
The builder's tree migration remains the flagged follow-up.

Test-side (asymmetry rule — deleted behavior, deleted tests):
TestPodcastSmartCollections rewritten onto trees (mode lives in the
tree root), match-mode subtests and tree_wins_over_legacy_rules
deleted, TestRulesToFilterTree deleted, contract-pin envelope keys
retracted per the contract_pins 2026-10-31 retirement entry. Mirrors
hand-synced: openapi.yaml, bruno create/update, docs/api/*.md,
docs/schema.go, api.d.ts. Full container suite green (155s).
2026-10-31 19:06:48 -04:00

1.8 KiB

method, path, auth, area
method path auth area
GET /api/collections jwt collections

List Collections

Lists the signed-in user's collections with live book counts. Smart (rule-driven) collections are excluded by default so legacy readers never see stale membership — pass include_auto=true to include them.

Example request

GET /api/collections?include_auto=true&sort_by=name

Example response

{
  "collections": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "To Read",
      "description": "Books I want to read soon",
      "color": "#00FF00",
      "icon": "📖",
      "created_at": "2026-10-24 13:57:01.81234 +0000 UTC",
      "book_count": 12,
      "is_smart": false
    }
  ],
  "total": 1
}

created_at uses Go's time.String() format, not RFC3339.

Query parameters

Param Type Default Notes
include_auto boolean false include smart collections
sort_by string — name sorts alphabetically; otherwise service order
library_id uuid — scopes book counts to one library (smart collections only)

Response fields

Field Type Notes
collections array CollectionSummary objects, see below
total number length of collections

Collection item

Field Type Notes
id string (uuid)
name string
description string
color string
icon string
created_at string Go time.String() format
book_count number live count; respects library_id for smart collections
is_smart boolean

Errors

Status Body When
500 {"error": "..."} unexpected server error