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 ```