# 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](../progress/position-contract.md): 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](resolve_book.md). | | `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 ```http POST /api/sync/koreader/progress Authorization: Bearer {device_token} Content-Type: application/json ``` ```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) ```json { "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 |