reMarkable File Sync API

Overview

reMarkable File Sync API

Goal of this project is to figure out and document the API which is used by the reMarkable Paper Tablet for syncing documents between the device, the desktop and mobile clients and the cloud service. A sample client implementation in PHP is also part of the project.

The API allows you to exchange files with your reMarkable tablet without the need to be in the same network or have physical access to the device. This makes it possible to create your own cloud services. Eg. tools that periodically sync files to other services like Dropbox or you could add automatic export to reMarkable in your tools.

API Documentation

I did my best to document what I cold figure out about the reMarkable File Sync API in the wiki: API Documentation

It should give you a good starting point when implementing your own client. You can also run the command line client in this repository (see below) with the --loglevel debug option to see the API calls in action.

Please feel free to extend and improve the documentation.

PHP API Client Library

This repository implements a PHP client to talk to the reMarkable file API. To use it in your projects, install via composer (currently only dev-master is available, versioning will be introduced later).

composer require splitbrain/remarkable-api

The library consists of three classes:

  • splitbrain\ReMarkableAPI\ReMarkableAPI - this the main API interface, implementing the calls as described in the wiki
  • splitbrain\ReMarkableAPI\ReMarkableFS - the reMarkable stores all info in a flat hierarchy with documents identified by UUIDs only. This class makes the items accessible by path names (using / as a directory separator)
  • splitbrain\ReMarkableAPI\Client - this is just a thin wrapper around the Guzzle HTTP client which adds logging and handles authentication

After instantiating the ReMarkableAPI object, you need to call either register() or init() on it before you can issue any of the other calls. The first call will register your client through a 8 char code and return an API token. You need to save that token somewhere and pass it to init() for subsequent calls. Have a look at the command line client in remarkable.php to see how to use it all.

Command Line Client

To demonstrate the use of the PHP client library a command line script is included in the project. To use it, clone this project, then initialize the dependencies with composer:

composer install

Run the following to get a help screen on the usage:

./remarkable.php -h

Currently the following commands are implemented:

  • register - register this application as a new device
  • list - list all available files
  • delete - delete a folder or document
  • mkdir - create new folders
  • upload - upload a new PDF
  • download - download a Document
Comments
  • Problem connecting as of today

    Problem connecting as of today

    I've been using your script since February with no problems at all. Works very well and is indispensable for me as I only have Linux systems. As of this morning, I get the following error message when I try to use the script to even list the contents of the cloud: ☠ Client error: POST https://my.remarkable.com/token/user/new resulted in a 403 Forbidden response: You have been signed out. Please update your app to log in again. I get the same message if I try to "register" again. Any suggestions? Thanks, eric

    opened by ericsfraga 9
  • Feature request: include metadata in download

    Feature request: include metadata in download

    I have scripts that use the metadata information to process the lines files from the tablet. It would be very helpful if the download, using the php script, could include the metadata file along with the others in the zip file created.

    opened by ericsfraga 5
  • "Corrupt" files when uploading

    If I try to upload a PDF document, the PHP script seems to do the job but the tablet claims the document is corrupt when it downloads from the cloud. The "list" however seems to show that the documents are stored in the cloud (although if no "to" was given on the upload, they are placed in a separate folder named with the UUID of the document). Happy to provide more details if desired.

    Downloading (which is my main use case for your code) works very well indeed. Thank you!

    opened by ericsfraga 5
  • authentication endpoints respond in html

    authentication endpoints respond in html

    Hi, I did few tests in the past days with these api but all of a sudden since yesterday the authentication endpoints started to respond in html instead of normal string responses. I did my tests with Insomnia client, did not change the requests and everything was working fine but when I resumed my work yesterday I started to receive these html response (of which the rendered preview is empty).

    The other endpoints seem to respond as usual.

    Am I doing or did I do something wrong? Anyone else facing the same problem?

    Thanks, Nik

    opened by DrNik 3
  • Interfacing with reMarkable2 and trash items

    Interfacing with reMarkable2 and trash items

    The command line tool crashed with:

    ☠ Unknown ID trash. Inconsitent meta data
    

    I think it's because I have deleted a file on the reMarkable2. It's a bit difficult to test, sorry about that.

    The --loglevel debug shows:

    -> GET https://document-storage-production-dot-remarkable-production.appspot.com/document-storage/json/2/docs
    <- Status 200 5705 bytes
    [
     ...
        {
            "ID": "c2a07c8d-2fa1-47ec-9522-b178f88f443e",
            "Version": 2,
            "Message": "",
            "Success": true,
            "BlobURLGet": "",
            "BlobURLGetExpires": "0001-01-01T00:00:00Z",
            "ModifiedClient": "2020-12-25T11:21:56.481481Z",
            "Type": "DocumentType",
            "VissibleName": "redacted.pdf",
            "CurrentPage": 0,
            "Bookmarked": false,
            "Parent": "trash"
        },
    ...
    Exception caught in /home/ohad/dev/ReMarkableAPI/src/RemarkableFS.php:165
    #0 /home/ohad/dev/ReMarkableAPI/src/RemarkableFS.php(176): splitbrain\RemarkableAPI\RemarkableFS->calcPath('trash')
    #1 /home/ohad/dev/ReMarkableAPI/src/RemarkableFS.php(50): splitbrain\RemarkableAPI\RemarkableFS->calcPath('c2a07c8d-2fa1-4...')
    #2 /home/ohad/dev/ReMarkableAPI/src/RemarkableFS.php(28): splitbrain\RemarkableAPI\RemarkableFS->refreshIndex()
    #3 /home/ohad/dev/ReMarkableAPI/remarkable.php(117): splitbrain\RemarkableAPI\RemarkableFS->__construct(Object(splitbrain\RemarkableAPI\RemarkableAPI))
    #4 /home/ohad/dev/ReMarkableAPI/remarkable.php(73): Remarkable->cmdList()
    #5 /home/ohad/dev/ReMarkableAPI/vendor/splitbrain/php-cli/src/CLI.php(131): Remarkable->main(Object(splitbrain\phpcli\Options))
    #6 /home/ohad/dev/ReMarkableAPI/remarkable.php(225): splitbrain\phpcli\CLI->run()
    #7 {main}
    
    opened by ohad 2
  • Error during Authentication:

    Error during Authentication: "Failed to decode POST body as JSON"

    Hey, I'm trying to authenticate a custom script of mine, but I'm having some issues. Here's my code

    import requests
    
    r = requests.post(
        "https://my.remarkable.com/token/json/2/device/new",
        data={
            "code": "<code>",
            "deviceDesc": "desktop-windows",
            "deviceID": "<uuid4>",
        },
    )
    

    When I run this I get a response with a code of 400 and the text "Failed to decode POST body as JSON".

    Has the API changed?

    Thanks for the awesome writeup.

    opened by enricozb 2
  • Bump guzzlehttp/guzzle from 6.5.5 to 6.5.7

    Bump guzzlehttp/guzzle from 6.5.5 to 6.5.7

    Bumps guzzlehttp/guzzle from 6.5.5 to 6.5.7.

    Release notes

    Sourced from guzzlehttp/guzzle's releases.

    Release 6.5.7

    See change log for changes.

    Release 6.5.6

    See change log for changes.

    Changelog

    Sourced from guzzlehttp/guzzle's changelog.

    6.5.7 - 2022-06-09

    • Fix failure to strip Authorization header on HTTP downgrade
    • Fix failure to strip the Cookie header on change in host or HTTP downgrade

    6.5.6 - 2022-05-25

    • Fix cross-domain cookie leakage
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • Bump guzzlehttp/guzzle from 6.5.5 to 6.5.6

    Bump guzzlehttp/guzzle from 6.5.5 to 6.5.6

    Bumps guzzlehttp/guzzle from 6.5.5 to 6.5.6.

    Release notes

    Sourced from guzzlehttp/guzzle's releases.

    Release 6.5.6

    See change log for changes.

    Changelog

    Sourced from guzzlehttp/guzzle's changelog.

    6.5.6 - 2022-05-25

    • Fix cross-domain cookie leakage
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • User token endpoint broken?

    User token endpoint broken?

    Has the API been updated by reMarkable?

    I've been working on a Ruby API wrapper and am running into issues with user token endpoint. The device token endpoint is working correctly, but when I attempt to use that token to fetch / refresh a user token, I get a 400 with the message "You must connect a rM device in the webapp before you can use this service."

    However, when I check the web interface, a new app has been created.

    I also attempted to register a device with rmapi and am getting similar issues.

    Ideas?

    opened by jordanstreiff 1
  • Client error 401 response

    Client error 401 response

    I started getting this error:

    $ ./remarkable.php list
    ☠ Client error: `GET https://document-storage-production-dot-remarkable-production.appspot.com/document-storage/json/2/docs` resulted in a `401 Unauthorized` response:
    Serving request failed, Msg: invalid token: token contains an invalid number of segments: invalid token, Origin: invalid (truncated...)
    

    This error might be related to the recent software update (release 2.7).

    opened by ohad 1
  • [q] how to get ReMarkable user ID

    [q] how to get ReMarkable user ID

    Hi, thanks for the detailed writeup!

    I see that the token given out by rM is, in fact, a JWT token that has an "auth0-userid" field. Is it a real user ID that's unique (and same) for every user?

    For instance, mine is auth0|5f634d3c591dce00704a9822; I don't have a second rM account to see if it's different for other users.

    opened by utrack 1
  • Issues with reMarkable API service

    Issues with reMarkable API service

    Do you also experience errors 500 when trying to use the API? I get a 500 for any request on https://document-storage-production-dot-remarkable-production.appspot.com. Do you know if the team at reMarkable upgraded their internal API and left this one behind?

    Btw, their service manager returns the same service URL as before when queried:

    $ curl "https://service-manager-production-dot-remarkable-production.appspot.com/service/json/1/document-storage"
    {"Status":"OK","Host":"document-storage-production-dot-remarkable-production.appspot.com"}
    
    opened by RomainGehrig 1
  • Using Any API Method (e.g. ./remarkable.php list) Post `register` is Failing with 401

    Using Any API Method (e.g. ./remarkable.php list) Post `register` is Failing with 401

    As the title suggests, I am unable to successfully call any endpoint - even after I call remarkable.php register <valid-registration-code>.

    This may even be an issue with the overall usage of remarkable's endpoints, as I have been unable to call their endpoints directly.

    Steps to reproduce:

    1. Query for a new token:
      		GET: https://webapp-production-dot-remarkable-production.appspot.com/token/json/2/device/new`
      
      		 { 
      		      "code": "fuzzbuzz",
      		      "deviceDesc": "desktop-windows",
      		      "deviceID": "160022F6-EA84-42C7-B413-C1F5C6C3112F"
      		 }
      
    2. Fetch a refresh token:
      		POST: https://webapp-production-dot-remarkable-production.appspot.com/token/json/2/user/new
      		--header 'Authorization: Bearer eyJ...'
      
    3. Take the token from step 2, and validate the document endpoint:
      		GET: https://service-manager-production-dot-remarkable-production.appspot.com/service/json/1/document-storage?environment=production&group=auth0%7C5a68dc51cb30df3877a1d7c4&apiVer=2
      		--header 'Authorization: Bearer eyJ...'
      

      Which returns

      		{
      		      "Status": "OK",
      		      "Host": "document-storage-production-dot-remarkable-production.appspot.com"
      		}
      
    4. Take this endpoint, and query the docs endpoint
      		GET: https://document-storage-production-dot-remarkable-production.appspot.com/document-storage/json/2/docs
      		--header 'Authorization: Bearer eyJ...'
      

      Which returns a 401: Unauthorized status code. What am I missing?

    opened by funkeyfreak 7
  • Updating PDF doesn't sync with tablet

    Updating PDF doesn't sync with tablet

    I'm trying to update a PDF document. When I send the new version of the pdf document I'm making sure to update the version and the ModifiedClient metadata. So far I can see the updated pdf on the mac and android apps. But the PDF on the remarkable tablet stays the same, even after trying manual sync on settings -> storage -> check cloud sync.

    If I draw on the non updated PDF on the tablet. It brings back the old version on all devices.

    Is there a way to make sure the tablet downloads the latest version as well? Has anybody has updated a PDF successfully?

    I'm testing on the Remarkable 2 on version 2.6.2.75

    update: Same Issue with version 2.7.0.51

    opened by stevenbarragan 0
  • Deleting root-node gives issues

    Deleting root-node gives issues

    When uploading a file with the command like this: remarkable.php upload /path1/path2/path3/filename.txt the script correctly creates all the directories path1->path2->path3 with the name inside path3. This worked excellent.

    But then I tried to delete "path1" directly, without deleting the sub-directories. This delete is not "recursive", so the sub-directories seem to still exist, and then seems to lead to some corruption. When I tried to run the "remarkable.php list"-command, it gave an error message about wrong metadata (Don't remember the exact error message)

    But if I deleted all the path2, path3 and the file inside path3, the list-command worked again, and it seems that I cleaned everything up.

    => Please consider one of two options:

    1. Don't allow to delete directories that are not empty
    2. Make the delete-command recursive to delete everything

    Else, love the php-"app"!

    opened by Anonym-Bruker 0
  • change User agent

    change User agent

    According to https://github.com/juruen/rmapi/issues/23#issuecomment-425832548 you may now provide your own user agent string.

    I guess it is about this... https://github.com/splitbrain/ReMarkableAPI/blob/5eafff7a50981d59d0f5a05712b11e210842210c/src/RemarkableAPI.php#L69

    opened by Klap-in 2
Sync Wordpress Pages and Posts (even custom post types + fields) from static Markdown + YAML files

Sync Markdown Files to WordPress Posts and Pages Static site generators let you use a revision-controlled tree of markdown files to make a site, but d

null 26 Sep 26, 2022
File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery

File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.

Sebastian Tschan 31.1k Dec 30, 2022
File Storage Api

flux-file-storage-api File Storage Api Installation Native Download RUN (mkdir -p /%path%/libs/flux-file-storage-api && cd /%path%/libs/flux-file-stor

null 1 Dec 12, 2022
Optimizes class loading performance by generating a single PHP file containing all of the autoloaded files.

Class Preloader for PHP This tool is used to generate a single PHP script containing all of the classes required for a specific use case. Using a sing

Class Preloader 356 Nov 26, 2022
⚙️ A WordPress plugin to set WordPress options from a .env file.

dotenv A WordPress plugin to set WordPress options from a .env file. Any WPENV_ prefixed variables in the .env will be used to override the WordPress

Brad Parbs 13 Oct 6, 2022
CrateKeyShopGUI Pocketmine-MP plugin which can be set in Config.yml file

CrateKeyShopGUI CrateKeyShopGUI Pocketmine-MP plugin which can be set in Config.yml file Depend FormAPI EconomyAPI PiggyCrate InvCrashFix Download Dow

null 4 Jan 7, 2022
🔨 Prefixes all PHP namespaces in a file/directory to isolate the code bundled in PHARs.

PHP-Scoper PHP-Scoper is a tool which essentially moves any body of code, including all dependencies such as vendor directories, to a new and distinct

Humbug 590 Jan 2, 2023
Rules to detect game engines and other technologies based on Steam depot file lists

SteamDB File Detection Rule Sets This is a set of scripts that are used by SteamDB to make educated guesses about the engine(s) & technology used to b

Steam Database 103 Dec 14, 2022
This shell script and PHP file create a browseable HTML site from the Zig standard library source.

Browseable Zig standard library This shell script and PHP file create a browseable HTML site from the Zig standard library source. The idea is to inve

Dave Gauer 3 Mar 20, 2022
Write to Laravel Config files and maintain file integrity

Laravel Config Writer Write to Laravel Config files and maintain file integrity. This library is an extension of the Config component used by Laravel.

Sam Geo 158 Dec 30, 2022
Control all text in multiple file bad words filter with worps

About Worps | PHP! Control all text in multiple file bad words filter with worps If you try online Click What to do use for worps Create new object Wo

null 1 Dec 30, 2021
A PHP library for creating EDI 837 claim equivalent to paper-claim 1500. EDI X12 ANSI 837 File 5010 Version

EDI X12 ANSI 5010 PHP Library for creating EDI X12 ANSI 837 File 5010 Version A Simple PHP function for creating an EDI X12 ANSI 837 file version 0050

WalksWithMe 7 Jan 3, 2023
This packages enables the ability to serve file streams in a smart way

A blade component for easy image manipulation Want to serve private hosted images without the need to code your own logic ? Want to resize your images

Dieter Coopman 205 Dec 19, 2022
One-file composer scripts

Melody - One-file composer scripts Create a file named test.php: <?php <<<CONFIG packages: - "symfony/finder: ~2.8" CONFIG; $finder = Symfony\Com

SensioLabs 399 Aug 18, 2022
Composer script handling your ignored parameter file

Managing your ignored parameters with Composer This tool allows you to manage your ignored parameters when running a composer install or update. It wo

Incenteev 921 Nov 21, 2022
High-performance, low-memory-footprint, single-file embedded database for key/value storage

LDBA - a fast, pure PHP, key-value database. Information LDBA is a high-performance, low-memory-footprint, single-file embedded database for key/value

Simplito 12 Nov 13, 2022
Virtual File System

VFS (Virtual File System) VFS is a virtual file system for PHP built using the stream wrapper API. Streams are exposed just as typical file:// or http

Andrew Lawson 295 Oct 29, 2022
PHP Project - Export your profile to vCard file

KamVCard PHP v1.00 Easy to have your own vCard file, fill in the form and hit the "Export" button UPDATE : Now you can add your picture too ! Preview

Med Reda Kamal 3 Nov 6, 2022