Files
bookhoard/docs/developer/api/progress/get_media_progress.md
T
John O'Keefe b10bf3e8c7 docs(api): real progress endpoint contract; remove nonexistent-endpoint docs
The progress documentation described GET/POST /api/progress/:id —
endpoints that do not exist in the router — while the endpoint every
client actually uses (GET/PUT/DELETE /api/media-items/:id/progress) had
no field-level docs at all.

New:
- progress/position-contract.md: the canonical position model — server
  as position authority, the three-tier submission (percentage /
  context_text / epubcfi), ingest verification and healing, the restore
  handles, the OPF spine numbering hazard (canonical CFI spine steps
  include linear="no" items; clients resolve documents by anchor_href
  and land by css_selector + char_offset, never by spine step), the
  offset currencies (UTF-16 at the wire, runes internal), and
  context_text rules.
- progress/get_media_progress.md and update_media_progress.md: the real
  endpoints with full field tables, conditionality of the restore
  handles, the first-page anti-clobber guard, and healed-response
  semantics.
- progress/delete_media_progress.md: the real DELETE route.
- koreader/sync_progress.md and koreader/get_metadata.md rewritten to
  the actual payloads: the plugin sends a single-book array whose
  "epubcfi" field is a CRE xpointer; the metadata response navigates
  via koreader_xpointer (canonical CFI converted back to CRE), with
  page as the canonical locator for fixed-layout books.

Removed: the five files documenting the nonexistent /api/progress/:id
GET/POST/DELETE endpoints. Kept get_progress_history.md (that route
exists). The legacy developer/api-reference.md and the indexed
api/api-reference.md progress sections now match the wire and link the
new docs; the duplicate "Universal Progress" section points at Reading
Progress.
2026-09-26 21:27:44 -04:00

99 lines
3.8 KiB
Markdown

# Get Media Item Progress
Get the stored reading progress for a media item, plus the
server-derived restore handles for reflowable books.
**Endpoint**: `GET /api/media-items/:id/progress`
**Auth**: Required (Bearer token)
See [Position Contract](position-contract.md) for the semantics of every
field — what is verified, what the currencies are, and how clients
should restore.
## Path Parameters
| Parameter | Type | Required | Description |
| --------- | ------------- | -------- | --------------- |
| id | string (UUID) | Yes | Media item UUID |
### Example Request
```http
GET /api/media-items/774641f9-317b-4087-8e04-53bb4392ae56/progress
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```
## Response (200 OK) — no progress stored
When the item has no progress row, an empty fixed-layout-style stub is
returned (not 404):
```json
{
"current_page": 0,
"total_pages": null
}
```
## Response (200 OK) — progress stored
```json
{
"id": "e8239659-0ed6-42ae-a946-8440d7655b42",
"media_item_id": "774641f9-317b-4087-8e04-53bb4392ae56",
"user_id": "1b64992e-3408-4d84-9e03-e2dc4950e1dd",
"current_page": null,
"total_pages": null,
"last_read_at": "2026-09-26T14:52:34.806446Z",
"percentage": 0.045,
"character_offset": 16375,
"epubcfi": "epubcfi(/6/4!/4/8[_idContainer003]/58/1:598)",
"chapter": null,
"chapter_progress": null,
"format_group": "reflowable",
"total_characters": 592216,
"chapter_count": 1,
"last_sync_device": "web",
"last_sync_source": "koreader",
"last_sync_timestamp": "2026-09-26T14:52:34.806446Z",
"css_selector": "body>div:nth-child(4)>p:nth-child(29)",
"anchor_href": "1984.xhtml",
"char_offset": 598,
"context_text": "from day to day, but there was none in which Goldstein was not the principal figure. He was the prim"
}
```
### Field Reference
Base fields (always present when a row exists):
| Field | Type | Description |
| ----- | ---- | ----------- |
| `percentage` | float | Position as a fraction of the whole book (0..1). |
| `epubcfi` | string | The stored canonical standard CFI. **Spine steps index the OPF spine as written, including `linear="no"` items — do not resolve them against a readium reading order.** See the [Position Contract](position-contract.md#spine-numbering-hazard--read-this-before-parsing-a-stored-cfi). |
| `context_text` | string | The stored verification context (≤100 whitespace-normalized chars from the anchor). |
| `character_offset` | int | Book-wide rune offset. Internal currency — consistent with `total_characters`. |
| `current_page`, `total_pages` | int | Fixed-layout page position (null for reflowable). |
| `chapter`, `chapter_progress` | int, float | Chapter index and within-chapter fraction, when known. |
| `format_group` | string | `reflowable`, `fixed_layout`, `comic_archive`, … Gates the restore handles. |
| `total_characters`, `chapter_count` | int | Book metrics, for client-side fraction math. |
| `last_sync_device`, `last_sync_source`, `last_sync_timestamp` | — | Which client last wrote the row. |
Restore handles (conditional — served only for convertible reflowable
books whose stored anchor re-resolves at GET time):
| Field | Type | Description |
| ----- | ---- | ----------- |
| `anchor_href` | string | The spine document containing the anchor (e.g. `1984.xhtml`). **Resolve your resource by this**, never by the CFI's spine step. |
| `css_selector` | string | Body-relative chain to the anchor's block element. |
| `char_offset` | int | Anchor offset within the block's concatenated text, in UTF-16 code units. |
| `epubcfi` | string | Re-served (possibly healed) canonical CFI — present whenever re-verification produced one. |
## Error Responses
| Code | Description |
| ---- | ----------- |
| 400 | Invalid media item id |
| 401 | Invalid or expired token |
| 500 | Database error |