Files
bookhoard/docs/developer/api/koreader/sync_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

3.0 KiB

Sync Progress

Push reading progress from a KOReader device.

Endpoint: POST /api/sync/koreader/progress Auth: Required (Device authentication — Authorization: Bearer {device_token})

This is the device-native tier of the Position Contract: the KOReader payload carries a CRE xpointer and the server converts it to the canonical standard CFI, verifies it against the submitted context_text, and heals it on mismatch — exactly like every other client.

Request Body

Field Type Required Description
books array Yes One book object (the reference client sends a single-element array).
sync_mode string No immediate (default) or manual.

Book Object

Field Type Required Description
uuid string (UUID) No Bookhoard UUID, once the device has linked the book via Resolve Book.
sha256 string Yes File content hash (64 hex chars) — the primary book identity.
title string No Document title.
authors array No Author names.
percentage float Yes Position as a fraction of the book (0..1).
context_text string No Up to 100 whitespace-normalized chars from the current position — enables the server's verification/healing. Strongly recommended.
page int No Current page (fixed-layout books).
total_pages int No Page count (fixed-layout books).
epubcfi string Reflowable only A CRE xpointer (/body/DocFragment[N]/body/...), not a CFI — the field name is historical. Fixed-layout books must omit it and carry their position in page/total_pages.
file_path string No Device-local file path (informational).
device_info object No { koreader_version, device_model }.

Example Request

POST /api/sync/koreader/progress
Authorization: Bearer {device_token}
Content-Type: application/json
{
  "books": [
    {
      "uuid": "774641f9-317b-4087-8e04-53bb4392ae56",
      "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "title": "1984",
      "authors": ["George Orwell"],
      "percentage": 0.045,
      "context_text": "from day to day, but there was none in which Goldstein was not the principal figure. He was the prim",
      "page": 14,
      "total_pages": 311,
      "epubcfi": "/body/DocFragment[1]/body/p[29]/text().598",
      "device_info": {
        "koreader_version": "v2026.07.1",
        "device_model": "emulator"
      }
    }
  ],
  "sync_mode": "immediate"
}

Response (202 Accepted)

{
  "sync_status": "ok",
  "books_synced": 1,
  "timestamp": "2026-09-26T21:25:09Z"
}

Per-book results and any detected sync conflicts are carried in book_results and conflicts when present.

Error Responses

Code Description
400 Invalid request format
401 Device authentication failed
500 Database error