# Bookhoard API Reference > **⚠️ Legacy Document**: This is a monolithic API reference (1,600+ lines). > For updated, split endpoint documentation with interactive API explorer, see [API Documentation Portal](api/api-reference.md). > > **Use the split docs for:** > > - Easier navigation by category > - Interactive API explorer > - Endpoint-specific examples > - Latest updates --- Complete API documentation for Bookhoard v1.0 with Universal Cross-Platform Sync support. ## Table of Contents 1. [Authentication](#authentication) 2. [Users & Profiles](#users--profiles) 3. [Libraries](#libraries) 4. [Media Items](#media-items) 5. [Reading Progress](#reading-progress) 6. [Notes & Highlights](#notes--highlights) 7. [Ratings](#ratings) 8. [Device Management](#device-management) 9. [Analytics](#analytics) 10. [Book Matching & Linking](#book-matching--linking) 11. [Collections](#collections) → See [COLLECTIONS_API.md](COLLECTIONS_API.md) 12. [OPDS](#opds-open-publication-distribution-system) 13. [Sync Protocol - KOReader](#sync-protocol---koreader) 14. [Sync Protocol - Kobo](#sync-protocol---kobo) 15. [Universal Progress](#universal-progress) 16. [Conflicts](#conflicts) 17. [Sync Queue](#sync-queue) 18. [WebSocket](#websocket) ## Base URL ``` Production: https://your-domain.com/api Development: http://localhost:8765/api ``` ## Authentication Most endpoints require authentication. Include your JWT token in the Authorization header: ``` Authorization: Bearer ``` ### Register User ```http POST /api/auth/register Content-Type: application/json { "email": "user@example.com", "username": "john", "password": "SecureP@ss123!", "first_name": "John", "last_name": "Doe" } ``` **Response** (201): ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "d4f5g6h7...", "user": { "id": "uuid-here", "email": "user@example.com", "username": "john", "role": "user", "theme": "tokyo-night", "created_at": "2026-01-31T10:00:00Z" } } ``` ### Login ```http POST /api/auth/login Content-Type: application/json { "email": "user@example.com", "password": "SecureP@ss123!" } ``` **Response** (200): ```json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "d4f5g6h7...", "user": { "id": "uuid-here", "email": "user@example.com", "username": "john", "role": "user" } } ``` ### Refresh Token ```http POST /api/auth/refresh Content-Type: application/json { "refresh_token": "d4f5g6h7..." } ``` **Response** (200): ```json { "token": "new-jwt-token", "refresh_token": "new-refresh-token" } ``` ### Logout ```http POST /api/auth/logout Authorization: Bearer ``` **Response** (204): No Content ## Users & Profiles ### Get Current User ```http GET /api/users/me Authorization: Bearer ``` **Response** (200): ```json { "id": "uuid", "email": "user@example.com", "username": "john", "first_name": "John", "last_name": "Doe", "theme": "tokyo-night", "role": "user", "max_devices": 10, "created_at": "2026-01-31T10:00:00Z" } ``` ### Update Profile ```http PUT /api/users/me/profile Authorization: Bearer Content-Type: application/json { "first_name": "John", "last_name": "Smith" } ``` ### Update Theme ```http PUT /api/users/me/theme Authorization: Bearer Content-Type: application/json { "theme": "dracula" } ``` ### Change Password ```http PUT /api/users/me/password Authorization: Bearer Content-Type: application/json { "current_password": "oldPassword", "new_password": "NewSecureP@ss123!" } ``` ### Update Scan Settings ```http PUT /api/libraries/scan-settings Authorization: Bearer Content-Type: application/json { "scan_poll_interval_seconds": 60, "auto_scan_enabled": true } ``` ## Libraries ### Get Visible Libraries ```http GET /api/libraries/visible Authorization: Bearer ``` **Response** (200): ```json { "libraries": [ { "id": "uuid", "name": "My Ebooks", "description": "Ebook collection", "type_name": "ebooks", "is_visible": true } ] } ``` ### Get Library Details ```http GET /api/libraries/{library_id} Authorization: Bearer ``` ### Create Library (Admin Only) ```http POST /api/libraries Authorization: Bearer Content-Type: application/json { "name": "Comics Collection", "description": "Digital comics", "type": "comics" } ``` ### Add Library Folder (Admin Only) ```http POST /api/libraries/{library_id}/folders Authorization: Bearer Content-Type: application/json { "folder_path": "/path/to/comics" } ``` ### Set Library Visibility (Admin Only) ```http POST /api/libraries/visibility Authorization: Bearer Content-Type: application/json { "user_id": "user-uuid", "library_id": "library-uuid", "is_visible": true } ``` ## Media Items ### List Media Items ```http GET /api/media-items?library_id={library_id}&limit=20&offset=0 Authorization: Bearer ``` **Query Parameters**: - `library_id` (required): UUID of library - `limit`: Number of items to return (max 100, default 20) - `offset`: Number of items to skip **Response** (200): ```json { "media_items": [ { "id": "uuid", "library_id": "uuid", "title": "Book Title", "author": "Author Name", "description": "Book description", "file_path": "/path/to/book.epub", "file_size": 1024000, "mime_type": "application/epub+zip", "cover_image_path": "/path/to/cover.jpg", "series": "Series Name", "series_number": 1, "tags": "sci-fi, space opera", "language": "en", "page_count": 350, "genre": "Science Fiction", "copyright_year": 2023, "created_at": "2026-01-31T10:00:00Z" } ], "total": 100 } ``` ### Get Media Item ```http GET /api/media-items/{media_id} Authorization: Bearer ``` ### Search Media Items ```http GET /api/media-items/search?q={query}&limit=20&offset=0 Authorization: Bearer ``` **Query Parameters**: - `q` (required): Search query (minimum 2 characters) - `limit`: Number of results (default 20) - `offset`: Number to skip **Response** (200): ```json { "results": [ { "id": "uuid", "title": "Book Title", "author": "Author Name", "match_score": 0.95 } ] } ``` ### Update Media Item (Admin Only) ```http PUT /api/media-items/{media_id} Authorization: Bearer Content-Type: application/json { "title": "Updated Title", "author": "Updated Author", "description": "Updated description", "series": "Series", "series_number": 2 } ``` ### Delete Media Item (Admin Only) ```http DELETE /api/media-items/{media_id} Authorization: Bearer ``` ## Reading Progress ### Get Reading Progress ```http GET /api/media-items/{media_id}/progress Authorization: Bearer ``` **Response** (200): ```json { "media_item_id": "uuid", "user_id": "uuid", "current_page": 45, "total_pages": 200, "percentage": 0.225, "character_offset": 15432, "epubcfi": "epubcfi(/6/4/2:15)", "chapter": 3, "chapter_progress": 0.5, "last_read_at": "2026-01-31T10:00:00Z", "format_group": "reflowable", "viewport_y": 0.12, "zoom_level": 1.0 } ``` ### Update Reading Progress ```http PUT /api/media-items/{media_id}/progress Authorization: Bearer Content-Type: application/json { "source": "web", "location": { "percentage": 0.45678, "epubcfi": "epubcfi(/6/4/2:15)", "character": 15432, "chapter": 3, "page": 89, "total_pages": 200 }, "device_metadata": { "device_type": "web", "user_agent": "Mozilla/5.0..." } } ``` **Response** (200): ```json { "sync_status": "success", "progress_updated": true, "devices_notified": ["device-1", "device-2"], "broadcast": true } ``` ### Delete Reading Progress ```http DELETE /api/media-items/{media_id}/progress Authorization: Bearer ``` ## Notes & Highlights ### Get Notes ```http GET /api/media-items/{media_id}/notes Authorization: Bearer ``` **Response** (200): ```json { "notes": [ { "id": "uuid", "media_item_id": "uuid", "user_id": "uuid", "content": "This is an interesting passage...", "position": "epubcfi(/6/4/2:15)", "percentage_location": 0.45, "character_start": 15432, "character_end": 15480, "epubcfi_location": "epubcfi(/6/4/2:15)", "created_at": "2026-01-31T10:00:00Z", "updated_at": "2026-01-31T10:00:00Z" } ] } ``` ### Create Note ```http POST /api/media-items/{media_id}/notes Authorization: Bearer Content-Type: application/json { "content": "This is a note", "position": "epubcfi(/6/4/2:15)", "percentage_location": 0.45, "epubcfi_location": "epubcfi(/6/4/2:15)" } ``` ### Update Note ```http PUT /api/media-items/notes/{note_id} Authorization: Bearer Content-Type: application/json { "content": "Updated note content", "position": "epubcfi(/6/4/2:20)" } ``` ### Delete Note ```http DELETE /api/media-items/notes/{note_id} Authorization: Bearer ``` ### Get Highlights ```http GET /api/media-items/{media_id}/highlights Authorization: Bearer ``` **Response** (200): ```json { "highlights": [ { "id": "uuid", "media_item_id": "uuid", "user_id": "uuid", "selection_text": "Highlighted text passage...", "start_position": "epubcfi(/6/4/2:15)", "end_position": "epubcfi(/6/4/2:20)", "color": "#ffff00", "percentage_start": 0.45, "percentage_end": 0.47, "character_start": 15432, "character_end": 15480, "epubcfi_start": "epubcfi(/6/4/2:15)", "epubcfi_end": "epubcfi(/6/4/2:20)", "created_at": "2026-01-31T10:00:00Z" } ] } ``` ### Create Highlight ```http POST /api/media-items/{media_id}/highlights Authorization: Bearer Content-Type: application/json { "selection_text": "Highlighted text...", "start_position": "epubcfi(/6/4/2:15)", "end_position": "epubcfi(/6/4/2:20)", "color": "#ffff00", "percentage_start": 0.45, "percentage_end": 0.47 } ``` ### Update Highlight ```http PUT /api/media-items/highlights/{highlight_id} Authorization: Bearer Content-Type: application/json { "selection_text": "Updated text", "color": "#00ff00" } ``` ### Delete Highlight ```http DELETE /api/media-items/highlights/{highlight_id} Authorization: Bearer ``` ## Ratings ### Get Rating ```http GET /api/media-items/{media_id}/rating Authorization: Bearer ``` **Response** (200): ```json { "rating": 8, "user_id": "uuid", "media_item_id": "uuid" } ``` ### Set Rating ```http POST /api/media-items/{media_id}/rating Authorization: Bearer Content-Type: application/json { "rating": 8 } ``` **Rating Scale**: 1-10 (odd numbers = half-stars: 1=0.5★, 2=1★, 3=1.5★, ..., 9=4.5★, 10=5★) ### Update Rating ```http PUT /api/media-items/{media_id}/rating Authorization: Bearer Content-Type: application/json { "rating": 9 } ``` ### Delete Rating ```http DELETE /api/media-items/{media_id}/rating Authorization: Bearer ``` ## Device Management ### Register Device ```http POST /api/devices/register Content-Type: application/json { "device_name": "My Kobo Clara", "device_type": "kobo|koreader|web|mobile", "device_identifier": "hardware-specific-id" } ``` **Response** (201): ```json { "device_id": "uuid", "registration_id": "registration-uuid", "auth_url": "https://bookhoard.com/devices/auth/confirm/abc123", "qr_code": "data:image/png;base64,iVBORw0KG...", "expires_in": 300 } ``` ### Check Registration Status ```http POST /api/devices/auth/status Content-Type: application/json { "registration_id": "registration-uuid" } ``` **Response** (200): ```json { "status": "pending|approved|expired", "auth_token": "device-bearer-token...", "device_id": "uuid", "sync_endpoints": { "progress": "https://bookhoard.com/api/sync/progress", "metadata": "https://bookhoard.com/api/sync/metadata", "annotations": "https://bookhoard.com/api/sync/annotations" } } ``` ### List User Devices ```http GET /api/devices Authorization: Bearer ``` **Response** (200): ```json { "devices": [ { "id": "uuid", "device_name": "My Kobo Clara", "device_type": "kobo", "last_sync": "2026-01-31T10:00:00Z", "last_seen": "2026-01-31T10:05:00Z", "sync_enabled": true, "auto_sync": true, "sync_frequency_minutes": 5 } ] } ``` ### Update Device Settings ```http PUT /api/devices/{device_id} Authorization: Bearer Content-Type: application/json { "device_name": "Updated Name", "sync_enabled": true, "auto_sync": true, "sync_frequency_minutes": 5 } ``` ### Revoke Device ```http DELETE /api/devices/{device_id} Authorization: Bearer ``` ## Analytics ### Get Reading Statistics ```http GET /api/analytics/reading-stats?start_date={date}&end_date={date} Authorization: Bearer ``` **Query Parameters**: - `start_date` (optional): Start date (ISO 8601 format) - `end_date` (optional): End date (ISO 8601 format) **Response** (200): ```json { "pages_read": 1250, "books_completed": 5, "reading_time_hours": 42.5, "sessions_count": 28, "average_session_minutes": 91 } ``` ### Get Device Usage Statistics ```http GET /api/analytics/device-usage Authorization: Bearer ``` **Response** (200): ```json { "devices": [ { "device_id": "uuid", "device_name": "My Kobo Clara", "device_type": "kobo", "sync_count": 145, "last_sync": "2026-02-01T10:00:00Z", "pages_synced": 890 } ] } ``` ### Get Popular Books ```http GET /api/analytics/popular-books?limit={limit} Authorization: Bearer ``` **Query Parameters**: - `limit` (optional): Number of results (default: 10) **Response** (200): ```json { "books": [ { "media_item_id": "uuid", "title": "Book Title", "author": "Author Name", "cover_image_path": "/covers/book.jpg", "read_count": 15, "average_rating": 8.5 } ] } ``` ## Book Matching & Linking ### Query Books for Matching ```http POST /api/sync/books/query Authorization: Bearer Content-Type: application/json { "identifiers": ["isbn:978-0345391802", "uuid:abc-123"], "sha256": "a1b2c3d4e5f6abc123...", "title": "The Hobbit", "author": "J.R.R. Tolkien", "file_size": 2456789 } ``` **Response** (200): ```json { "matches": [ { "media_item_id": "uuid-123", "bookhoard_uuid": "uuid-123", "confidence": 1.0, "match_method": "uuid_match" } ], "action": "auto_link" } ``` ### Bulk Link Books ```http POST /api/sync/bulk-link-books Authorization: Bearer Content-Type: application/json { "links": [ { "unlinked_book_id": "uuid-1", "media_item_id": "uuid-2", "confidence_score": 1.0 } ] } ``` **Response** (200): ```json { "results": [ { "unlinked_book_id": "uuid-1", "status": "success", "media_item_id": "uuid-2" } ], "total": 1, "successful": 1, "failed": 0 } ``` ### Auto-Link Books ```http POST /api/sync/auto-link-books Authorization: Bearer Content-Type: application/json { "confidence_threshold": 0.8, "limit": 50 } ``` **Response** (200): ```json { "auto_linked": 15, "results": [ { "unlinked_book_id": "uuid-1", "title": "The Hobbit", "matched_media_item_id": "uuid-2", "confidence": 0.95, "match_method": "sha256_match" } ] } ``` ### Get Unlinked Book Suggestions ```http GET /api/sync/unlinked-books/{id}/suggestions Authorization: Bearer ``` **Response** (200): ```json { "unlinked_book_id": "uuid-1", "title_from_device": "The Hobbit", "sha256": "", "suggestions": [ { "media_item_id": "uuid-2", "bookhoard_uuid": "uuid-2", "confidence": 0.95, "match_method": "sha256_match" } ], "total_suggestions": 1, "action": "auto_link" } ``` ## Collections For complete collection management documentation, see **[COLLECTIONS_API.md](COLLECTIONS_API.md)**. **Quick Reference**: - `GET /api/collections` - List all collections - `POST /api/collections` - Create new collection - `GET /api/collections/{id}` - Get collection details - `PUT /api/collections/{id}` - Update collection - `DELETE /api/collections/{id}` - Delete collection - `POST /api/collections/test-rules` - Test auto-assign rules - `POST /api/collections/bulk-add-books` - Bulk add books to collections - `GET /api/collections/{id}/books` - Get books in collection **Features**: - Auto-assign rules based on genre, author, series, tags, language, publisher, year - Device shelf mappings (Kobo shelves, KOReader categories) - Test rules before applying - Bulk book operations ## OPDS (Open Publication Distribution System) ### Get Device Catalog ```http GET /opds/devices/{deviceId}/catalog?page={page}&per_page={per_page} ``` **Query Parameters**: - `page` (optional): Page number (default: 1) - `per_page` (optional): Items per page (default: 50, max: 200) The feed is paginated via standard OPDS link relations. Clients (e.g. KOReader) walk pages by following the `rel="next"` link until it is absent. OpenSearch paging metadata (`totalResults`, `itemsPerPage`, `startIndex`) is also included. **Response** (200 - OPDS 1.2 XML): ```xml urn:uuid:device-id Bookhoard Library 2026-02-01T12:00:00Z 1814 50 51 urn:uuid:bookhoard-uuid-123 The Hobbit J.R.R. Tolkien 2026-02-01T10:00:00Z uuid-123 abc123... ``` ### Download Book with Format Conversion ```http GET /opds/devices/{deviceId}/download/{bookId}?format={format} ``` **Query Parameters**: - `format` (optional): Book format - `epub` (default), `kepub` **Response** (200): - Headers: - `Content-Type`: `application/epub+zip` or `application/vnd.kobo+xml+zip` - `Content-Disposition`: attachment; filename="The Hobbit.epub" - `X-Bookhoard-UUID`: uuid-123 - `X-Bookhoard-SHA256`: abc123... (original hash) - `X-Bookhoard-KEPUB-SHA256`: xyz789... (KEPUB hash if format=kepub) ### Search OPDS Catalog ```http GET /opds/devices/{deviceId}/search # OpenSearch description GET /opds/devices/{deviceId}/search?q={query} # search results feed ``` When called **without** a `q` parameter, returns an OpenSearch description document (`application/opensearchdescription+xml`). OPDS clients fetch this to learn the search URL template, then substitute `{searchTerms}`: ```xml Bookhoard Search the Bookhoard library UTF-8 UTF-8 ``` When called **with** a `q` parameter, **Response** (200 - OPDS 1.2 XML with search results, including `opensearch:totalResults`). ### List Available Formats ```http GET /opds/devices/{deviceId}/formats/{bookId} ``` **Response** (200): ```json { "media_item_id": "uuid-123", "formats": [ { "format_type": "epub", "file_path": "/path/to/book.epub", "file_sha256": "abc123...", "file_size_bytes": 2456789, "mime_type": "application/epub+zip", "available": true }, { "format_type": "kepub", "file_path": "/cache/book.kepub.epub", "file_sha256": "xyz789...", "file_size_bytes": 2478932, "mime_type": "application/vnd.kobo+xml+zip", "available": true } ] } ``` ## Sync Protocol - KOReader ### KOReader Progress Sync ```http POST /api/sync/koreader/progress Authorization: Bearer Content-Type: application/json { "library_id": "optional-uuid", "books": [ { "uuid": "book-uuid", "title": "Book Title", "authors": ["Author Name"], "progress": 0.45, "percentage": 0.45, "last_read": "2026-01-30T20:00:00Z", "chapter": 3, "epubcfi": "epubcfi(/6/4/2:15)", "character": 15432, "bookmarks": [ { "chapter": 3, "datetime": "2026-01-30T19:55:00Z", "notes": "highlighted text", "pos0": "epubcfi(/6/4/2:15)", "pos1": "epubcfi(/6/4/2:20)", "page": 45, "text": "highlighted text excerpt", "type": "highlight" } ], "highlights": [], "notes": [] } ] } ``` **Response** (202): ```json { "sync_status": "accepted", "books_synced": 1, "conflicts": [ { "book_uuid": "book-uuid", "conflict_type": "progress_mismatch", "device_progress": 0.45, "server_progress": 0.42, "resolution": "device_wins" } ] } ``` ### KOReader Metadata Fetch ```http GET /api/sync/koreader/metadata/{book_uuid} Authorization: Bearer ``` **Response** (200): ```json { "uuid": "book-uuid", "title": "Book Title", "authors": ["Author Name"], "progress": { "percentage": 0.42, "character": 15432, "epubcfi": "epubcfi(/6/4/2:15)", "chapter": 3, "chapter_progress": 0.234 }, "annotations": { "highlights": [...], "notes": [...], "bookmarks": [...] }, "last_sync": "2026-01-30T20:00:00Z" } ``` ## Sync Protocol - Kobo > **Status: Coming Soon** — Native Kobo sync is implemented server-side but not yet supported on real devices. These endpoints are under active development and may change. ### Kobo Markup Sync ```http POST /api/sync/kobo/markup Authorization: Bearer x-kobo-device: {"DeviceId":"device-id","Model":"Kobo Clara"} Content-Type: application/json { "ReadingSync": [ { "ContentId": "book-uuid", "PercentRead": 45.6, "EntitlementId": "entitlement-id", "RemainingTimeMinutes": 120, "LastModified": "2026-01-30T20:00:00Z" } ], "BookmarkSync": [ { "ContentId": "book-uuid", "BookmarkText": "highlighted text", "BookmarkType": "annotation", "BookmarkTitle": "Chapter 3" } ] } ``` **Response** (200): ```json { "Status": "Success", "MarkupsSynced": 5, "BookmarksSynced": 3 } ``` ### Kobo Library Fetch ```http GET /api/sync/kobo/library Authorization: Bearer ``` **Response** (200): ```json { "library_sync": [ { "ContentId": "book-uuid", "ContentType": "6", "Title": "Book Title", "Author": "Author Name", "PercentRead": 42.3, "PagesRemaining": 115, "BookmarkCount": 3, "LastModified": "2026-01-30T20:00:00Z" } ] } ``` ## Universal Progress ### Get Universal Progress ```http GET /api/progress/{book_uuid} Authorization: Bearer ``` **Response** (200): ```json { "book_id": "book-uuid", "format_group": "reflowable", "universal_progress": 0.45678, "location_references": { "percentage": 0.45678, "epubcfi": "epubcfi(/6/4/2:15)", "character": 15432, "chapter": 3, "chapter_progress": 0.234, "viewport_y": 0.12 }, "device_progress": { "koreader": { "percentage": 0.45678, "last_sync": "2026-01-30T20:00:00Z" }, "kobo": { "percentage": 45.6, "last_sync": "2026-01-30T19:55:00Z" }, "web": { "display_page": 89, "total_pages": 200, "last_sync": "2026-01-30T20:05:00Z" } }, "annotations": { "highlights": [...], "notes": [...], "bookmarks": [...] }, "conflicts": [ { "id": "conflict-uuid", "type": "progress", "resolved": false, "sources": ["koreader", "kobo"] } ] } ``` ### Update Universal Progress ```http POST /api/progress/{book_uuid} Authorization: Bearer Content-Type: application/json { "source": "web|koreader|kobo|mobile", "location": { "percentage": 0.45678, "epubcfi": "epubcfi(/6/4/2:15)", "character": 15432, "chapter": 3, "page": 89, "total_pages": 200 }, "device_metadata": { "device_type": "web", "user_agent": "..." } } ``` ## Conflicts ### List Conflicts ```http GET /api/conflicts?status=unresolved&type=progress Authorization: Bearer ``` **Query Parameters**: - `status`: "unresolved|all" (default: "unresolved") - `type`: "progress|note|highlight|all" (default: "all") **Response** (200): ```json { "conflicts": [ { "id": "conflict-uuid", "media_item_id": "book-uuid", "media_item_title": "Book Title", "conflict_type": "progress", "conflict_data": { "koreader": { "source": "koreader", "timestamp": "2026-01-30T20:10:00Z", "data": { "percentage": 0.45, "epubcfi": "epubcfi(/6/4/2:15)", "character": 15432 } }, "kobo": { "source": "kobo", "timestamp": "2026-01-30T20:05:00Z", "data": { "percentage": 0.42 } } }, "resolution_status": "unresolved", "created_at": "2026-01-30T20:10:05Z" } ], "total": 1, "unresolved": 1 } ``` ### Get Conflict Details ```http GET /api/conflicts/{conflict_id} Authorization: Bearer ``` ### Resolve Conflict ```http POST /api/conflicts/{conflict_id}/resolve Authorization: Bearer Content-Type: application/json { "winner": "koreader|kobo|web|manual", "manual_data": { "percentage": 0.43, "epubcfi": "epubcfi(/6/4/2:20)", "character": 15500 }, "apply_to_all_future_conflicts": false, "reason": "user chose more recent progress" } ``` **Response** (200): ```json { "conflict_resolved": true, "applied_to": { "progress": true, "annotations": false }, "devices_synced": ["device-1", "device-2"] } ``` ### Delete Conflict ```http DELETE /api/conflicts/{conflict_id} Authorization: Bearer ``` ### Dismiss All Resolved ```http DELETE /api/conflicts/dismiss-resolved Authorization: Bearer ``` ## Sync Queue ### List Queue Items ```http GET /api/queue/items?limit=50&offset=0 Authorization: Bearer ``` **Response** (200): ```json { "items": [ { "id": "uuid", "device_id": "device-uuid", "device_name": "My Kobo", "media_item_id": "book-uuid", "sync_type": "progress", "sync_data": {}, "priority": 5, "attempts": 0, "max_attempts": 3, "status": "pending", "error_message": null, "created_at": "2026-01-31T10:00:00Z" } ], "total": 100 } ``` ### Process Queue Item ```http POST /api/queue/items/{queue_item_id}/process Authorization: Bearer ``` ### Retry Queue Item ```http POST /api/queue/items/{queue_item_id}/retry Authorization: Bearer ``` ### Delete Queue Item ```http DELETE /api/queue/items/{queue_item_id} Authorization: Bearer ``` ### Clear Queue ```http DELETE /api/queue/clear Authorization: Bearer ``` ### Clear Failed Items ```http DELETE /api/queue/clear-failed Authorization: Bearer ``` ### Get Queue Stats ```http GET /api/queue/stats Authorization: Bearer ``` **Response** (200): ```json { "pending": 15, "processing": 2, "failed": 3, "completed": 100, "total": 120 } ``` ## WebSocket ### Connect to WebSocket ``` WS /ws/sync?token= ``` ### Message Format **Client → Server (Heartbeat)**: ```json { "type": "ping" } ``` **Server → Client (Progress Update)**: ```json { "type": "progress_update", "timestamp": "2026-01-31T10:00:00Z", "data": { "book_id": "uuid", "progress": { "percentage": 0.45678, "epubcfi": "epubcfi(/6/4/2:15)", "chapter": 3 }, "annotations": {} }, "source_device": { "id": "device-uuid", "name": "My Kobo", "type": "kobo" } } ``` **Server → Client (Conflict Detected)**: ```json { "type": "conflict", "timestamp": "2026-01-31T10:00:00Z", "data": { "book_id": "uuid", "conflict_id": "uuid", "conflict_type": "progress" } } ``` **Server → Client (Pong)**: ```json { "type": "pong" } ``` ## Error Responses All endpoints return standardized error responses: ```json { "error": "Error message", "message": "Detailed error information (if available)", "code": "ERROR_CODE" } ``` ### HTTP Status Codes - **200**: OK - Request successful - **201**: Created - Resource created successfully - **204**: No Content - Successful deletion or update with no content - **400**: Bad Request - Invalid request parameters - **401**: Unauthorized - Missing or invalid authentication - **403**: Forbidden - Insufficient permissions - **404**: Not Found - Resource does not exist - **409**: Conflict - Resource conflict (e.g., duplicate) - **422**: Unprocessable Entity - Validation error - **429**: Too Many Requests - Rate limit exceeded - **500**: Internal Server Error - Server error ### Rate Limiting **Per-Device Limits**: - Sync requests: 60/minute - Progress updates: 120/minute - Metadata requests: 30/minute **Per-User Limits**: - All requests: 300/minute - Conflict resolutions: 10/minute - Device registrations: 5/hour **Rate Limit Headers**: ``` X-RateLimit-Limit: 60 X-RateLimit-Remaining: 45 X-RateLimit-Reset: 60 ``` ## Bruno v3.0 Collections Complete API test collections are available in the `bruno/` directory: ``` bruno/ ├── user/ # Authentication & profiles ├── admin/ # Admin operations ├── library/ # Library management ├── media-items/ # Media content ├── progress/ # Reading progress ├── notes/ # Notes API ├── highlights/ # Highlights API ├── ratings/ # Ratings API ├── devices/ # Device management ├── sync-koreader/ # KOReader sync protocol ├── sync-kobo/ # Kobo sync protocol ├── conflicts/ # Conflict resolution ├── queue/ # Sync queue management └── collection.yml # Main collection file ``` ## Testing with Bruno OpenCollection YAML Install Bruno CLI: ```bash npm install -g @usebruno/cli ``` Run all tests: ```bash bruno run ``` Run specific collection: ```bash bruno run bruno/devices/ ``` ## Additional Resources - [README.md](README.md) - Getting started guide - [UNIVERSAL_SYNC_IMPLEMENTATION_GUIDE.md](UNIVERSAL_SYNC_IMPLEMENTATION_GUIDE.md) - Sync architecture - [KOBOREADER_SETUP.md](KOBOREADER_SETUP.md) - KOReader device setup - [KOBO_SETUP.md](KOBO_SETUP.md) - Kobo device setup --- **Document Version**: 1.0 **Last Updated**: 2026-01-31 **API Version**: v1.0