Skip to content

Latest commit

 

History

History
821 lines (619 loc) · 23.7 KB

File metadata and controls

821 lines (619 loc) · 23.7 KB

KoShelf API Reference

All endpoints are served under /api/ when running in web server mode.

Response Envelope

Every successful response is wrapped in an ApiResponse<T> envelope:

{
  "data": { ... },
  "meta": {
    "version": "0.1.0",
    "generated_at": "2026-03-14T12:00:00Z"
  }
}

For readability, endpoint examples below show the inner data payload unless stated otherwise.

Error responses use a separate shape:

{
  "error": {
    "code": "invalid_query",
    "message": "Human-readable description"
  }
}

Error Codes

Code HTTP Status Description
invalid_query 400 Invalid or missing query parameter
invalid_week_key 400 Week key is not a valid Monday date
invalid_month_key 400 Month key is not in YYYY-MM format
invalid_year 400 Year is not a valid 4-digit number
invalid_credentials 400 or 401 Invalid login password or current password
password_too_short 400 New password does not meet minimum length
unauthorized 401 Missing, invalid, or expired session
rate_limited 429 Too many authentication attempts
conflict 409 Metadata file was modified externally; re-fetch and retry
not_found 404 Requested resource does not exist
internal_server_error 500 Server-side error

Common Parameters

Several parameters are shared across multiple endpoints:

scope

Filters results by content type.

Value Description
all All content types (default)
books Books only
comics Comics only

from / to

Date range filter. Both must be provided together or both omitted. Format: YYYY-MM-DD. to must be >= from.

tz

IANA timezone name (e.g. America/New_York, Europe/Berlin). Controls how date boundaries and period calculations are applied. Defaults to UTC if omitted.


Endpoints

GET /api/site

Returns site metadata and capabilities.

Parameters: None

Response:

{
  "title": "KoShelf",
  "language": "en_US",
  "capabilities": {
    "has_books": true,
    "has_comics": false,
    "has_reading_data": true,
    "has_files": false,
    "auth_enabled": true,
    "has_writeback": false
  }
}

Capability flags:

Field Type Description
has_books boolean At least one book is available
has_comics boolean At least one comic is available
has_reading_data boolean Reading statistics data is available
has_files boolean Original item files are available at /assets/files/{id}.{format}
auth_enabled boolean Serve-mode authentication is enabled
has_writeback boolean Metadata writeback is enabled (--enable-writeback)

GET /api/items

Returns all library items with optional filtering and sorting.

Parameters:

Name Type Required Default Description
scope string No all Content type filter: all, books, comics
sort string No title Sort field (see below)
order string No varies asc or desc (default depends on sort field)

Sort fields and default order:

Sort Value Default Order Description
title asc Alphabetical by title
author asc Alphabetical by first author
status asc By reading status
progress desc By progress percentage
rating desc By user rating
annotations desc By annotation count
last_open_at desc By last opened date

Response:

{
  "items": [
    {
      "id": "abc123",
      "title": "Example Book",
      "authors": ["Author Name"],
      "series": { "name": "Series Name", "index": "1" },
      "status": "reading",
      "progress_percentage": 45.2,
      "rating": 4,
      "annotation_count": 12,
      "cover_url": "/assets/covers/abc123.webp",
      "content_type": "book"
    }
  ]
}
Field Type Description
id string Unique item identifier
title string Item title
authors string[] List of authors
series object? Series info with name (string) and optional index (string)
status string One of: reading, complete, abandoned, unknown
progress_percentage number? Reading progress 0–100
rating number? User rating (typically 0–5)
annotation_count number Number of annotations
cover_url string Path to cover image
content_type string book or comic

GET /api/items/{id}

Returns detailed information for a single library item.

Path Parameters:

Name Type Required Description
id string Yes Library item ID

Query Parameters:

Name Type Required Default Description
include string No (none) Comma-separated list of extra data to include

Include tokens:

Token Description
highlights Include highlight annotations
bookmarks Include bookmark annotations
statistics Include reading statistics
completions Include completion history
reader_presentation Include KOReader presentation settings (font, margins, etc.)
chapters Include table of contents entries
all Include everything (supersedes other tokens)

Unknown tokens return a 400 error. Duplicates and extra whitespace are ignored.

Response:

The item object extends the list item with additional fields:

Field Type Description
language string? Content language
publisher string? Publisher name
description string? Book description (sanitized HTML)
review_note string? User's review / summary note
format string Source file format (e.g. epub, cbz)
pages number? Total page count
search_base_path string Base path for external search links
subjects string[] Genres / subjects
identifiers object[] External identifiers (see below)
has_metadata boolean Whether a KOReader sidecar file exists for this item

Each identifier:

Field Type Description
scheme string Identifier type (e.g. isbn, asin)
value string The identifier value
display_scheme string Human-readable scheme name
url string? Link to external catalog

Optional includes:

highlights / bookmarks — arrays of annotations:

Field Type Description
id string Annotation identifier
chapter string? Chapter title
datetime string? Creation timestamp
datetime_updated string? Last modification timestamp
pageno number? Page number
text string? Highlighted text
note string? User note
pos0 string? Start position (CFI or XPointer)
pos1 string? End position (CFI or XPointer)
color string? Highlight color name
drawer string? Highlight style (e.g. lighten, underscore, strikeout, invert)

statistics:

Field Type Description
item_stats.notes number? Note count
item_stats.last_open_at string? ISO 8601 timestamp
item_stats.highlights number? Highlight count
item_stats.pages number? Page count
item_stats.total_reading_time_sec number? Total reading time in seconds
session_stats.session_count number Number of reading sessions
session_stats.average_session_duration_sec number? Average session length
session_stats.longest_session_duration_sec number? Longest session length
session_stats.last_read_date string? ISO 8601 date
session_stats.reading_speed number? Pages per hour

reader_presentation:

Field Type Description
font_face string? Font name
font_size_pt number? Font size in points
line_spacing_percentage number? Line spacing percentage
horizontal_margins number[]? Left and right margins [left, right]
top_margin number? Top margin
bottom_margin number? Bottom margin
embedded_fonts boolean? Whether embedded fonts are used
hyphenation boolean? Whether hyphenation is enabled
floating_punctuation boolean? Whether floating punctuation is enabled
word_spacing number[]? Word spacing range [min, max]

completions:

Field Type Description
entries object[] Completion records
entries[].start_date string ISO 8601 date
entries[].end_date string ISO 8601 date
entries[].reading_time_sec number Time spent reading
entries[].session_count number Number of sessions
entries[].pages_read number Pages read
total_completions number Total times completed
last_completion_date string? ISO 8601 date

chapters:

Field Type Description
title string Chapter title
position number Fractional position within the book (0.0–1.0)

Status Codes: 200, 400 (invalid include), 404 (item not found)


PATCH /api/items/{id}

Update item metadata. Only available when writeback is enabled (--enable-writeback).

Path Parameters:

Name Type Required Description
id string Yes Library item ID

Request Body:

All fields use three-state patch semantics: omit the field to leave it unchanged, set it to a value to update, or set it to null to clear.

{
  "review_note": "A thoughtful exploration of ...",
  "rating": 4,
  "status": "complete"
}
Field Type Description
review_note string? User review / summary note. null clears it
rating number? Rating 0–5. 0 or null clears the rating
status string? One of: reading, complete, abandoned

Response: 204 No Content

Status Codes: 204, 400 (invalid rating or status), 404 (item not found or writeback not enabled), 409 (metadata file modified externally — re-fetch and retry)


PATCH /api/items/{id}/annotations/{annotation_id}

Update an annotation's note, color, or highlight style. Only available when writeback is enabled (--enable-writeback).

Path Parameters:

Name Type Required Description
id string Yes Library item ID
annotation_id string Yes Annotation identifier

Request Body:

The note field uses three-state patch semantics (omit / value / null to clear). color and drawer are plain optional fields (omit to leave unchanged).

{
  "note": "Updated note text",
  "color": "blue",
  "drawer": "lighten"
}
Field Type Description
note string? Note text. null clears the note
color string? One of: red, orange, yellow, green, olive, cyan, blue, purple, gray
drawer string? One of: lighten, underscore, strikeout, invert

Response: 204 No Content

Status Codes: 204, 400 (invalid color or drawer), 404 (annotation or item not found, or writeback not enabled), 409 (metadata file modified externally — re-fetch and retry)


DELETE /api/items/{id}/annotations/{annotation_id}

Delete an annotation. Only available when writeback is enabled (--enable-writeback).

Path Parameters:

Name Type Required Description
id string Yes Library item ID
annotation_id string Yes Annotation identifier

Response: 204 No Content

Status Codes: 204, 404 (annotation or item not found, or writeback not enabled), 409 (metadata file modified externally — re-fetch and retry)


GET /api/items/{id}/page-activity

Returns page-level reading heatmap data for a single library item.

Path Parameters:

Name Type Required Description
id string Yes Library item ID

Query Parameters:

Name Type Required Default Description
completion string No all Filter by completion. all aggregates all readings; 0, 1, etc. filter by completion index

Response:

Field Type Description
total_pages number Total page count of the book
pages object[] Per-page aggregated reading data
pages[].page number Page number
pages[].total_duration number Total reading time on this page (seconds)
pages[].read_count number Number of times this page was visited
annotations object[] Annotations with page numbers
annotations[].page number Page number
annotations[].kind string One of: highlight, bookmark, note

Status Codes: 200, 400 (invalid completion param), 404 (item not found or no reading data)


GET /api/reading/summary

Returns aggregate reading statistics for a time period.

Parameters:

Name Type Required Default Description
scope string No all Content type filter
from string No Start date (YYYY-MM-DD), requires to
to string No End date (YYYY-MM-DD), requires from
tz string No UTC IANA timezone

Response:

{
  "range": {
    "from": "2025-01-01T00:00:00Z",
    "to": "2025-12-31T23:59:59Z",
    "tz": "UTC"
  },
  "overview": {
    "reading_time_sec": 360000,
    "pages_read": 5000,
    "sessions": 200,
    "completions": 15,
    "items_completed": 12,
    "longest_reading_time_in_day_sec": 7200,
    "most_pages_in_day": 120,
    "average_session_duration_sec": 1800,
    "longest_session_duration_sec": 5400
  },
  "streaks": {
    "current": { "days": 5, "start_date": "2025-12-27", "end_date": "2025-12-31" },
    "longest": { "days": 30, "start_date": "2025-03-01", "end_date": "2025-03-30" }
  },
  "heatmap_config": {
    "max_scale_sec": 7200
  }
}

GET /api/reading/metrics

Returns time-series reading data points grouped by period.

Parameters:

Name Type Required Default Description
metric string Yes Comma-separated metric names (see below)
group_by string Yes Grouping period (see below)
scope string No all Content type filter
from string No Start date (YYYY-MM-DD), requires to
to string No End date (YYYY-MM-DD), requires from
tz string No UTC IANA timezone

Available metrics:

Metric Description
reading_time_sec Total reading time in seconds
pages_read Total pages read
sessions Number of reading sessions
completions Number of books completed
average_session_duration_sec Average session length
longest_session_duration_sec Longest session length

Group-by values:

Value Key Format Description
total all-time Single aggregate point
day YYYY-MM-DD Daily breakdown
week YYYY-MM-DD Weekly (key = Monday date)
month YYYY-MM Monthly breakdown
year YYYY Yearly breakdown

Response:

{
  "metrics": ["reading_time_sec", "pages_read"],
  "group_by": "day",
  "scope": "all",
  "points": [
    { "key": "2025-12-01", "reading_time_sec": 3600, "pages_read": 50 },
    { "key": "2025-12-02", "reading_time_sec": 1800, "pages_read": 25 }
  ]
}

Metric values are flattened directly into each point object.


GET /api/reading/available-periods

Returns available time periods that have reading data, useful for building period selectors.

Parameters:

Name Type Required Default Description
source string Yes Data source: reading_data or completions
group_by string Yes Grouping: week, month, or year
scope string No all Content type filter
from string No Start date (YYYY-MM-DD), requires to
to string No End date (YYYY-MM-DD), requires from
tz string No UTC IANA timezone

Source/group_by constraints:

  • source=completions does not support group_by=week
  • All other combinations are valid

Response:

{
  "source": "reading_data",
  "group_by": "month",
  "periods": [
    {
      "key": "2025-12",
      "start_date": "2025-12-01T00:00:00Z",
      "end_date": "2025-12-31T23:59:59Z",
      "reading_time_sec": 36000,
      "pages_read": 500
    }
  ],
  "latest_key": "2025-12"
}

When source=completions, periods contain completions (number) instead of reading_time_sec / pages_read.


GET /api/reading/calendar

Returns reading events and statistics for a calendar month.

Parameters:

Name Type Required Default Description
month string Yes Month in YYYY-MM format
scope string No all Content type filter
tz string No UTC IANA timezone

Response:

{
  "month": "2025-12",
  "events": [
    {
      "item_ref": "ref-1",
      "start": "2025-12-01T20:00:00Z",
      "end": "2025-12-01T21:00:00Z",
      "reading_time_sec": 3600,
      "pages_read": 45
    }
  ],
  "items": {
    "ref-1": {
      "title": "Example Book",
      "authors": ["Author Name"],
      "content_type": "book",
      "item_id": "abc123",
      "item_cover": "/assets/covers/abc123.webp"
    }
  },
  "stats_by_scope": {
    "all": { "items_read": 3, "pages_read": 500, "reading_time_sec": 36000, "active_days_percentage": 65.0 },
    "books": { "items_read": 2, "pages_read": 400, "reading_time_sec": 30000, "active_days_percentage": 55.0 },
    "comics": { "items_read": 1, "pages_read": 100, "reading_time_sec": 6000, "active_days_percentage": 20.0 }
  }
}

The items map is keyed by item_ref values used in the events array. item_id and item_cover are present only if the item exists in the library catalog.


GET /api/reading/completions

Returns books completed in a time period with detailed per-item statistics.

Parameters:

Name Type Required Default Description
scope string No all Content type filter
year string No latest 4-digit year (mutually exclusive with from/to)
from string No Start date (YYYY-MM-DD), mutually exclusive with year
to string No End date (YYYY-MM-DD), mutually exclusive with year
group_by string No month Grouping: month or none
include string No (none) Comma-separated: summary, share_assets
tz string No UTC IANA timezone

If neither year nor from/to is provided, defaults to the latest year with completions.

Response (group_by=month):

{
  "groups": [
    {
      "key": "2025-03",
      "items_finished": 2,
      "reading_time_sec": 72000,
      "items": [ ... ]
    }
  ]
}

Response (group_by=none):

{
  "items": [ ... ]
}

Each completion item:

Field Type Description
title string Item title
authors string[] Authors
start_date string ISO 8601 date
end_date string ISO 8601 date
reading_time_sec number Total reading time
session_count number Number of sessions
pages_read number Pages read
calendar_length_days number? Days between start and end
average_speed number? Pages per hour
average_session_duration_sec number? Average session length
rating number? User rating
review_note string? User review
series string? Series name
item_id string? Library item ID (if in catalog)
item_cover string? Cover URL (if in catalog)
content_type string? book or comic

Optional include: summary

Field Type Description
total_items number Items completed
total_reading_time_sec number Total reading time
longest_session_duration_sec number Longest single session
average_session_duration_sec number Average session length
active_days number Days with reading activity
active_days_percentage number Percentage of days active (0–100)
longest_streak_days number Longest consecutive reading streak
best_month string? Month with most completions (YYYY-MM)

Optional include: share_assets

Field Type Description
story_url string URL to story-format share image
square_url string URL to square-format share image
banner_url string URL to banner-format share image

POST /api/auth/login

Auth endpoints are available only when authentication is enabled in serve mode (--enable-auth).

Authenticate and receive a session cookie. Not protected by auth middleware.

Request Body:

{
  "password": "string"
}

Response:

{
  "ok": true
}

Sets a koshelf_session cookie (HttpOnly, SameSite=Strict, 30-day TTL). The Secure flag is set when served over HTTPS.

Status Codes: 200, 401 (wrong password), 404 (auth not configured), 429 (rate limited — includes Retry-After header)


POST /api/auth/logout

End the current session and clear the session cookie. Requires authentication.

Request Body: None

Response:

{
  "ok": true
}

Status Codes: 200, 401 (not authenticated), 404 (auth not configured)


PUT /api/auth/password

Change the authentication password. Requires authentication. Rate limited.

Request Body:

{
  "current_password": "string",
  "new_password": "string"
}

new_password must be 8–1024 characters.

Response:

{
  "ok": true
}

Revokes all other sessions on success — only the current session is preserved.

Status Codes: 200, 400 (wrong current password or invalid new password), 401, 429 (rate limited — includes Retry-After header)


GET /api/auth/sessions

List all active (non-expired) sessions. Requires authentication.

Response:

[
  {
    "id": "uuid-v7",
    "user_agent": "Mozilla/5.0 ...",
    "browser": "Firefox 128",
    "os": "Linux",
    "last_seen_ip": "192.168.1.1",
    "created_at": "2026-03-01T10:00:00Z",
    "last_seen_at": "2026-03-18T14:30:00Z",
    "is_current": true
  }
]

Ordered by last_seen_at descending. browser and os are parsed from the user agent string.

Status Codes: 200, 401, 404 (auth not configured)


DELETE /api/auth/sessions/{session_id}

Revoke a specific session. Cannot revoke the current session (use logout instead). Requires authentication.

Response:

{
  "ok": true
}

Status Codes: 200, 400 (cannot revoke current session), 401, 404 (session not found or auth not configured)


GET /api/events/stream

Server-Sent Events (SSE) stream for real-time data change notifications.

Parameters: None

Behavior:

  • Content-Type: text/event-stream
  • Event type: data_changed
  • Keep-alive sent every 15 seconds
  • Clients should reconnect on disconnect and reload relevant data when a data_changed event is received