github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/docs/notes.md (about)

     1  [Table of contents](README.md#table-of-contents)
     2  
     3  # Notes for collaborative edition
     4  
     5  The cozy-notes application can be used to take notes, and collaborate on them.
     6  The note is persisted as a file in the VFS, but it also has specific routes to
     7  enable the collaborative edition in real-time. The content of the file is a
     8  markdown export of the notes, except if the note has images, and it is a tar
     9  with the markdown as `index.md` and the images in that case. The markdown
    10  format is mostly compatible with CommonMark, but there are a few changes:
    11  
    12  - we are using the [consistent attribute syntax](https://talk.commonmark.org/t/consistent-attribute-syntax/272) for some markups like colors and underline
    13  - the tables are not saved like in [GFM](https://github.github.com/gfm/#tables-extension-) because we can have merged cells and several paragraphs inside a cell
    14  - misc.
    15  
    16  The downloaded files can be reuploaded to the Cozy, and if the `.cozy-note`
    17  extension is kept, the stack will try to recreate the Prosemirror tree, making
    18  possible to use the uploaded file as a note in Cozy-Notes with realtime
    19  collaboration.
    20  
    21  ## Routes
    22  
    23  ### POST /notes
    24  
    25  It creates a note: it creates a files with the right metadata for collaborative edition.
    26  
    27  **Note:** a permission on `POST io.cozy.files` is required to use this route.
    28  
    29  #### Parameter
    30  
    31  | Parameter | Description                                                               |
    32  | --------- | ------------------------------------------------------------------------- |
    33  | title     | The title of the note, that will also be used for the filename            |
    34  | dir_id    | The identifier of the directory where the file will be created (optional) |
    35  | schema    | The schema for prosemirror (with OrderedMap transformed as arrays)        |
    36  | content   | The initial content of the note (optional)                                |
    37  
    38  **Note:** if the `dir_id` is not given, the file will be created in a `Notes`
    39  directory (and this directory will have a referenced_by on the notes apps to
    40  allow to find this directory even if it is renamed or moved later).
    41  
    42  #### Request
    43  
    44  ```http
    45  POST /notes HTTP/1.1
    46  Host: alice.example.net
    47  Content-Type: application/vnd.api+json
    48  ```
    49  
    50  ```json
    51  {
    52    "data": {
    53      "type": "io.cozy.notes.documents",
    54      "attributes": {
    55        "title": "My new note",
    56        "dir_id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
    57        "schema": {
    58          "nodes": [
    59            ["doc", { "content": "block+" }],
    60            ["paragraph", { "content": "inline*", "group": "block" }],
    61            ["blockquote", { "content": "block+", "group": "block" }],
    62            ["horizontal_rule", { "group": "block" }],
    63            [
    64              "heading",
    65              {
    66                "content": "inline*",
    67                "group": "block",
    68                "attrs": { "level": { "default": 1 } }
    69              }
    70            ],
    71            ["code_block", { "content": "text*", "marks": "", "group": "block" }],
    72            ["text", { "group": "inline" }],
    73            [
    74              "image",
    75              {
    76                "group": "inline",
    77                "inline": true,
    78                "attrs": { "alt": {}, "src": {}, "title": {} }
    79              }
    80            ],
    81            ["hard_break", { "group": "inline", "inline": true }],
    82            [
    83              "ordered_list",
    84              {
    85                "content": "list_item+",
    86                "group": "block",
    87                "attrs": { "order": { "default": 1 } }
    88              }
    89            ],
    90            ["bullet_list", { "content": "list_item+", "group": "block" }],
    91            ["list_item", { "content": "paragraph block*" }]
    92          ],
    93          "marks": [
    94            ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
    95            ["em", {}],
    96            ["strong", {}],
    97            ["code", {}]
    98          ],
    99          "topNode": "doc"
   100        }
   101      }
   102    }
   103  }
   104  ```
   105  
   106  #### Response
   107  
   108  ```http
   109  HTTP/1.1 201 Created
   110  Content-Type: application/vnd.api+json
   111  ```
   112  
   113  ```json
   114  {
   115    "data": {
   116      "type": "io.cozy.files",
   117      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   118      "meta": {
   119        "rev": "1-f71ee54e2"
   120      },
   121      "attributes": {
   122        "type": "file",
   123        "name": "My new note.cozy-note",
   124        "trashed": false,
   125        "md5sum": "NjhiMzI5ZGE5ODkzZTM0MDk5YzdkOGFkNWNiOWM5NDAgIC0K",
   126        "created_at": "2019-11-05T12:38:04Z",
   127        "updated_at": "2019-11-05T12:38:04Z",
   128        "tags": [],
   129        "metadata": {
   130          "title": "My new note",
   131          "content": { "type": "doc", "content": [{ "type": "paragraph" }] },
   132          "version": 0,
   133          "schema": {
   134            "nodes": [
   135              ["doc", { "content": "block+" }],
   136              ["paragraph", { "content": "inline*", "group": "block" }],
   137              ["blockquote", { "content": "block+", "group": "block" }],
   138              ["horizontal_rule", { "group": "block" }],
   139              [
   140                "heading",
   141                {
   142                  "content": "inline*",
   143                  "group": "block",
   144                  "attrs": { "level": { "default": 1 } }
   145                }
   146              ],
   147              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
   148              ["text", { "group": "inline" }],
   149              [
   150                "image",
   151                {
   152                  "group": "inline",
   153                  "inline": true,
   154                  "attrs": { "alt": {}, "src": {}, "title": {} }
   155                }
   156              ],
   157              ["hard_break", { "group": "inline", "inline": true }],
   158              [
   159                "ordered_list",
   160                {
   161                  "content": "list_item+",
   162                  "group": "block",
   163                  "attrs": { "order": { "default": 1 } }
   164                }
   165              ],
   166              ["bullet_list", { "content": "list_item+", "group": "block" }],
   167              ["list_item", { "content": "paragraph block*" }]
   168            ],
   169            "marks": [
   170              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
   171              ["em", {}],
   172              ["strong", {}],
   173              ["code", {}]
   174            ],
   175            "topNode": "doc"
   176          }
   177        },
   178        "size": 1,
   179        "executable": false,
   180        "class": "text",
   181        "mime": "text/vnd.cozy.note+markdown",
   182        "cozyMetadata": {
   183          "doctypeVersion": "1",
   184          "metadataVersion": 1,
   185          "createdAt": "2019-11-05T12:38:04Z",
   186          "createdOn": "https://alice.example.net/",
   187          "updatedAt": "2019-11-05T12:38:04Z",
   188          "uploadedAt": "2019-11-05T12:38:04Z",
   189          "uploadedOn": "https://alice.example.net/"
   190        }
   191      },
   192      "relationships": {
   193        "parent": {
   194          "links": {
   195            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
   196          },
   197          "data": {
   198            "type": "io.cozy.files",
   199            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
   200          }
   201        }
   202      }
   203    }
   204  }
   205  ```
   206  
   207  ### GET /notes
   208  
   209  It returns the list of notes, sorted by last update. It adds the path for the
   210  files in the response, as it can be convient for the notes application.
   211  
   212  **Note:** a permission on `GET io.cozy.files` is required to use this route.
   213  
   214  #### Request
   215  
   216  ```http
   217  GET /notes HTTP/1.1
   218  Host: alice.example.net
   219  Accept: application/vnd.api+json
   220  ```
   221  #### Response
   222  
   223  ```http
   224  HTTP/1.1 200 OK
   225  Content-Type: application/vnd.api+json
   226  ```
   227  
   228  ```json
   229  {
   230    "links": {
   231      "next": "/notes?page[cursor]=a078d6f0-04a9-0138-3e03-543d7eb8149c"
   232    },
   233    "data": [{
   234      "type": "io.cozy.files",
   235      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   236      "meta": {
   237        "rev": "1-f71ee54e2"
   238      },
   239      "attributes": {
   240        "type": "file",
   241        "name": "My new note.cozy-note",
   242        "path": "/Notes/my new note.cozy-note",
   243        "trashed": false,
   244        "md5sum": "NjhiMzI5ZGE5ODkzZTM0MDk5YzdkOGFkNWNiOWM5NDAgIC0K",
   245        "created_at": "2019-11-05T12:38:04Z",
   246        "updated_at": "2019-11-05T12:38:04Z",
   247        "tags": [],
   248        "metadata": {
   249          "title": "My new note",
   250          "content": { "type": "doc", "content": [{ "type": "paragraph" }] },
   251          "version": 0,
   252          "schema": {
   253            "nodes": [
   254              ["doc", { "content": "block+" }],
   255              ["paragraph", { "content": "inline*", "group": "block" }],
   256              ["blockquote", { "content": "block+", "group": "block" }],
   257              ["horizontal_rule", { "group": "block" }],
   258              [
   259                "heading",
   260                {
   261                  "content": "inline*",
   262                  "group": "block",
   263                  "attrs": { "level": { "default": 1 } }
   264                }
   265              ],
   266              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
   267              ["text", { "group": "inline" }],
   268              [
   269                "image",
   270                {
   271                  "group": "inline",
   272                  "inline": true,
   273                  "attrs": { "alt": {}, "src": {}, "title": {} }
   274                }
   275              ],
   276              ["hard_break", { "group": "inline", "inline": true }],
   277              [
   278                "ordered_list",
   279                {
   280                  "content": "list_item+",
   281                  "group": "block",
   282                  "attrs": { "order": { "default": 1 } }
   283                }
   284              ],
   285              ["bullet_list", { "content": "list_item+", "group": "block" }],
   286              ["list_item", { "content": "paragraph block*" }]
   287            ],
   288            "marks": [
   289              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
   290              ["em", {}],
   291              ["strong", {}],
   292              ["code", {}]
   293            ],
   294            "topNode": "doc"
   295          }
   296        },
   297        "size": 1,
   298        "executable": false,
   299        "class": "text",
   300        "mime": "text/vnd.cozy.note+markdown",
   301        "cozyMetadata": {
   302          "doctypeVersion": "1",
   303          "metadataVersion": 1,
   304          "createdAt": "2019-11-05T12:38:04Z",
   305          "createdOn": "https://alice.example.net/",
   306          "updatedAt": "2019-11-05T12:38:04Z",
   307          "uploadedAt": "2019-11-05T12:38:04Z",
   308          "uploadedOn": "https://alice.example.net/"
   309        }
   310      },
   311      "relationships": {
   312        "parent": {
   313          "links": {
   314            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
   315          },
   316          "data": {
   317            "type": "io.cozy.files",
   318            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
   319          }
   320        }
   321      }
   322    }]
   323  }
   324  ```
   325  
   326  
   327  ### GET /notes/:id
   328  
   329  It fetches the file with the given id. It also includes the changes in the
   330  content that have been accepted by the stack but not yet persisted to the file.
   331  
   332  #### Request
   333  
   334  ```http
   335  GET /notes/bf0dbdb0-e1ed-0137-8548-543d7eb8149c HTTP/1.1
   336  Host: alice.example.net
   337  Accept: application/vnd.api+json
   338  ```
   339  
   340  #### Response
   341  
   342  ```http
   343  HTTP/1.1 200 OK
   344  Content-Type: application/vnd.api+json
   345  ```
   346  
   347  ```json
   348  {
   349    "data": {
   350      "type": "io.cozy.files",
   351      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   352      "meta": {
   353        "rev": "4-1482b88a"
   354      },
   355      "attributes": {
   356        "type": "file",
   357        "name": "My new note.cozy-note",
   358        "trashed": false,
   359        "md5sum": "NjhiMzI5ZGE5ODkzZTM0MDk5YzdkOGFkNWNiOWM5NDAgIC0K",
   360        "created_at": "2019-11-05T12:38:04Z",
   361        "updated_at": "2019-11-05T12:38:52Z",
   362        "tags": [],
   363        "metadata": {
   364          "title": "My new note",
   365          "content": { "type": "doc", "content": [{ "type": "horizontal_rule" }] },
   366          "version": 3,
   367          "schema": {
   368            "nodes": [
   369              ["doc", { "content": "block+" }],
   370              ["paragraph", { "content": "inline*", "group": "block" }],
   371              ["blockquote", { "content": "block+", "group": "block" }],
   372              ["horizontal_rule", { "group": "block" }],
   373              [
   374                "heading",
   375                {
   376                  "content": "inline*",
   377                  "group": "block",
   378                  "attrs": { "level": { "default": 1 } }
   379                }
   380              ],
   381              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
   382              ["text", { "group": "inline" }],
   383              [
   384                "image",
   385                {
   386                  "group": "inline",
   387                  "inline": true,
   388                  "attrs": { "alt": {}, "src": {}, "title": {} }
   389                }
   390              ],
   391              ["hard_break", { "group": "inline", "inline": true }],
   392              [
   393                "ordered_list",
   394                {
   395                  "content": "list_item+",
   396                  "group": "block",
   397                  "attrs": { "order": { "default": 1 } }
   398                }
   399              ],
   400              ["bullet_list", { "content": "list_item+", "group": "block" }],
   401              ["list_item", { "content": "paragraph block*" }]
   402            ],
   403            "marks": [
   404              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
   405              ["em", {}],
   406              ["strong", {}],
   407              ["code", {}]
   408            ],
   409            "topNode": "doc"
   410          }
   411        },
   412        "size": 4,
   413        "executable": false,
   414        "class": "text",
   415        "mime": "text/vnd.cozy.note+markdown",
   416        "cozyMetadata": {
   417          "doctypeVersion": "1",
   418          "metadataVersion": 1,
   419          "createdAt": "2019-11-05T12:38:04Z",
   420          "createdOn": "https://alice.example.net/",
   421          "updatedAt": "2019-11-05T12:38:04Z",
   422          "uploadedAt": "2019-11-05T12:38:04Z",
   423          "uploadedOn": "https://alice.example.net/"
   424        }
   425      },
   426      "relationships": {
   427        "parent": {
   428          "links": {
   429            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
   430          },
   431          "data": {
   432            "type": "io.cozy.files",
   433            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
   434          }
   435        }
   436      }
   437    }
   438  }
   439  ```
   440  
   441  ### GET /notes/:id/text
   442  
   443  It returns the content of the note as text with no formatting.
   444  
   445  #### Request
   446  
   447  ```http
   448  GET /notes/bf0dbdb0-e1ed-0137-8548-543d7eb8149c/text HTTP/1.1
   449  Host: alice.example.net
   450  ```
   451  
   452  #### Response
   453  
   454  ```http
   455  HTTP/1.1 200 OK
   456  Content-Type: text/plain; charset=UTF-8
   457  ```
   458  
   459  ```
   460  This is the content of the note.
   461  ```
   462  
   463  
   464  ### GET /notes/texts
   465  
   466  It returns the content of several notes as text with no formatting. The
   467  identifiers of the notes must be given in the query-string parameter `ids`, as
   468  a comma-separated list.
   469  
   470  #### Request
   471  
   472  ```http
   473  GET /notes/texts?ids=bf0dbdb0-e1ed-0137-8548-543d7eb8149c,6bc77a60-c8fe-013c-20a9-18c04daba326 HTTP/1.1
   474  Host: alice.example.net
   475  ```
   476  
   477  #### Response
   478  
   479  ```http
   480  HTTP/1.1 200 OK
   481  Content-Type: application/json
   482  ```
   483  
   484  ```json
   485  {
   486    "bf0dbdb0-e1ed-0137-8548-543d7eb8149c": "This is the content of the note.",
   487    "6bc77a60-c8fe-013c-20a9-18c04daba326": "This is the text of another note."
   488  }
   489  ```
   490  
   491  ### GET /notes/:id/steps?Version=xxx
   492  
   493  It returns the steps since the given version. If the revision is too old, and
   494  the steps are no longer available, it returns a 412 response with the whole
   495  document for the note.
   496  
   497  #### Request
   498  
   499  ```http
   500  GET /notes/bf0dbdb0-e1ed-0137-8548-543d7eb8149c/steps?Version=3 HTTP/1.1
   501  Host: alice.example.net
   502  Accept: application/vnd.api+json
   503  ```
   504  
   505  #### Response (success)
   506  
   507  ```http
   508  HTTP/1.1 200 OK
   509  Content-Type: application/vnd.api+json
   510  ```
   511  
   512  ```json
   513  {
   514    "data": [{
   515      "type": "io.cozy.notes.steps",
   516      "attributes": {
   517        "sessionID": "543781490137",
   518        "stepType": "replace",
   519        "from": 1,
   520        "to": 1,
   521        "slice": {
   522          "content": [{ "type": "text", "text": "H" }]
   523        },
   524        "version": 4
   525      }
   526    }, {
   527      "type": "io.cozy.notes.steps",
   528      "attributes": {
   529        "sessionID": "543781490137",
   530        "stepType": "replace",
   531        "from": 2,
   532        "to": 2,
   533        "slice": {
   534          "content": [{ "type": "text", "text": "ello" }]
   535        },
   536        "version": 5
   537      }
   538    }]
   539  }
   540  ```
   541  
   542  #### Response (failure)
   543  
   544  ```http
   545  HTTP/1.1 412 Precondition Failed
   546  Content-Type: application/vnd.api+json
   547  ```
   548  
   549  ```json
   550  {
   551    "data": {
   552      "type": "io.cozy.files",
   553      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   554      "meta": {
   555        "rev": "4-1482b88a"
   556      },
   557      "attributes": {
   558        "type": "file",
   559        "name": "My new note.cozy-note",
   560        "trashed": false,
   561        "md5sum": "NjhiMzI5ZGE5ODkzZTM0MDk5YzdkOGFkNWNiOWM5NDAgIC0K",
   562        "created_at": "2019-11-05T12:38:04Z",
   563        "updated_at": "2019-11-05T12:38:52Z",
   564        "tags": [],
   565        "metadata": {
   566          "title": "My new note",
   567          "content": { "type": "doc", "content": [{ "type": "horizontal_rule" }] },
   568          "version": 6,
   569          "schema": {
   570            "nodes": [
   571              ["doc", { "content": "block+" }],
   572              ["paragraph", { "content": "inline*", "group": "block" }],
   573              ["blockquote", { "content": "block+", "group": "block" }],
   574              ["horizontal_rule", { "group": "block" }],
   575              [
   576                "heading",
   577                {
   578                  "content": "inline*",
   579                  "group": "block",
   580                  "attrs": { "level": { "default": 1 } }
   581                }
   582              ],
   583              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
   584              ["text", { "group": "inline" }],
   585              [
   586                "image",
   587                {
   588                  "group": "inline",
   589                  "inline": true,
   590                  "attrs": { "alt": {}, "src": {}, "title": {} }
   591                }
   592              ],
   593              ["hard_break", { "group": "inline", "inline": true }],
   594              [
   595                "ordered_list",
   596                {
   597                  "content": "list_item+",
   598                  "group": "block",
   599                  "attrs": { "order": { "default": 1 } }
   600                }
   601              ],
   602              ["bullet_list", { "content": "list_item+", "group": "block" }],
   603              ["list_item", { "content": "paragraph block*" }]
   604            ],
   605            "marks": [
   606              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
   607              ["em", {}],
   608              ["strong", {}],
   609              ["code", {}]
   610            ],
   611            "topNode": "doc"
   612          }
   613        },
   614        "size": 4,
   615        "executable": false,
   616        "class": "text",
   617        "mime": "text/vnd.cozy.note+markdown",
   618        "cozyMetadata": {
   619          "doctypeVersion": "1",
   620          "metadataVersion": 1,
   621          "createdAt": "2019-11-05T12:38:04Z",
   622          "createdOn": "https://alice.example.net/",
   623          "updatedAt": "2019-11-05T12:38:04Z",
   624          "uploadedAt": "2019-11-05T12:38:04Z",
   625          "uploadedOn": "https://alice.example.net/"
   626        }
   627      },
   628      "relationships": {
   629        "parent": {
   630          "links": {
   631            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
   632          },
   633          "data": {
   634            "type": "io.cozy.files",
   635            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
   636          }
   637        }
   638      }
   639    }
   640  }
   641  ```
   642  
   643  ### PUT /notes/:id/title
   644  
   645  It updates the title.
   646  
   647  #### Request
   648  
   649  ```http
   650  PUT /notes/bf0dbdb0-e1ed-0137-8548-543d7eb8149c/title HTTP/1.1
   651  Host: alice.example.net
   652  Content-Type: application/vnd.api+json
   653  ```
   654  
   655  ```json
   656  {
   657    "data": {
   658      "type": "io.cozy.notes.documents",
   659      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   660      "attributes": {
   661        "sessionID": "543781490137",
   662        "title": "A new title for my note"
   663      }
   664    }
   665  }
   666  ```
   667  
   668  #### Response
   669  
   670  ```http
   671  HTTP/1.1 200 OK
   672  Content-Type: application/vnd.api+json
   673  ```
   674  
   675  ```json
   676  {
   677    "data": {
   678      "type": "io.cozy.files",
   679      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   680      "meta": {
   681        "rev": "4-1482b88a"
   682      },
   683      "attributes": {
   684        "type": "file",
   685        "name": "A new title for my note.cozy-note",
   686        "trashed": false,
   687        "md5sum": "NjhiMzI5ZGE5ODkzZTM0MDk5YzdkOGFkNWNiOWM5NDAgIC0K",
   688        "created_at": "2019-11-05T12:38:04Z",
   689        "updated_at": "2019-11-05T12:39:37Z",
   690        "tags": [],
   691        "metadata": {
   692          "title": "A new title for my note",
   693          "content": { "type": "doc", "content": [{ "type": "horizontal_rule" }] },
   694          "version": 3,
   695          "schema": {
   696            "nodes": [
   697              ["doc", { "content": "block+" }],
   698              ["paragraph", { "content": "inline*", "group": "block" }],
   699              ["blockquote", { "content": "block+", "group": "block" }],
   700              ["horizontal_rule", { "group": "block" }],
   701              [
   702                "heading",
   703                {
   704                  "content": "inline*",
   705                  "group": "block",
   706                  "attrs": { "level": { "default": 1 } }
   707                }
   708              ],
   709              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
   710              ["text", { "group": "inline" }],
   711              [
   712                "image",
   713                {
   714                  "group": "inline",
   715                  "inline": true,
   716                  "attrs": { "alt": {}, "src": {}, "title": {} }
   717                }
   718              ],
   719              ["hard_break", { "group": "inline", "inline": true }],
   720              [
   721                "ordered_list",
   722                {
   723                  "content": "list_item+",
   724                  "group": "block",
   725                  "attrs": { "order": { "default": 1 } }
   726                }
   727              ],
   728              ["bullet_list", { "content": "list_item+", "group": "block" }],
   729              ["list_item", { "content": "paragraph block*" }]
   730            ],
   731            "marks": [
   732              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
   733              ["em", {}],
   734              ["strong", {}],
   735              ["code", {}]
   736            ],
   737            "topNode": "doc"
   738          }
   739        },
   740        "size": 4,
   741        "executable": false,
   742        "class": "text",
   743        "mime": "text/vnd.cozy.note+markdown",
   744        "cozyMetadata": {
   745          "doctypeVersion": "1",
   746          "metadataVersion": 1,
   747          "createdAt": "2019-11-05T12:38:04Z",
   748          "createdOn": "https://alice.example.net/",
   749          "updatedAt": "2019-11-05T12:39:37Z",
   750          "uploadedAt": "2019-11-05T12:38:04Z",
   751          "uploadedOn": "https://alice.example.net/"
   752        }
   753      },
   754      "relationships": {
   755        "parent": {
   756          "links": {
   757            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
   758          },
   759          "data": {
   760            "type": "io.cozy.files",
   761            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
   762          }
   763        }
   764      }
   765    }
   766  }
   767  ```
   768  
   769  ### PATCH /notes/:id
   770  
   771  It sends some steps to apply on the document. The last known version of the
   772  note must be sent in the `If-Match` header to avoid conflicts.
   773  
   774  #### Request
   775  
   776  ```http
   777  PATCH /notes/bf0dbdb0-e1ed-0137-8548-543d7eb8149c HTTP/1.1
   778  Host: alice.example.net
   779  If-Match: 3
   780  Content-Type: application/vnd.api+json
   781  ```
   782  
   783  ```json
   784  {
   785    "data": [{
   786      "type": "io.cozy.notes.steps",
   787      "attributes": {
   788        "sessionID": "543781490137",
   789        "stepType": "replace",
   790        "from": 1,
   791        "to": 1,
   792        "slice": {
   793          "content": [{ "type": "text", "text": "H" }]
   794        }
   795      }
   796    }, {
   797      "type": "io.cozy.notes.steps",
   798      "attributes": {
   799        "sessionID": "543781490137",
   800        "stepType": "replace",
   801        "from": 2,
   802        "to": 2,
   803        "slice": {
   804          "content": [{ "type": "text", "text": "ello" }]
   805        }
   806      }
   807    }]
   808  }
   809  ```
   810  
   811  #### Response (success)
   812  
   813  ```http
   814  HTTP/1.1 200 OK
   815  Content-Type: application/vnd.api+json
   816  ```
   817  
   818  ```json
   819  {
   820    "data": {
   821      "type": "io.cozy.files",
   822      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
   823      "meta": {
   824        "rev": "4-1482b88a"
   825      },
   826      "attributes": {
   827        "type": "file",
   828        "name": "A new title for my note.cozy-note",
   829        "trashed": false,
   830        "md5sum": "MDlmN2UwMmYxMjkwYmUyMTFkYTcwN2EyNjZmMTUzYjMgIC0K",
   831        "created_at": "2019-11-05T12:38:04Z",
   832        "updated_at": "2019-11-05T12:39:37Z",
   833        "tags": [],
   834        "metadata": {
   835          "title": "A new title for my note",
   836          "content": {
   837            "type": "doc",
   838            "content": [{ "type": "paragraph", "text": "Hello" }]
   839          },
   840          "version": 5,
   841          "schema": {
   842            "nodes": [
   843              ["doc", { "content": "block+" }],
   844              ["paragraph", { "content": "inline*", "group": "block" }],
   845              ["blockquote", { "content": "block+", "group": "block" }],
   846              ["horizontal_rule", { "group": "block" }],
   847              [
   848                "heading",
   849                {
   850                  "content": "inline*",
   851                  "group": "block",
   852                  "attrs": { "level": { "default": 1 } }
   853                }
   854              ],
   855              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
   856              ["text", { "group": "inline" }],
   857              [
   858                "image",
   859                {
   860                  "group": "inline",
   861                  "inline": true,
   862                  "attrs": { "alt": {}, "src": {}, "title": {} }
   863                }
   864              ],
   865              ["hard_break", { "group": "inline", "inline": true }],
   866              [
   867                "ordered_list",
   868                {
   869                  "content": "list_item+",
   870                  "group": "block",
   871                  "attrs": { "order": { "default": 1 } }
   872                }
   873              ],
   874              ["bullet_list", { "content": "list_item+", "group": "block" }],
   875              ["list_item", { "content": "paragraph block*" }]
   876            ],
   877            "marks": [
   878              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
   879              ["em", {}],
   880              ["strong", {}],
   881              ["code", {}]
   882            ],
   883            "topNode": "doc"
   884          }
   885        },
   886        "size": 6,
   887        "executable": false,
   888        "class": "text",
   889        "mime": "text/vnd.cozy.note+markdown",
   890        "cozyMetadata": {
   891          "doctypeVersion": "1",
   892          "metadataVersion": 1,
   893          "createdAt": "2019-11-05T12:38:04Z",
   894          "createdOn": "https://alice.example.net/",
   895          "updatedAt": "2019-11-05T12:39:37Z",
   896          "uploadedAt": "2019-11-05T12:38:04Z",
   897          "uploadedOn": "https://alice.example.net/"
   898        }
   899      },
   900      "relationships": {
   901        "parent": {
   902          "links": {
   903            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
   904          },
   905          "data": {
   906            "type": "io.cozy.files",
   907            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
   908          }
   909        }
   910      }
   911    }
   912  }
   913  ```
   914  
   915  #### Response (failure)
   916  
   917  If at least one step can't be applied, they will all be discarded, and the
   918  response will be this error:
   919  
   920  ```http
   921  HTTP/1.1 409 Conflict
   922  Content-Type: application/vnd.api+json
   923  ```
   924  
   925  ```json
   926  {
   927    "status": 409,
   928    "Title": "Conflict",
   929    "Detail": "Cannot apply the steps"
   930  }
   931  ```
   932  
   933  ### PUT /notes/:id/telepointer
   934  
   935  It updates the position of the pointer.
   936  
   937  #### Request
   938  
   939  ```http
   940  PUT /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/telepointer HTTP/1.1
   941  Content-Type: application/vnd.api+json
   942  ```
   943  
   944  ```json
   945  {
   946    "data": {
   947      "type": "io.cozy.notes.telepointers",
   948      "id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
   949      "attributes": {
   950        "sessionID": "543781490137",
   951        "anchor": 7,
   952        "head": 12,
   953        "type": "textSelection"
   954      }
   955    }
   956  }
   957  ```
   958  
   959  #### Response
   960  
   961  ```http
   962  HTTP/1.1 204 No Content
   963  ```
   964  
   965  ### POST /notes/:id/sync
   966  
   967  It forces writing the note to the virtual file system. It may be used after the
   968  title has been changed, or when the user quits the note.
   969  
   970  #### Request
   971  
   972  ```http
   973  POST /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/sync HTTP/1.1
   974  ```
   975  
   976  #### Response
   977  
   978  ```http
   979  HTTP/1.1 204 No Content
   980  ```
   981  
   982  ### GET /notes/:id/open
   983  
   984  It return the parameters to build the URL where the note can be opened. It can
   985  be on the same cozy instance, or on another instance if the note is shared.
   986  
   987  If the identifier doesn't give a note, the response will be a `404 Page not
   988  found`.
   989  
   990  #### Request
   991  
   992  ```http
   993  GET /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/open HTTP/1.1
   994  Host: bob.cozy.example
   995  ```
   996  
   997  #### Response
   998  
   999  ```http
  1000  HTTP/1.1 200 OK
  1001  Content-Type: application/vnd.api+json
  1002  ```
  1003  
  1004  ```json
  1005  {
  1006    "data": {
  1007      "type": "io.cozy.notes.url",
  1008      "id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
  1009      "attributes": {
  1010        "note_id": "05781bea244247fb38f2cd50262c07b5",
  1011        "subdomain": "flat",
  1012        "protocol": "https",
  1013        "instance": "alice.cozy.example",
  1014        "sharecode": "543d7eb8149c",
  1015        "public_name": "Bob"
  1016      }
  1017    }
  1018  }
  1019  ```
  1020  
  1021  ### PUT /notes/:id/schema
  1022  
  1023  It can be used to update the schema of the given note.
  1024  
  1025  #### Request
  1026  
  1027  ```http
  1028  PUT /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/schema HTTP/1.1
  1029  Host: alice.example.net
  1030  Content-Type: application/vnd.api+json
  1031  ```
  1032  
  1033  ```json
  1034  {
  1035    "data": {
  1036      "type": "io.cozy.notes.documents",
  1037      "attributes": {
  1038        "schema": {
  1039          "nodes": [
  1040            ["doc", { "content": "block+" }],
  1041            [
  1042              "panel",
  1043              {
  1044                "content": "(paragraph | heading | bulletList | orderedList)+",
  1045                "group": "block",
  1046                "attrs": { "panelType": { "default": "info" } }
  1047              }
  1048            ],
  1049            ["paragraph", { "content": "inline*", "group": "block" }],
  1050            ["blockquote", { "content": "block+", "group": "block" }],
  1051            ["horizontal_rule", { "group": "block" }],
  1052            [
  1053              "heading",
  1054              {
  1055                "content": "inline*",
  1056                "group": "block",
  1057                "attrs": { "level": { "default": 1 } }
  1058              }
  1059            ],
  1060            ["code_block", { "content": "text*", "marks": "", "group": "block" }],
  1061            ["text", { "group": "inline" }],
  1062            [
  1063              "image",
  1064              {
  1065                "group": "inline",
  1066                "inline": true,
  1067                "attrs": { "alt": {}, "src": {}, "title": {} }
  1068              }
  1069            ],
  1070            ["hard_break", { "group": "inline", "inline": true }],
  1071            [
  1072              "ordered_list",
  1073              {
  1074                "content": "list_item+",
  1075                "group": "block",
  1076                "attrs": { "order": { "default": 1 } }
  1077              }
  1078            ],
  1079            ["bullet_list", { "content": "list_item+", "group": "block" }],
  1080            ["list_item", { "content": "paragraph block*" }]
  1081          ],
  1082          "marks": [
  1083            ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
  1084            ["em", {}],
  1085            ["strong", {}],
  1086            ["code", {}]
  1087          ],
  1088          "version": 2,
  1089          "topNode": "doc"
  1090        }
  1091      }
  1092    }
  1093  }
  1094  ```
  1095  
  1096  #### Response
  1097  
  1098  ```http
  1099  HTTP/1.1 200 OK
  1100  Content-Type: application/vnd.api+json
  1101  ```
  1102  
  1103  ```json
  1104  {
  1105    "data": {
  1106      "type": "io.cozy.files",
  1107      "id": "bf0dbdb0-e1ed-0137-8548-543d7eb8149c",
  1108      "meta": {
  1109        "rev": "10-241a3436007b"
  1110      },
  1111      "attributes": {
  1112        "type": "file",
  1113        "name": "My new note.cozy-note",
  1114        "trashed": false,
  1115        "md5sum": "NjhiMzI5ZGE5ODkzZTM0MDk5YzdkOGFkNWNiOWM5NDAgIC0K",
  1116        "created_at": "2019-11-05T12:38:04Z",
  1117        "updated_at": "2019-12-10T14:49:12Z",
  1118        "tags": [],
  1119        "metadata": {
  1120          "title": "My new note",
  1121          "content": { "type": "doc", "content": [{ "type": "paragraph" }] },
  1122          "version": 0,
  1123          "schema": {
  1124            "nodes": [
  1125              ["doc", { "content": "block+" }],
  1126              [
  1127                "panel",
  1128                {
  1129                  "content": "(paragraph | heading | bullet_list | ordered_list)+",
  1130                  "group": "block",
  1131                  "attrs": { "panelType": { "default": "info" } }
  1132                }
  1133              ],
  1134              ["paragraph", { "content": "inline*", "group": "block" }],
  1135              ["blockquote", { "content": "block+", "group": "block" }],
  1136              ["horizontal_rule", { "group": "block" }],
  1137              [
  1138                "heading",
  1139                {
  1140                  "content": "inline*",
  1141                  "group": "block",
  1142                  "attrs": { "level": { "default": 1 } }
  1143                }
  1144              ],
  1145              ["code_block", { "content": "text*", "marks": "", "group": "block" }],
  1146              ["text", { "group": "inline" }],
  1147              [
  1148                "image",
  1149                {
  1150                  "group": "inline",
  1151                  "inline": true,
  1152                  "attrs": { "alt": {}, "src": {}, "title": {} }
  1153                }
  1154              ],
  1155              ["hard_break", { "group": "inline", "inline": true }],
  1156              [
  1157                "ordered_list",
  1158                {
  1159                  "content": "list_item+",
  1160                  "group": "block",
  1161                  "attrs": { "order": { "default": 1 } }
  1162                }
  1163              ],
  1164              ["bullet_list", { "content": "list_item+", "group": "block" }],
  1165              ["list_item", { "content": "paragraph block*" }]
  1166            ],
  1167            "marks": [
  1168              ["link", { "attrs": { "href": {}, "title": {} }, "inclusive": false }],
  1169              ["em", {}],
  1170              ["strong", {}],
  1171              ["code", {}]
  1172            ],
  1173            "version": 2,
  1174            "topNode": "doc"
  1175          }
  1176        },
  1177        "size": 1,
  1178        "executable": false,
  1179        "class": "text",
  1180        "mime": "text/vnd.cozy.note+markdown",
  1181        "cozyMetadata": {
  1182          "doctypeVersion": "1",
  1183          "metadataVersion": 1,
  1184          "createdAt": "2019-11-05T12:38:04Z",
  1185          "createdOn": "https://alice.example.net/",
  1186          "updatedAt": "2020-12-10T14:49:12Z",
  1187          "uploadedAt": "2019-11-05T12:38:04Z",
  1188          "uploadedOn": "https://alice.example.net/"
  1189        }
  1190      },
  1191      "relationships": {
  1192        "parent": {
  1193          "links": {
  1194            "related": "/files/f48d9370-e1ec-0137-8547-543d7eb8149c"
  1195          },
  1196          "data": {
  1197            "type": "io.cozy.files",
  1198            "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"
  1199          }
  1200        }
  1201      }
  1202    }
  1203  }
  1204  ```
  1205  
  1206  ### POST /notes/:id/images
  1207  
  1208  This route can be used to upload an image for a note. The note will be
  1209  transformed in a tar archive in the VFS, with the image saved inside it.
  1210  
  1211  This route can only be used to upload images (the content-type is checked) and
  1212  requires a POST permission on the note.
  1213  
  1214  The filename of the image is given in the query string, via the `Name`
  1215  parameter. In case of conflict (another image has the same name), the stack
  1216  will rename this image.
  1217  
  1218  If the image is larger than 768px, a thumbnail will be generated.
  1219  
  1220  #### Request
  1221  
  1222  ```http
  1223  POST /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/images?Name=diagram.jpg HTTP/1.1
  1224  Accept: application/vnd.api+json
  1225  Content-Length: 123456
  1226  Content-Type: image/jpeg
  1227  Host: cozy.example.com
  1228  <content>
  1229  ```
  1230  
  1231  #### Response
  1232  
  1233  ```http
  1234  HTTP/1.1 201 Created
  1235  Content-Type: application/vnd.api+json
  1236  ```
  1237  
  1238  ```json
  1239  {
  1240    "data": {
  1241      "type": "io.cozy.notes.images",
  1242      "id": "f48d9370-e1ec-0137-8547-543d7eb8149c/e57d2ec0-d281-0139-2bed-543d7eb8149c",
  1243      "meta": {
  1244        "rev": "1-588ab661"
  1245      },
  1246      "attributes": {
  1247        "name": "diagram.jpg",
  1248        "mime": "image/jpeg",
  1249        "width": 1000,
  1250        "height": 1000,
  1251        "willBeResized": true,
  1252        "cozyMetadata": {
  1253          "doctypeVersion": "1",
  1254          "metadataVersion": 1,
  1255          "createdAt": "2021-07-12T10:58:00Z",
  1256          "createdByApp": "notes",
  1257          "createdOn": "https://cozy.example.com/",
  1258          "updatedAt": "2021-07-12T10:58:00Z",
  1259          "uploadedAt": "2021-07-12T10:58:00Z",
  1260          "uploadedOn": "https://cozy.example.com/",
  1261          "uploadedBy": {
  1262            "slug": "notes"
  1263          }
  1264        }
  1265      },
  1266      "links": {
  1267        "self": "/notes/f48d9370-e1ec-0137-8547-543d7eb8149c/images/e57d2ec0-d281-0139-2bed-543d7eb8149c/543d7eb8149c128b"
  1268      }
  1269    }
  1270  }
  1271  ```
  1272  
  1273  ### POST /notes/:id/:image-id/copy
  1274  
  1275  Copy an existing image to another note. It is similar to `POST
  1276  /notes/:id/images` as creating an image, but can be useful to avoid downloading
  1277  and then reuploading the image content when the user makes a copy/paste.
  1278  
  1279  The `:id` and `:image-id` path parameters identify the source image. The
  1280  destination note will be specified in the query-string, as `To`.
  1281  
  1282  #### Query-String
  1283  
  1284  | Parameter  | Description                                       |
  1285  | ---------- | ------------------------------------------------- |
  1286  | To         | the ID of the note where the image will be copied |
  1287  
  1288  #### Request
  1289  
  1290  ```http
  1291  POST /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/e57d2ec0-d281-0139-2bed-543d7eb8149c/copy?To=76ddf590-905e-013c-5ff2-18c04daba326 HTTP/1.1
  1292  Accept: application/vnd.api+json
  1293  Host: cozy.example.com
  1294  ```
  1295  
  1296  #### Response
  1297  
  1298  ```http
  1299  HTTP/1.1 201 Created
  1300  Content-Type: application/vnd.api+json
  1301  ```
  1302  
  1303  ```json
  1304  {
  1305    "data": {
  1306      "type": "io.cozy.notes.images",
  1307      "id": "76ddf590-905e-013c-5ff2-18c04daba326/8d146530-905e-013c-5ff3-98b45e10905e",
  1308      "meta": {
  1309        "rev": "1-18c04dab"
  1310      },
  1311      "attributes": {
  1312        "name": "diagram.jpg",
  1313        "mime": "image/jpeg",
  1314        "width": 1000,
  1315        "height": 1000,
  1316        "willBeResized": true,
  1317        "cozyMetadata": {
  1318          "doctypeVersion": "1",
  1319          "metadataVersion": 1,
  1320          "createdAt": "2024-01-08T15:18:00Z",
  1321          "createdByApp": "notes",
  1322          "createdOn": "https://cozy.example.com/",
  1323          "updatedAt": "2024-01-08T15:18:00Z",
  1324          "uploadedAt": "2024-01-08T15:18:00Z",
  1325          "uploadedOn": "https://cozy.example.com/",
  1326          "uploadedBy": {
  1327            "slug": "notes"
  1328          }
  1329        }
  1330      },
  1331      "links": {
  1332        "self": "/notes/76ddf590-905e-013c-5ff2-18c04daba326/images/8d146530-905e-013c-5ff3-98b45e10905e/d251f620d98e1740"
  1333      }
  1334    }
  1335  }
  1336  ```
  1337  
  1338  ## Real-time via websockets
  1339  
  1340  You can subscribe to the [realtime](realtime.md) API for a document with the
  1341  `io.cozy.notes.events` doctype, and the id of a note file. It requires a permission
  1342  on this file, and it will send the events for this notes: changes of the title, the
  1343  steps applied, the telepointer updates, and images processed.
  1344  
  1345  ### Example
  1346  
  1347  ```
  1348  client > {"method": "AUTH",
  1349            "payload": "xxAppOrAuthTokenxx="}
  1350  client > {"method": "SUBSCRIBE",
  1351            "payload": {"type": "io.cozy.notes.events",
  1352                        "id": "f48d9370-e1ec-0137-8547-543d7eb8149c"}}
  1353  server > {"event": "UPDATED",
  1354            "payload": {"id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
  1355                        "type": "io.cozy.notes.events",
  1356                        "doc": {"doctype": "io.cozy.notes.documents",
  1357                                "sessionID": "543781490137",
  1358                                "title": "this is the new title of this note"}}}
  1359  server > {"event": "CREATED",
  1360            "payload": {"id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
  1361                        "type": "io.cozy.notes.events",
  1362                        "doc": {"doctype": "io.cozy.notes.steps",
  1363                                "sessionID": "543781490137",
  1364                                "version": 6,
  1365                                "stepType": "replace",
  1366                                "from": 1,
  1367                                "to": 1,
  1368                                "slice": {"content": [{"type": "text", "text": "H"}]}}}}
  1369  server > {"event": "UPDATED",
  1370            "payload": {"id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
  1371                        "type": "io.cozy.notes.events",
  1372                        "doc": {"doctype": "io.cozy.notes.telepointers", "sessionID": "543781490137", "anchor": 7, "head": 12, "type": "textSelection"}}}
  1373  server > {"event": "UPDATED",
  1374            "payload": {"id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
  1375                        "type": "io.cozy.notes.events",
  1376                        "doc": {"doctype": "io.cozy.notes.images",
  1377                                "image_id": "e57d2ec0-d281-0139-2bed-543d7eb8149c",
  1378                                "mime": "image/jpeg",
  1379                                "width": 768,
  1380                                "height": 768}}}
  1381  ```