Beam backfilled 11 library docs (CRUD, folders, visibility, browse, stats,
media-items) and re-attached frontmatter to create_collection.md — which had
silently lost its pilot version in cc893f75: the negative-test
'git checkout --' restored the OLD file because the pilot was never staged,
and a frontmatter-less doc is invisible to the gate. Claude restored the full
corrected pilot content (priority higher-wins, time.String() note, 409).
Claude registry: 11 entries + one inverted catch — DELETE /:id/folders DOES
bind {folder_path} (handler reuses AddLibraryFolderRequest); registry first
draft said bodyless. GET /:id/visibility returns joined
GetUserVisibleLibrariesRow (type_name etc.), not bare Libraries.
Gate-driven merges fixed create/get library response shapes (sqlc row keys:
created_by_admin_id, library_type_id, updated_at; dropped invented type/
folders/is_visible). Fixed invented request fields (user_id in
set-visibility; library_type_id in update). make test 9/9.
2.6 KiB
2.6 KiB
method, path, auth, area
| method | path | auth | area |
|---|---|---|---|
| POST | /api/collections | jwt | collections |
Create Collection
Creates a new collection for the signed-in user. If any rules are present (or
collection_type is "smart"), the collection is a smart collection whose
membership is evaluated live; otherwise it is static.
Creating a collection whose name already exists for the user returns 409.
Example request
{
"name": "To Read",
"description": "Books I want to read soon",
"color": "#00FF00",
"icon": "📖",
"collection_type": "static",
"auto_assign_rules": [],
"view_settings": { "kobo": { "view_mode": "grid" } }
}
Example response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "8f14e45f-ceea-467f-a429-0b21bfc0b1a2",
"name": "To Read",
"description": "Books I want to read soon",
"color": "#00FF00",
"icon": "📖",
"collection_type": "static",
"auto_assign_rules": null,
"view_settings": { "kobo": { "view_mode": "grid" } },
"book_count": 0,
"created_at": "2026-10-24 13:57:01.81234 +0000 UTC"
}
Note: created_at is serialized with Go's time.String() format, not
RFC3339. Treat it as display text; do not parse it as an ISO timestamp.
Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | yes | unique per user |
| description | string | no | |
| color | string | no | hex color, e.g. "#FF5733" |
| icon | string | no | emoji |
| collection_type | string | no | "static" (default) or "smart"; also implied by non-empty auto_assign_rules |
| auto_assign_rules | array | no | rule objects, see below |
| view_settings | object | no | per-device display preferences |
Auto-assign rule object
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string | no | rule id |
| field | string | yes | one of genre, series, author, language, publisher, copyright_year, tags |
| operator | string | yes | one of equals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than |
| value | string | yes | value to compare against |
| priority | number | no | 1-10; higher values take precedence |
| enabled | boolean | no | absent = enabled (rules written before this field existed keep matching) |
Errors
| Status | Body | When |
|---|---|---|
| 400 | {"error": "..."} |
invalid JSON or validation failure |
| 409 | {"error": "a collection with this name already exists"} |
name taken |
| 500 | {"error": "..."} |
unexpected server error |