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:
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 |
|
||||
@@ -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
|
||||
|
||||
> **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
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"data": {}
|
||||
}
|
||||
@@ -27,7 +35,7 @@ This endpoint requires device authentication (not user JWT). This is a Kobo comp
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"result": "ok"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
> **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
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"bookmarks": [
|
||||
{
|
||||
@@ -45,10 +53,11 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"message": "Bookmarks synced successfully",
|
||||
"synced_count": 1
|
||||
"BookmarksSynced": null,
|
||||
"MarkupsSynced": null,
|
||||
"Status": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
> **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)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"result": "ok",
|
||||
"resources": {
|
||||
"image_host": "https://cdn.kobo.com/...",
|
||||
"image_url_quality_template": "{ImageId}/h270/{ImageId}_landscape_cover.jpg"
|
||||
}
|
||||
"last_sync": null,
|
||||
"library_sync": null,
|
||||
"total_books": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
> **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
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"bookmarks": [
|
||||
{
|
||||
@@ -53,10 +61,11 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"message": "Markup synced successfully",
|
||||
"synced_count": 1
|
||||
"BookmarksSynced": null,
|
||||
"MarkupsSynced": null,
|
||||
"Status": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
> **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
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"book_ids": ["content-id-1", "content-id-2"],
|
||||
"full_sync": false
|
||||
@@ -29,24 +37,11 @@ This endpoint requires device authentication (not user JWT). Kobo devices authen
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"books": [
|
||||
{
|
||||
"ContentID": "content-id-1",
|
||||
"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
|
||||
"BookmarksSent": null,
|
||||
"BooksSynced": null,
|
||||
"HighlightsSent": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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 metadata for KOReader device.
|
||||
@@ -26,18 +34,11 @@ X-Device-Key: device-auth-key
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"books": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"title": "Book Title",
|
||||
"authors": ["Author Name"],
|
||||
"file": "book.epub",
|
||||
"size": 1234567
|
||||
}
|
||||
],
|
||||
"total": 42
|
||||
"last_sync": null,
|
||||
"library_sync": null,
|
||||
"total_books": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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 a book's stored progress and annotations for a KOReader device —
|
||||
@@ -23,7 +31,7 @@ Authorization: Bearer {device_token}
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"uuid": "774641f9-317b-4087-8e04-53bb4392ae56",
|
||||
"title": "1984",
|
||||
|
||||
@@ -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
|
||||
state. Used by devices to link a freshly downloaded book before their first
|
||||
@@ -32,12 +41,9 @@ Authorization: Bearer {device_token}
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"book_uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"title": "Book Title",
|
||||
"author": "Author Name"
|
||||
"book_uuid": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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, 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
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"book_sha256": "64-hex-char-sha256",
|
||||
"bookmarks": [
|
||||
@@ -83,7 +91,7 @@ KOReader paints highlights from a fixed palette of color names; the web reader u
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"sync_status": "ok",
|
||||
"bookmarks_synced": 1,
|
||||
|
||||
@@ -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
|
||||
|
||||
Push reading progress from a KOReader device.
|
||||
@@ -42,7 +50,7 @@ Authorization: Bearer {device_token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"books": [
|
||||
{
|
||||
@@ -67,11 +75,12 @@ Content-Type: application/json
|
||||
|
||||
## Response (202 Accepted)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"sync_status": "ok",
|
||||
"books_synced": 1,
|
||||
"timestamp": "2026-09-26T21:25:09Z"
|
||||
"timestamp": "2026-09-26T21:25:09Z",
|
||||
"device_updated": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -552,4 +552,58 @@ var Schemas = map[string]Schema{
|
||||
// Delegates to ListMediaItems with library_id injected.
|
||||
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
|
||||
}`),
|
||||
},
|
||||
}
|
||||
Reference in new issue
Block a user