04 November 2026

This describes a potential Kobo integration that is generic with respect to book logging services; the burden of integration and support is on the book logging service, not Rakuten.

Service description and Kobo UI

The general design is that the Kobo device itself pushes some Kobo book state to a properly configured receiver when the Kobo syncs.

The set-up on the device would look something like:

  • A text entry field for a site that can handle the POST.
  • An on/off button for that site (so the end-user can disable it without deleting it).
  • An authentication token text entry field for that site.
  • Exclude books unread in the past [1,2,3,4,5,6,7,8] weeks from the update.
  • A button to add another site to which to push data.

Examples of the UI on the Kobo device are shown in Figure 1 and Figure 2.

kobo-device-booktracking-ui.png

Figure 1: An example UI for the configuration on the Kobo device; it is up to the receiver to inform the user of the correct values and otherwise provide instructions for configuration and expectations

kobo-device-booktracking-ui-two-configs.png

Figure 2: An example of UI showing two configurations

The Kobo device would issue an https POST to the site(s) configured and deliver one body that contains all of the information it can send. This may be formatted as JSON, CBOR, Protobuf, or whatever Rakuten implements. Examples of JSON bodies and OpenAPI specifications are below.

This would allow any book reading app (Goodreads, StoryGraph, Bookly, Fable, Bookbeee, etc.) to implement an endpoint for their Kobo users. Their uses will need to configure it. The endpoint will accept the data and do what they want with it. This is a POST-only, outbound-only activity from the device that doesn't involve the Kobo servers at all. In fact, the device can ignore status codes from the receiving server entirely, if it wants (alternatively, it could return a short error code that would be displayed by the Kobo device; this is shown in the wire-frame diagrams).

This will send side-loaded (Dropbox/GDrive) book data, although the data for those books may be incomplete, and the receiver will have to decide what to do with that information.

NB: I don't know what data the Kobo device has that might be useful to the book tracking service. I may have over-estimated what the Kobo device can send, or left out useful information I didn't know the Kobo device had.

In summary:

  • the data comes directly from the Kobo Ereader device and it sent to the receiving service
  • the receiving service is responsible for accepting the data sent from the Kobo Ereader device
  • the receiving service is responsible for documenting the configuration for their end users
  • Rakuten is responsible for publishing the specification for each version so those implementing a receiving service can continue to accept data from Kobo Ereader devices.

Example JSON body

An example POST'd JSON body might be:

{
  "header": {
    "deviceId": "asdfsadf",
    "apiVersion": "1.0.0"
  },
  "books": [
    {
      "title": "asdfasdf",
      "author": "Asdf Gfasdgf",
      "isbn": "123sdf-sadf234-sadf-234",
      "publication": {
        "publisher": "asdfsadf",
        "date": "1976-07-04",
        "fileFormat": "PDF",
        "coverArt": "base-64-encoded-png-or-jpg==",
        "numPages": 123
      },
      "reading": {
        "pagesLastRead": 4,
        "dateLastRead": "2026-03-04"
      }
    }
  ]
}

Example OpenAPI specification

An OpenAPI specification for a server that wants to receive the data (and for the device sending the data) might look like:

openapi: 3.0.3
info:
  title: Kobo POST body
  version: 1.0.0
  description: >-
    The OpenAPI spec for receiving this version of a POST
    request
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://www.example.com/api
security:
  - bearerAuth: []

paths:
  /kobo-reading-data:
    post:
      summary: Accept Kobo reading data
      operationId: acceptKoboReadingData
      requestBody:
        description: >-
          The endpoint that accepts requests from the Kobo
          device configured to send data
        content:
          application/json:
            schema:
              type: object
              properties:
                header:
                  $ref: "#/components/schemas/header"
                books:
                  $ref: "#/components/schemas/books"
      responses:
        "200":
          description: Reading data accepted
        "404":
          description: Not found
        "429":
          description: Too many requests
        "400":
          description: Bad request
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    header:
      type: object
      required:
        - version
      properties:
        version:
          type: string
          example: "1.0.0"
        deviceId:
          type: string
          example: "4b2e45a9-36c0-4dcb-b484-9d251ff6abc6"
    publish:
      type: object
      properties:
        publisher:
          type: string
          example: "Simon and Schuster"
          description: The name of the publisher
        date:
          type: string
          example: "1976-07-04"
          description: The publication date in YYYY-MM-DD format
        fileFormat:
          type: string
          enum:
            - EPUB
            - EPUB3
            - KEPUB
            - PDF
            - MOBI
            - TXT
            - HTML
            - RTF
            - CBZ
            - CBR
            - UNKNOWN
          description: >-
            The format of the document on the Kobo, falling back to
            `UNKNOWN` if the format is not one of the others. This can be
            updated, but will also cause a version bump in the API version in
            the POST, so you can either deal with this as an enum of known
            values, or reject updates from later API versions until you are able
            to update the receiver to handle the new types
        numPages:
          type: integer
          example: 426
          description: The number of pages in the book
        coverArt:
          type: string
          description: a base64-encoded JPEG or PNG file
    reading:
      type: object
      properties:
        pagesLastRead:
          type: integer
          example: 4
          description: >-
            The number of pages read in the last "session" or since
            the last update or whatever the Kobo device wants to put here
        lastReadDate:
          type: string
          example: "2026-03-04"
          description: The date this book was last read in YYYY-MM-DD format
    book:
      type: object
      properties:
        title:
          type: string
          example: Meditations
        author:
          type: string
          example: Marcus Aurelius
        isbn:
          type: string
          example: "9780812968255"
        publication:
          $ref: "#/components/schemas/publish"
        reading:
          $ref: "#/components/schemas/reading"
    books:
      type: array
      items:
        $ref: "#/components/schemas/book"