Expose server bookmarks via OPDS

Mark Winckle 3 years ago updated by DarkShadowSwE 2 years ago 14

Can you expose server side bookmarks via OPDS so third party applications can pick up from where you left on the web viewer?

Seth Chim could use this in his android application for example.


As far as I know, there is no concept of bookmark in the OPDS specifications.

But nothing prevents third party apps from using the bookmarking REST API added in 2.0.

I don't have enough time to write a proper doc, but it's really easy to use. For instance, the URL for comics bookmarks is:


To create, read or delete a bookmark, you just have to replace <COMIC_ID> with the actual id of the comic (already provided in the OPDS feed) and call the URL with the appropriate HTTP verb (i.e. PUT, GET or DELETE).

A comic bookmark is simply the page number as the body of the request.

For books, the URL is the same, with "isBook" set to true instead of false.

A book bookmark is the chapter number (as defined in the spine of the epub file) and the percentage of progression in the current chapter, separated by a #, e.g:


This is great, thanks. I have tested this and it works just fine in Java, but only on an unauthenticated server. I have included the basic auth headers as usual but it does not work. Is the api incomplete or am I missing something?


The API is behind the same authentication layer as all the other pages served by Ubooquity, which is not basic auth. Hence the issue.

I added basic auth support when I implemented the OPDS feed, because the OPDS specification requires it. But for everything else, authentication is done the following way:

  1. Ubooquity serves a login pages, wich contains a server "salt" and the server current time
  2. The user enters his crendentials
  3. The browser sends the login and the hashed password (generated with the user's password, the server salt and the server time) to the server
  4. The server returns the home page, with a cookie containg a user token
  5. The token is included by the browser with each subsequent request

This way, the password is never sent in plain text over the network. (the token can still be intercepted and access gained, but it's still better than exposing the paswword)

I prefer to restric basic auth to the OPDS feed, because it's really unsafe when you don't use HTTPS.

So I'll try to quickly add an authentication API that can be used by external clients.

It will require some hashing work on client side to protect the password the way I described earlier, but I can provide code samples for that.

Thanks, really looking forward to it! I finished the implementation without authentication and it works really great. The system uses local bookmarks first and then attempts of load remote bookmarks. If a remote bookmark is found, it takes preference and also updates the local one. This is so it can be compatible with below Ubooquity 2.0. It's really nice testing with multiple devices and seeing the saved progress.

Thanks again!


Just a heads-up, I'm going to change the bookmark API in the next version.

I'm going to use Json structures instead of plain text (with additional data: a "finished" flag and a "last update timestamp").

I prefer to do it right now an try to keep the API stable afterwards.


I've got the authentication and user api working, one thing I've noticed is that there are no timestamps for the bookmarks. It might be a good idea to include those so that clashes can be handled safely. Obviously not an urgent request.

Some notes for anyone else that wants to implement the api:

POST data required "servertime=<server time>&login=<username>&hash=<hashed password>"

The HMACSHA256 algorithm is performed twice, the first time the key is the password and the message is the serversalt,

the second time the key is the first hash and the message is the servertime.

@Tom @Gauntlet

Can you help me out, I've been trying to get this working with no luck. Here are my logs:

9831-9879 D/OkHttp: --> PUT http/1.1
9831-9879 D/OkHttp: Content-Type: application/json; charset=utf-8
9831-9879 D/OkHttp: Content-Length: 2
9831-9879 D/OkHttp: Authorization: Basic c2V0aDphYmNk
9831-9879 D/OkHttp: --> END PUT
9831-9879 D/OkHttp: <-- 403 You must be authenticated to access this resource (9ms)
9831-9879 D/OkHttp: Date: Fri, 30 Jun 2017 01:59:38 GMT
9831-9879 D/OkHttp: Content-Length: 0
9831-9879 D/OkHttp: Server: Jetty(9.4.0.v20161208)
9831-9879 D/OkHttp: <-- END HTTP

Note: Only fails when require authentication. POST does not work, but PUT works when authentication is not required.

I've spent a few weeks tinkering and still have no progress. I don't see any message about server time or server salt. There is no redirect or even a response body, notice Content-Length:0. How do I authenticate? Any help would be appreciated!

@Seth So I don't have much experience with Java/Android but since C# is similar here is my GetToken method. I'm just guessing, but I think you aren't retrieving the login page from web interface? The serversalt and servertime are included in the login form.

Hope that helps, I'll keep an eye on the thread if you have any more questions.

Edit: in the snippet I included Client is C# System.Net.Http.HttpClient, it handles web requests, manages basic auth credentials and cookies.


First line helped me. I was looking for response from bookmark api which is incorrect. This helps a lot, thanks for sharing.

Thank you for your snippet and help. I have successfully got it working thanks to you. 

The bookmark api has changed since 2.1.0 update. You just need to enter parameters. Here is what the json looks like:

  "docId" : 857,
  "isBook" : false,
  "mark" : "9",
  "isFinished" : false,
  "lastUpdate" : 1499912783755

Thanks again, time to finally move on to more exciting problems.

Glad to have helped. I use your (fantastic) app so I eventually get to benefit too :D.

Sorry for my lack of answer, I have a hard time reading and answering all posts on the forum.

In any case, the bookmark API will not be changed in the foreseeable future.

It might be a good idea to include those so that clashes can be handled safely.

Good idea.

I could even use this timestamp to display the "recently read" books.

I have tried the "/user-api/bookmark" some and it only accepts a valid "docId" and only return that single array (as it should) like Seths example above.

But could it be possible to add something like /user-api/bookmarks" (an "S" at the end) to return an json array of all the bookmarks the user have?

Then maby Seth could use that for "Kuboo" to implement cross device bookmark sync maby?

Then maby it could be used to keep track of bookmarks and positions on the web reader that come with Ubooquity.

And it could be used for solving some of this: https://ubooquity.userecho.com/communities/1/topics/581-user-data-stored-on-server-recent-files-last-reading-position-readunread-favorites-tags