Instant Comments

Since version 2020.1, PSPDFKit for Web and PSPDFKit Server allow users to collaborate on uploaded documents in real time by adding and viewing comments.

PSPDFKit Server enables automatic comment synchronization powered by our Instant engine. It also provides a set of server-to-server APIs that you can use to create and fetch comments in documents and individual Instant layers. You can use these APIs to import entire comment threads from your existing systems.

Any comments added through the APIs are instantly picked up by the PSPDFKit for Web viewer, and any comments users create through the UI can be retrieved using the API.

This guide covers the following:

For a more general overview of this feature, see our Introduction to Instant Comments guide for PSPDFKit for Web.

Data Model

Each comment is attached to a root annotation. Comments under the same root annotation form a single comment thread. The root annotation serves as an anchor for comments, marking their position in a document. Currently, only comment marker annotations and markup annotations can be used as root annotations.

Working with Root Annotations

Root annotations are regular annotations stored on PSPDFKit Server. They can be fetched, created, updated, and deleted using our server-to-server annotation APIs. However, there are a couple rules which apply only to root annotations:

  • When they’re deleted, all comments attached to them are deleted as well.
  • When the last comment under the root annotation is deleted, the annotation itself is removed.

Comment Markers

pspdfkit/comment-marker is an annotation type used for marking a comment thread’s position in the document. It can be manipulated using the annotation APIs, just like any other annotation can. However, it’s only available if the Instant Comments feature is enabled in the license.

Adding Comments to an Existing Root Annotation

You can use the following API to add multiple comments to the existing root annotation.

Request

Send a POST request to /api/documents/:document_id/annotations/:annotation_id/comments with a payload containing the comments you want to add. This request adds comments to the annotation in the document’s default layer. To target a named layer, use the /api/documents/:document_id/layers/:layer_name/annotations/:annotation_id/comments path:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
POST /api/documents/my-document/annotations/my-annotation/comments HTTP/1.1
Content-Type: application/json
Authorization: Token token=<secret token>

{
  "comments": [
    {
      "id": "my-comment",
      "user_id": "user-1",
      "content": {
        "text": "I like it!",
        "creatorName": "Jane Doe",
        "createdAt": "2020-01-17T19:17:36.579151Z",
        "updatedAt": "2020-01-17T19:28:36.579151Z",
        "customData": { "key": "value" }
      }
    },
    {
      "content": {
        "text": "Thanks! I appreciate it."
      }
    }
  ]
}
Copy
1
2
3
4
curl -X POST http://localhost:5000/api/documents/my-document/annotations/my-annotation \
  -H "Content-Type: application/json"
  -H "Authorization: Token token=<secret token>"
  -d '{"comments": [{"content": {"text": "I like it!", ...}}, ...]}'

If you don’t provide an id for a comment, the ID will be autogenerated by the server. The user_id is optional — it sets the createdBy attribute of the retrieved comments and will be null if omitted. creatorName, createdAt, updatedAt, and customData properties are optional as well.

Response

On success, the server responds with the status 200 and an array of newly created comment IDs:

1
2
3
4
HTTP/1.1 200 OK
Content-Type: application/json

{"data": {"comments": [{"id": "7KPV2Z667S9V2CYNHZAA5P6984"}, {"id": "my-comment"}]}}

If the selected root annotation doesn’t have a correct type (i.e. it’s neither a comment marker nor a markup annotation), the server responds with the status 400. If the root annotation with a given ID doesn’t exist in the specified document and layer, the 404 response is returned. If any of the comment parameters are invalid, the response status code is 422.

Adding Comments along with the Root Annotation

You can leverage the following API to create an entire comment thread at once — i.e. a root annotation together with multiple comments attached to it.

Request

Send a POST request to /api/documents/:document_id/comments with a body containing a root annotation and comments. If you want to add the comment thread in the named layer, send a request to the /api/documents/:document_id/layers/:layer_name/comments path:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
POST /api/documents/my-document/comments HTTP/1.1
Content-Type: application/json
Authorization: Token token=<secret token>

{
  "annotation": {
    "id": "my-annotation",
    "content": {
      "v": 1,
      "type": "pspdfkit/comment-marker",
      "opacity": 1,
      "bbox": [100, 100, 50, 50],
      "pageIndex": 0,
      "createdAt": "2020-01-17T19:08:22.741791Z",
      "updatedAt": "2020-01-17T19:08:22.741791Z"
    }
  },
  "comments": [
    {
      "id": "my-comment",
      "user_id": "user-1",
      "content": {
        "text": "I like it!",
        "creatorName": "Jane Doe",
        "createdAt": "2020-01-17T19:17:36.579151Z",
        "updatedAt": "2020-01-17T19:28:36.579151Z",
        "customData": {
          "key": "value"
        }
      }
    },
    {
      "content": {
        "text": "Thanks! I appreciate it."
      }
    }
  ]
}
Copy
1
2
3
4
curl -X POST http://localhost:5000/api/documents/my-document/annotations/my-annotation \
  -H "Content-Type: application/json"
  -H "Authorization: Token token=<secret token>"
  -d '{"annotation": {"content": {"bbox": [100, 100, 50, 50], ...}}, "comments": [...]}'

The annotation data needs to be the same as it would be if the annotation were created using the annotation APIs. If the annotation id is not supplied, it will be generated by the server.

Response

When the request is successful, the server returns the IDs of the created root annotation and comments:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "annotation": {
      "id": "my-annotation"
    },
    "comments": [
      {
        "id": "7KQ0ZASA8RRB0FXBEGXVPF43YH"
      },
      {
        "id": "my-comment"
      }
    ]
  }
}

If the annotation doesn’t have a correct type (i.e. it’s neither a comment marker nor a markup annotation), the server responds with the status 400. If any of the annotation or comment parameters are invalid, the response status code is 422.

Retrieving Comments under the Root Annotation

The following endpoint can be used to retrieve all comments on the given root annotation.

Request

Send a GET request to /api/documents/:document_id/annotations/:annotation_id/comments to fetch comments under the root annotation from the default layer, or use the /api/documents/:document_id/layer/:layer_name/annotations/:annotation_id/comments path to target a named layer:

1
2
3
GET /api/documents/my-document/annotations/my-annotation/comments
Accept: application/json
Authorization: Token token=<secret token>
Copy
1
2
3
curl http://localhost:5000/api/documents/my-document/annotations/my-annotation/comments \
  -H "Content-Type: application/json" \
  -H "Authorization: Token token=secret"

Response

On success, the server returns all the comments attached to the specified root annotation:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "comments": [
      {
        "content": {
          "createdAt": "2020-01-17T19:17:36.579Z",
          "creatorName": "Jane Doe",
          "customData": {
            "key": "value"
          },
          "text": "I like it!",
          "updatedAt": "2020-01-17T19:28:36.579Z"
        },
        "createdBy": "user-1",
        "id": "my-comment",
        "updatedBy": null
      },
      {
        "content": {
          "createdAt": "2020-01-17T19:23:27.078Z",
          "creatorName": null,
          "customData": null,
          "text": "Thanks! I appreciate it.",
          "updatedAt": null
        },
        "createdBy": null,
        "id": "7KPV2Z667S9V2CYNHZAA5P6984",
        "updatedBy": null
      }
    ]
  }
}

Just like how when comments are created, if the selected root annotation doesn’t have a correct type (i.e. it’s neither a comment marker nor a markup annotation), the server responds with the status 400. If the root annotation with a given ID doesn’t exist in the specified document and layer, the response status code is 404.

Importing and Exporting

When you download a document’s or layer’s PDF file, the comments will be visible in compatible PDF viewers. If you upload the exported file again, the comments will be extracted from it automatically and available in PSPDFKit Server and the Web viewer.

Currently, it is not possible to export or import comments using Instant JSON files.