docs(kobo,koreader,collections): Beam WO5 batch 6 — 12 endpoints

Kobo emulation + KOReader calibre-compatible surface backfilled with
device-token auth; collections reorder + update_rule created.

Claude registry: 12 entries. kobo/koreader emulation endpoints all bind
arbitrary protocol POST bodies (markup/bookmark/gettests/sync-from-server,
koreader progress/bookmarks) — registered as AnyJSONObject, not bodyless as
first drafted. Initialization returns the library sync payload, not the init
response.

Contract correction in update_rule.md: the route is SetRuleEnabled ({enabled}
required -> {collection_id, is_smart}); Beam's draft invented rule-field
updates. Reordered response/status: {item_ids} -> {status: reordered}.

make test 9/9.
This commit is contained in:
John O'Keefe committed 2026-10-24 16:25:00 -04:00
1 parent b77eab7ffd
commit 25ef0d12ba
13 files changed
+268 -57

No files matched your search

@@ -0,0 +1,47 @@
---
method: PUT
path: /api/collections/:id/items/order
auth: jwt
area: collections
---
<!-- source: internal/handlers/collections.go:1332 CollectionHandler.ReorderItems -->
# Reorder Collection Items
Applies a static collection's manual playlist order. Owner-only; the payload
is the COMPLETE ordered id list — ids omitted keep their relative order
appended after (the service never deletes membership).
## Example request
```json example-request
{
"item_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"660e8400-e29b-41d4-a716-446655440001"
]
}
```
## Example response
```json example-response
{
"status": "reordered"
}
```
## Fields
| Field | Type | Required | Notes |
| ----- | ---- | -------- | ----- |
| item_ids | array of string (uuid) | Yes | Complete ordered list of media item ids |
## Errors
| Status | Body | When |
| ------ | ---- | ---- |
| 400 | `{"error": "item_ids must be a non-empty ordered id list"}` / `{"error": "invalid media item id: ..."}` | malformed or empty list |
| 401 | `{"error": "unauthorized"}` | no/invalid JWT |
| 403 | `{"error": "only the collection owner can reorder it"}` | non-owner |
| 500 | `{"error": "..."}` | unexpected server error |
@@ -0,0 +1,51 @@
---
method: PUT
path: /api/collections/:id/rules/:ruleId
auth: jwt
area: collections
---
<!-- source: internal/handlers/collections.go:922 CollectionHandler.UpdateRule (SetRuleEnabled) -->
# Update Collection Rule
Despite the route name, this endpoint **toggles a rule's enabled state only**.
It does not update the rule's field/operator/value — rules are replaced wholesale
via [Create Collection](create_collection.md) (`auto_assign_rules`) or added via
the rules endpoint.
## Example request
```json example-request
{
"enabled": false
}
```
## Example response
```json example-response
{
"collection_id": "550e8400-e29b-41d4-a716-446655440000",
"is_smart": true,
"enabled": null,
"field": null,
"id": null,
"operator": null,
"priority": null,
"value": null
}
```
## Request fields
| Field | Type | Required | Notes |
| ----- | ---- | -------- | ----- |
| enabled | boolean | yes | toggles the rule on/off |
## Errors
| Status | Body | When |
| ------ | ---- | ---- |
| 400 | `{"error": "invalid collection id"}` / `{"error": "rule id required"}` / `{"error": "invalid request"}` | malformed |
| 404 | `{"error": "rule not found"}` | no such rule |
| 500 | `{"error": "..."}` | unexpected server error |
+10 -2
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/sync/kobo/:token/v1/analytics/gettests
auth: device-token
area: kobo
---
<!-- source: internal/handlers/kobo.go:702 KoboHandler.AnalyticsGettests -->
# Analytics GetTests # Analytics GetTests
> **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change. > **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change.
@@ -19,7 +27,7 @@ This endpoint requires device authentication (not user JWT). This is a Kobo comp
### Example Request ### Example Request
```json ```json example-request
{ {
"data": {} "data": {}
} }
@@ -27,7 +35,7 @@ This endpoint requires device authentication (not user JWT). This is a Kobo comp
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"result": "ok" "result": "ok"
} }
+13 -4
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/sync/kobo/:token/bookmark
auth: device-token
area: kobo
---
<!-- source: internal/handlers/kobo.go:613 KoboHandler.Bookmark -->
# Bookmark Sync # Bookmark Sync
> **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change. > **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change.
@@ -29,7 +37,7 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
### Example Request ### Example Request
```json ```json example-request
{ {
"bookmarks": [ "bookmarks": [
{ {
@@ -45,10 +53,11 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"message": "Bookmarks synced successfully", "BookmarksSynced": null,
"synced_count": 1 "MarkupsSynced": null,
"Status": null
} }
``` ```
+12 -6
View File
@@ -1,3 +1,11 @@
---
method: GET
path: /api/sync/kobo/:token/v1/initialization
auth: device-token
area: kobo
---
<!-- source: internal/handlers/kobo.go:282 KoboHandler.Initialization -->
# Kobo Initialization # Kobo Initialization
> **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change. > **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change.
@@ -29,13 +37,11 @@ X-Device-Key: device-auth-key
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"result": "ok", "last_sync": null,
"resources": { "library_sync": null,
"image_host": "https://cdn.kobo.com/...", "total_books": null
"image_url_quality_template": "{ImageId}/h270/{ImageId}_landscape_cover.jpg"
}
} }
``` ```
+13 -4
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/sync/kobo/:token/markup
auth: device-token
area: kobo
---
<!-- source: internal/handlers/kobo.go:396 KoboHandler.Markup -->
# Markup Sync # Markup Sync
> **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change. > **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change.
@@ -33,7 +41,7 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
### Example Request ### Example Request
```json ```json example-request
{ {
"bookmarks": [ "bookmarks": [
{ {
@@ -53,10 +61,11 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"message": "Markup synced successfully", "BookmarksSynced": null,
"synced_count": 1 "MarkupsSynced": null,
"Status": null
} }
``` ```
+13 -18
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/sync/kobo/:token/sync-from-server
auth: device-token
area: kobo
---
<!-- source: internal/handlers/kobo.go:751 KoboHandler.SyncFromServer -->
# Sync From Server # Sync From Server
> **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change. > **Status: Coming Soon** — Native Kobo sync is not yet supported on real devices; this endpoint is under active development and may change.
@@ -20,7 +28,7 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
### Example Request ### Example Request
```json ```json example-request
{ {
"book_ids": ["content-id-1", "content-id-2"], "book_ids": ["content-id-1", "content-id-2"],
"full_sync": false "full_sync": false
@@ -29,24 +37,11 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"books": [ "BookmarksSent": null,
{ "BooksSynced": null,
"ContentID": "content-id-1", "HighlightsSent": null
"Title": "Book Title",
"Author": "Author Name",
"ISBN": "978-0123456789",
"PublicationDate": "2026-02-08",
"FileSize": 1234567,
"CoverImageId": "cover-id-1",
"Metadata": {
"language": "en",
"publisher": "Publisher Name"
}
}
],
"total": 2
} }
``` ```
+12 -11
View File
@@ -1,3 +1,11 @@
---
method: GET
path: /api/sync/koreader/library
auth: device-token
area: koreader
---
<!-- source: internal/handlers/koreader.go:1391 KOReaderHandler.GetLibrary -->
# Get Library # Get Library
Get library metadata for KOReader device. Get library metadata for KOReader device.
@@ -26,18 +34,11 @@ X-Device-Key: device-auth-key
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"books": [ "last_sync": null,
{ "library_sync": null,
"id": "uuid", "total_books": null
"title": "Book Title",
"authors": ["Author Name"],
"file": "book.epub",
"size": 1234567
}
],
"total": 42
} }
``` ```
+9 -1
View File
@@ -1,3 +1,11 @@
---
method: GET
path: /api/sync/koreader/metadata/:uuid
auth: device-token
area: koreader
---
<!-- source: internal/handlers/koreader.go:1025 KOReaderHandler.GetMetadata -->
# Get Metadata # Get Metadata
Get a book's stored progress and annotations for a KOReader device — Get a book's stored progress and annotations for a KOReader device —
@@ -23,7 +31,7 @@ Authorization: Bearer {device_token}
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"uuid": "774641f9-317b-4087-8e04-53bb4392ae56", "uuid": "774641f9-317b-4087-8e04-53bb4392ae56",
"title": "1984", "title": "1984",
+12 -6
View File
@@ -1,4 +1,13 @@
# Resolve Book ---
method: GET
path: /api/sync/koreader/resolve
auth: device-token
area: koreader
---
<!-- source: internal/handlers/koreader.go:1002 KOReaderHandler.ResolveBook -->
# Resolve Book:<|end|><|start|> assistant to=edit<|channel|>analysis <|constrain|>xml<|message|><rfl:parameter name="path">
/home/nymusicman/Code/bookhoard/docs/developer/api/koreader/sync_bookmarks.md
Map a book's file SHA-256 to its Bookhoard UUID without touching progress Map a book's file SHA-256 to its Bookhoard UUID without touching progress
state. Used by devices to link a freshly downloaded book before their first state. Used by devices to link a freshly downloaded book before their first
@@ -32,12 +41,9 @@ Authorization: Bearer {device_token}
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"book_uuid": "550e8400-e29b-41d4-a716-446655440000", "book_uuid": "550e8400-e29b-41d4-a716-446655440000"
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"title": "Book Title",
"author": "Author Name"
} }
``` ```
+10 -2
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/sync/koreader/bookmarks
auth: device-token
area: koreader
---
<!-- source: internal/handlers/koreader.go:1456 KOReaderHandler.SyncBookmarks -->
# Sync Bookmarks # Sync Bookmarks
Sync bookmarks, notes, and highlights from a KOReader device (bidirectional — the response also returns the server's current state for the book so the device can reconcile). Sync bookmarks, notes, and highlights from a KOReader device (bidirectional — the response also returns the server's current state for the book so the device can reconcile).
@@ -52,7 +60,7 @@ KOReader paints highlights from a fixed palette of color names; the web reader u
### Example Request ### Example Request
```json ```json example-request
{ {
"book_sha256": "64-hex-char-sha256", "book_sha256": "64-hex-char-sha256",
"bookmarks": [ "bookmarks": [
@@ -83,7 +91,7 @@ KOReader paints highlights from a fixed palette of color names; the web reader u
## Response (200 OK) ## Response (200 OK)
```json ```json example-response
{ {
"sync_status": "ok", "sync_status": "ok",
"bookmarks_synced": 1, "bookmarks_synced": 1,
+12 -3
View File
@@ -1,3 +1,11 @@
---
method: POST
path: /api/sync/koreader/progress
auth: device-token
area: koreader
---
<!-- source: internal/handlers/koreader.go:418 KOReaderHandler.SyncProgress -->
# Sync Progress # Sync Progress
Push reading progress from a KOReader device. Push reading progress from a KOReader device.
@@ -42,7 +50,7 @@ Authorization: Bearer {device_token}
Content-Type: application/json Content-Type: application/json
``` ```
```json ```json example-request
{ {
"books": [ "books": [
{ {
@@ -67,11 +75,12 @@ Content-Type: application/json
## Response (202 Accepted) ## Response (202 Accepted)
```json ```json example-response
{ {
"sync_status": "ok", "sync_status": "ok",
"books_synced": 1, "books_synced": 1,
"timestamp": "2026-09-26T21:25:09Z" "timestamp": "2026-09-26T21:25:09Z",
"device_updated": null
} }
``` ```
+54
View File
@@ -552,4 +552,58 @@ var Schemas = map[string]Schema{
// Delegates to ListMediaItems with library_id injected. // Delegates to ListMediaItems with library_id injected.
Response: json.RawMessage(`{"data": [{}]}`), Response: json.RawMessage(`{"data": [{}]}`),
}, },
// --- Beam batch 6: kobo/koreader (device-token) + collections missing ---
"POST /api/sync/kobo/:token/markup": {
Request: AnyJSONObject{}, // kobo protocol payload (ReadingSync/BookmarkSync)
Response: handlers.KoboSyncStatus{}, // kobo firmware naming: Status/MarkupsSynced
},
"POST /api/sync/kobo/:token/bookmark": {
Request: AnyJSONObject{}, // kobo bookmark payload
Response: handlers.KoboSyncStatus{},
},
"POST /api/sync/kobo/:token/v1/analytics/gettests": {
Request: AnyJSONObject{}, // kobo analytics POST body
// A/B test payload — dynamic keys (tests/features/configuration).
Response: AnyJSONObject{},
},
"GET /api/sync/kobo/:token/v1/initialization": {
// NOTE: returns the library sync payload, not the init response.
Response: handlers.KoboLibraryResponse{},
},
"POST /api/sync/kobo/:token/sync-from-server": {
Request: AnyJSONObject{}, // kobo sync POST body
Response: handlers.KoboServerSyncResponse{},
},
"POST /api/sync/koreader/progress": {
Request: handlers.KOReaderProgressRequest{},
Response: handlers.KOReaderSyncResponse{},
},
"GET /api/sync/koreader/resolve": {
Response: handlers.KOReaderResolveResponse{},
},
"GET /api/sync/koreader/metadata/:uuid": {
// Hand-built map in the handler.
Response: AnyJSONObject{},
},
"GET /api/sync/koreader/library": {
Response: handlers.KOReaderLibraryResponse{},
},
"POST /api/sync/koreader/bookmarks": {
Request: AnyJSONObject{}, // {highlights: [...]} inline struct in handler
Response: AnyJSONObject{}, // hand-built map in the handler
},
"PUT /api/collections/:id/items/order": {
// Inline request {item_ids} — pinned by sample.
Request: json.RawMessage(`{"item_ids": []}`),
Response: json.RawMessage(`{"status": "reordered"}`),
},
"PUT /api/collections/:id/rules/:ruleId": {
// Inline anonymous request struct — pinned by sample.
Request: json.RawMessage(`{"rule": {}}`),
Response: json.RawMessage(`{
"id": "", "collection_id": "", "field": "", "operator": "",
"value": "", "priority": 0, "enabled": false, "is_smart": false
}`),
},
} }