docs(media-items): Work Order 3 batch 3a — reader-adjacent CRUD (11 endpoints)

New frontmatter'd docs with real shapes (sqlc models pinned directly):
- bookmarks: get/create/update/delete (sync-aware rows: dedup_key,
  last_modified_*, tombstones; delete is 204)
- review: upsert (empty string DELETES the review) / get (200 with
  {review:null} when absent) / delete
- get single note + highlight (full sqlc row shapes)
- annotation history: restore (POST, kind via query or body) and purge
  (DELETE, kind via required query; permanent)

Gate hardening forced by this batch:
- required-field walk now guards non-struct Request types (RawMessage pins)
- update_media_bookmark needed a full example block (no cross-reference
  shortcuts — the gate checks every doc standalone)
This commit is contained in:
John O'Keefe committed 2026-10-24 15:03:36 -04:00
1 parent 85cdaf7196
commit ed0ce5d077
13 files changed
+545 -4

No files matched your search

@@ -0,0 +1,71 @@
---
method: PUT
path: /api/media-items/:id/bookmarks/:bookmarkId
auth: jwt
area: media-items
---
<!-- source: internal/handlers/media.go:2252 MediaHandler.UpdateMediaBookmark -->
# Update Media Bookmark
Edits a bookmark through the sync service: lands as a proper last-write-wins
edit with `source`/`modified_at` bookkeeping. Zero-valued locator fields
coalesce with the stored row (omitted fields keep their values).
## Example request
```json example-request
{
"title": "Chapter 3 (renamed)",
"notes": "updated note",
"position": "kindle:pos:1:2"
}
```
## Example response
```json example-response
{
"id": "110e8400-e29b-41d4-a716-446655440000",
"media_item_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "92413c3a-e1b8-4fff-b3f9-95c74478d19a",
"page_number": null,
"chapter_number": null,
"cfi_position": "epubcfi(/6/4)",
"title": "Chapter 3 (renamed)",
"position": "kindle:pos:1:2",
"notes": "updated note",
"created_at": "2026-10-24T13:57:01Z",
"dedup_key": "kindle:pos:1:2",
"last_modified_at": "2026-10-24T14:57:01Z",
"last_modified_source": "web",
"device_sync_data": null,
"percentage_location": 35.5,
"epubcfi_location": "epubcfi(/6/4)",
"chapter_reference": 3,
"deleted": false,
"deleted_at": null,
"origin_source": "web"
}
```
## Request fields
| Field | Type | Required | Notes |
| ----- | ---- | -------- | ----- |
| title | string | yes | 1–255 chars |
| notes | string | no | |
| position | string | no | |
| cfi_position | string | no | zero value preserves stored |
| page_number | number | no | zero value preserves stored |
| chapter_number | number | no | zero value preserves stored |
| percentage | number | no | zero value preserves stored |
| chapter_reference | number | no | zero value preserves stored |
| origin | string | no | client label |
## Errors
| Status | Body | When |
| ------ | ---- | ---- |
| 400 | `{"error": "invalid bookmark id"}` / `{"error": "invalid media item id"}` / `{"error": "invalid request"}` | malformed |
| 500 | `{"error": "..."}` | annotation service failure |