Annotations

Annotations are stored in the JSON annotation format on the server. PSPDFKit Server provides the following endpoints for working with annotations:


Fetching Document Annotations

To fetch all annotations in a given document, the following endpoint is provided. It can return a response in a few different formats based on the Accept header included in the request.

When Accept: application/x-ndjson is used (recommended), the response will be returned as Newline delimited JSON, allowing the client to process annotations individually or in batches before the complete response body has been received:

Request

1
2
3
GET /api/documents/:document_id/annotations
Accept: application/x-ndjson
Authorization: Token token="<secret token>"
Copy
1
2
3
$ curl http://localhost:5000/api/documents/abc/annotations \
   -H "Accept: application/x-ndjson" \
   -H "Authorization: Token token=<secret token>"

Response

Copy
1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: application/x-ndjson

{"id": "01BS98XZSCFV5QARF948FZWNG5", "content": {...}, "createdBy": "alice", "updatedBy": "bob"}
{"id": "01BS98Y0A0YDX4A54K04NQPQ6T", "content": {...}, "createdBy": "alice", "updatedBy": "bob"}
{"id": "01BS98Y0QFZF5K044JVMDD7W0T", "content": {...}, "createdBy": "alice", "updatedBy": "bob"}
...

When Accept: application/json is used, only the first 1,000 annotations are returned. If the document has more than 1,000 annotations, "truncated": true will be included in the response data:

Request

1
2
3
GET /api/documents/:document_id/annotations
Accept: application/json
Authorization: Token token="<secret token>"
Copy
1
2
3
$ curl http://localhost:5000/api/documents/abc/annotations \
   -H "Accept: application/json" \
   -H "Authorization: Token token=<secret token>"

Response

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

{
  "data": {
    "annotations": [
      {"id": "01BS98XZSCFV5QARF948FZWNG5", "content": {...}, "createdBy": "alice", "updatedBy": "bob"},
      {"id": "01BS98Y0A0YDX4A54K04NQPQ6T", "content": {...}, "createdBy": "alice", "updatedBy": "bob"},
      {"id": "01BS98Y0QFZF5K044JVMDD7W0T", "content": {...}, "createdBy": "alice", "updatedBy": "bob"},
      ...
    ],
    "truncated": true
  }
}

Each annotation includes the user_ids of both the person who created it and the person who last modified it. For annotations created or updated in the browser, the user_id is extracted from the JWT used for authentication.

Fetching Page Annotations

To fetch annotations for a specific page, the following endpoint is provided. It can return a response in a few different formats based on the Accept header included in the request. See the previous section for more details:

Request

1
2
3
GET /api/documents/:document_id/pages/:page_index/annotations
Accept: application/x-ndjson
Authorization: Token token="<secret token>"
Copy
1
2
3
$ curl http://localhost:5000/api/documents/abc/pages/1/annotations \
   -H "Accept: application/x-ndjson" \
   -H "Authorization: Token token=<secret token>"

Response

Copy
1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: application/x-ndjson

{"id": "01BS98XZSCFV5QARF948FZWNG5", "content": {...}, "createdBy": "alice", "updatedBy": "bob"}
{"id": "01BS98Y0A0YDX4A54K04NQPQ6T", "content": {...}, "createdBy": "alice", "updatedBy": "bob"}
{"id": "01BS98Y0QFZF5K044JVMDD7W0T", "content": {...}, "createdBy": "alice", "updatedBy": "bob"}
...

Creating an Annotation

To create a new annotation, send a POST request with a JSON body containing the annotation’s contents to /api/documents/:document_id/annotations. You need to specify the application/json content-type header; optionally, you should also specify an annotation_id and/or user_id. If you don’t specify the annotation_id, a ULID will be generated and returned in the response.

The content property expects a JSON object in our JSON annotation format:

Request

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /api/documents/:document_id/annotations
Content-Type: application/json
Authorization: Token token="<secret token>"
PSPDFKit-API-Version: 2017.7

{
  "id": "01BS98XZSCFV5QARF948FZWNG5",
  "user_id": "bob"
  "content": {
    "bbox": [146.89599609375, 383.48397827148438, 24, 24],
    "opacity": 1,
    "pageIndex": 1,
    "text": "This is a yellow note annotation",
    "type": "pspdfkit/note",
    "v": 1
  }
}
Copy
1
2
3
4
5
$ curl http://localhost:5000/api/documents/abc/annotations \
   -X POST \
   -H "Authorization: Token token=<secret token>" \
   -H "PSPDFKit-API-Version: 2017.7" \
   -d '{"id": "01BS98XZSCFV5QARF948FZWNG5", "user_id": "bob", "content":{"bbox": [146, 383, 24, 24], ...}}'

Response

The server will validate the content and return an HTTP response with the status 200 and the following JSON payload in the case of success:

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "annotation_id": "01BS98XZSCFV5QARF948FZWNG5"
  }
}

If there’s a validation error, the following response will be returned:

1
2
3
4
5
6
7
8
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "reason": "<error description>"
  }
}

Creating an Annotation with an Attachment

To create a new annotation with an attachment, send a multipart/form-data POST request and the JSON annotation file containing the annotation’s contents to /api/documents/:document_id/annotations. You need to specify the multipart/form-data content-type header. For each attachment that is referenced in the annotation and is not already uploaded to the server, you have to attach the attachment to the request as additional form data, where the name is the SHA256 of the attachment data:

Request

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
POST /api/documents/:document_id/annotations
Content-Type: multipart/form-data
Authorization: Token token="<secret token>"
PSPDFKit-API-Version: 2017.7

--customboundary
Content-Disposition: form-data; name="annotation"; filename="annotation.json"
Content-Type: application/json
{
  "content": {
    "bbox": [100,100,100,100],
    "opacity": 1,
    "pageIndex": 0,
    "type": "pspdfkit/image",
    "contentType":"image/png",
    "imageAttachmentId": "ffe23a235e3b733a498376f869292110e6663473712373ea6b4c0b02b469583d",
    "v": 1
  }
}

--customboundary
Content-Disposition: form-data; name="ffe23a235e3b733a498376f869292110e6663473712373ea6b4c0b02b469583d"; filename="example.png"
Content-Type: image/png

<Attachment data>
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ curl -XPOST http://localhost:5000/api/documents/abc/annotations \
   -X POST \
   -H pspdfkit-api-version\:\ 2017.7 \
   -H Authorization\:\ Token\ token\=secret \
   -H Content-Type\:\ multipart/form-data \
   -F annotation=" \
      { \
        \"content\": { \
          \"v\":1, \
          \"bbox\":[100,100,100,100], \
          \"contentType\":\"image/png\", \
          \"imageAttachmentId\": \"ffe23a235e3b733a498376f869292110e6663473712373ea6b4c0b02b469583d\", \
          \"opacity\":1, \
          \"pageIndex\":0, \
          \"type\":\"pspdfkit/image\" \
        } \
      }" \
   -F ffe23a235e3b733a498376f869292110e6663473712373ea6b4c0b02b469583d=@example.png

Response

The server will validate the content of the attachment and return an HTTP response with the status 200 and the following JSON payload in the case of success:

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "annotation_id": "01BS98XZSCFV5QARF948FZWNG5"
  }
}

If there’s a validation error, the following response will be returned:

1
2
3
4
5
6
7
8
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "reason": "<error description>"
  }
}

Updating an Annotation

To update an existing annotation, send a PUT request with a JSON body containing the new contents — and optionally, a user_id — to /api/documents/:document_id/annotations/:annotation_id, specifying the application/json content type.

The content property expects a JSON object in our JSON annotation format:

Request

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PUT /api/documents/:document_id/annotations/:annotation_id
Content-Type: application/json
Authorization: Token token="<secret token>"
PSPDFKit-API-Version: 2017.7

{
  "id": "01BS98XZSCFV5QARF948FZWNG5",
  "user_id": "alice"
  "content": {
    "bbox": [146.89599609375, 383.48397827148438, 24, 24],
    "opacity": 1,
    "pageIndex": 1,
    "text": "This text has been updated.",
    "type": "pspdfkit/note",
    "v": 1
  }
}
Copy
1
2
3
4
5
$ curl http://localhost:5000/api/documents/abc/annotations/01BS98XZSCFV5QARF948FZWNG5 \
   -X PUT \
   -H "Authorization: Token token=<secret token>" \
   -H "PSPDFKit-API-Version: 2017.7" \
   -d '{"id": "01BS98XZSCFV5QARF948FZWNG5", "user_id": "alice", "content": {"bbox": [146, 383, 24, 24], ...}}'

Response

The server will validate the content and return an HTTP response with the status 200 and an empty body in the case of success.

If there’s a validation error, the following response will be returned:

1
2
3
4
5
6
7
8
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "reason": "<error description>"
  }
}

Updating an Annotation with an Attachment

To update an existing annotation and also update its attachment, send a multipart/form-data PUT request and the JSON annotation file containing the new contents — and optionally, a user_id — to /api/documents/:document_id/annotations/:annotation_id, specifying the multipart/form-data content type. For each attachment that is referenced in the annotation and is not already uploaded to the server, you have to attach the attachment to the request as additional form data, where the name is the SHA256 hash encoded attachment data:

Request

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
PUT /api/documents/:document_id/annotations/:annotation_id
Content-Type: multipart/form-data
Authorization: Token token="<secret token>"
PSPDFKit-API-Version: 2017.7

--customboundary
Content-Disposition: form-data; name="annotation"; filename="annotation.json"
Content-Type: application/json
{
  "user_id": "adsf",
  "content": {
    "v":1,
    "bbox":[100,100,382,286],
    "contentType":"image/png",
    "imageAttachmentId": "0c76f21f309939d871362e080b4160634ff06bc07076b7940c42a53f89a99e0c",
    "opacity":1,
    "pageIndex":0,
    "type":"pspdfkit/image"
  }
}

--customboundary
Content-Disposition: form-data; name="0c76f21f309939d871362e080b4160634ff06bc07076b7940c42a53f89a99e0c"; filename="example.png"
Content-Type: image/png

<Attachment data>
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ curl http\://127.0.0.1\:5000/api/documents/1/annotations/01CNGKC7BRB7HR8THJJ5K66XGX \
   -X PUT \
   -H accept\:\ application/json \
   -H pspdfkit-api-version\:\ 2017.7 \
   -H Authorization\:\ Token\ token\=secret \
   -H Content-Type\:\ multipart/form-data \
   -F annotation=" \
      { \
        \"user_id\": \"adsf\", \
        \"content\": { \
          \"v\":1, \
          \"bbox\":[100,100,382,286], \
          \"contentType\":\"image/png\", \
          \"imageAttachmentId\": \"0c76f21f309939d871362e080b4160634ff06bc07076b7940c42a53f89a99e0c\", \
          \"opacity\":1, \
          \"pageIndex\":0, \
          \"type\":\"pspdfkit/image\" \
        } \
      } \
   " \
   -F 0c76f21f309939d871362e080b4160634ff06bc07076b7940c42a53f89a99e0c=@/Users/max/Desktop/challenger-deep.png

Response

The server will validate the content and return an HTTP response with the status 200 and an empty body in the case of success.

If there’s a validation error, the following response will be returned:

1
2
3
4
5
6
7
8
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "reason": "<error description>"
  }
}

Retrieving an Annotation

To retrieve an existing annotation, send a GET request to /api/documents/:document_id/annotations/:annotation_id:

Request

1
2
GET /api/documents/:document_id/annotations/:annotation_id
Authorization: Token token="<secret token>"
1
2
$ curl http://localhost:5000/api/documents/abc/annotations/01BS98XZSCFV5QARF948FZWNG5 \
   -H "Authorization: Token token=<secret token>"

Response

The server will return the annotation in the response as JSON data:

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

{
  "id": "01BS98XZSCFV5QARF948FZWNG5",
  "createdBy": "alice"
  "updatedBy": "alice"
  "content": {
    "bbox": [146.89599609375, 383.48397827148438, 24, 24],
    "opacity": 1,
    "pageIndex": 1,
    "text": "Example text",
    "type": "pspdfkit/note",
    "v": 1
  }
}

When no annotation with the requested annotation_id exists, the server responds with the following:

1
HTTP/1.1 404 Not Found

Deleting an Annotation

To delete an existing annotation, send a DELETE request to /api/documents/:document_id/annotations/:annotation_id:

Request

1
2
DELETE /api/documents/:document_id/annotations/:annotation_id
Authorization: Token token="<secret token>"
Copy
1
2
3
$ curl http://localhost:5000/api/documents/abc/annotations/01BS98XZSCFV5QARF948FZWNG5 \
   -X DELETE
   -H "Authorization: Token token=<secret token>"

Response

The server will return an HTTP response with the status 200 and an empty body.

Accessing Password-Protected Documents

To access password-protected documents via the upstream API, you need to include the pspdfkit-pdf-password header in all requests that work on a password-protected document.

API Versioning

Our current API version is 2017.7. You can specify which API version you want to use by setting the pspdfkit-api-version header. Currently supported versions are 2017.7 (default) and 2017.6. See the documentation of previous versions for details on how to use old APIs.

Using the Annotation API with Layers

All the endpoints described above work on the default layer of a document and can also be used on a specific layer in a document by replacing documents/:document_id with documents/:document_id/layers/:layer_id in the URL of the endpoint. For more information on layers and how to use them, take a look at Instant Layers.