Getting Started.

  1. Sample code
  2. Authentication
  3. Data format and error handling
  4. Embedding guide
  5. Custom document viewer page
  6. Tutorial

Sample code

Before starting with the API, you need to have access to a local Annotate installation; see Annotate Server Installation Guide for details or get in touch. You can use the API samples from any machine with web access to your annotate server.

The sample code demonstrates how to use the various API calls from PHP; there is also sample Java source for accessing the API. The test PHP samples (test_createAccount.php, etc) are intended to be run from the command line, and the HTML samples (html_loginAs.php, etc) should be run from a web browser, so it is useful to extract in a web area: e.g.

% cd /var/www/annotate            # ... or your web area
% unzip   # ... extracts to the "api/" subfolder

You should then be able to view the API sample instructions by pointing your web browser somewhere like: http://localhost/annotate/api/index.html For production use, you will want to restrict access to this api directory (e.g. using .htaccess or moving it elsewhere). The rest of this reference guide describes the API security model and lists the available API functions. There is also a short tutorial available here.


Each API request needs to be signed by the api-user using a secret api-key. The api user is the email address of the administrator Annotate account (on standalone installations), or the group admin user (for group accounts on The api key is shown in the advanced section of your Annotate account page.

Requests can be made by the api user to view the documents of a particular annotate user as long as the api user has permission. The request signature is a list of HTTP GET parameters to add to the request, e.g. to list the documents of user '':               # The admin user for the account
    &api-requesttime=123456                # the Unix timestamp (GMT)
    &     # The user's account
    &api-auth=xyz1234543983jeflgnwefgdgd   # The signed hash code.

The request time is the Unix timestamp (seconds since Jan 1st 1970), and requests expire after a few minutes. The api-auth code is a Base-64 encoded HMAC-SHA1 digest of the string:

$phpfn . "\n". $apiuser. "\n". $requesttime. "\n". $annotateuser
signed using the secret api-key. The authentication algorithm is similar to that used for signing Amazon S3 requests, so you may be able to adapt the sample code for your preferred programming language from the Amazon developer site. A PHP function which implements it is shown below: (see the Crypt_HMAC() documentation; this function is included in the sample code.

// ==== Pseudocode =====
  # Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of( StringToSign ) ) );

  // ==== PHP sample code ====
  function signRequest( $phpfn, $apiuser, $apikey, $annotateuser ) {
    $requesttime = time();
    $stringToSign = "$phpfn\n$apiuser\n$requesttime\n$annotateuser";  
    $hasher =& new Crypt_HMAC($apikey, "sha1");
    $signature = hex2b64($hasher->hash($stringToSign));
    return "api-user=".rawurlencode($apiuser).

The authentication mechanism in Java is similar:-

import sun.misc.BASE64Encoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;

// Make a base64 encoded HMAC / SHA1 hash of a string.
public static String sign(String apikey, String data) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1");
    byte[] keyBytes = apikey.getBytes("UTF8");
    SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
    byte[] signBytes = mac.doFinal(data.getBytes("UTF8"));
    String signature = (new BASE64Encoder()).encode(signBytes).replaceAll("\n","");
    return signature; 

public static String signRequest( String phpfn,
                  String apiuser,
                  String apikey,
                  String annotateuser,
                  long validfor)  throws Exception {        
    long requesttime = (new Date()).getTime() + validfor;

    String stringToSign = phpfn + "\n" + apiuser+ "\n"+ requesttime + "\n" + annotateuser;
    String signature = sign(apikey, stringToSign);

    return "api-user=" + URLEncoder.encode(apiuser, "UTF-8") + 
        "&api-requesttime=" + URLEncoder.encode(""+requesttime, "UTF-8") + 
        "&api-annotateuser=" + URLEncoder.encode(annotateuser, "UTF-8") + 
        "&api-auth=" + URLEncoder.encode(signature, "UTF-8");

Data format and error handling

Structured data is returned from requests in Javascript Object Notation (JSON) format. This is a simple language independent data interchange format with libraries for all major languages (see, and is a simpler and more efficient alternative to XML.

If there is an error with the request (e.g. with authentication), the return value from the GET request will be the string "ERR" + an error message, otherwise it will return a valid JSON string. You can test the response value with code like:

    // Sample PHP code for testing the API response
    $req = signRequest("listDocuments.php", "", 
                       "...your-secret-api-key...", "");
    $txt = file_get_contents("".$req);
    if (substr($txt, 0, 3) == "ERR") {
      print "Problem with request: $txt\n":
    else {
      // OK, parse as JSON object:
      $obj = json_decode($txt);

Embedding guide

This section includes instructions for embedding an Annotate panel within your own application. It assumes you have already installed a local copy of Annotate. There is a 'html_sample-proof.php' page included here which demonstrates how to embed an Annotate view of a PDF panel in an iframe.

How it works

The sample at 'api-sample-code/php/html_sample-proof.php' includes an example which you can use as a starting point; the sample is in PHP, but you can use any web scripting language to embed an Annotate panel (ASP, JSP, Perl, Python etc).

The sample first does a HTTP GET to upload a file, then embeds an iframe into the page which points to the API call that will create the user account if necessary, log the user in and display the document. To integrate it with your own system, you would want to pass parameters such as the link to the file and the email of the user who will make the notes. Both requests will need to be signed as indicated in the Authentication section and shown in th sample code.

The files are uploaded to the API user's default workspace. If you need to organise the files in different workspaces, you can supply a project ID or name which represents the workspace the file will be uploaded to.

Step 1: Specify the PDF file to upload

The first step is to get your script to perform a signed HTTP GET request to 'simpleUpload.php', passing the authentication parameters, a URL link to the file to be uploaded and a number of optional parameters. These optional parameters include the option to upload the document to a given workspace, hide certain buttons in the document viewer (list of buttons and codes available here) and disable the URL caching for debugging. The response will be a link to the uploaded document which will be used in the src= link of an iframe, to be embedded in your web page.

// The sample URL below shows the GET parameters.
// *NB* You need to URI encode the values (%40 inplace of @ etc)                   # The admin user for the account
  &api-requesttime=123456                    # the Unix timestamp (GMT)
  &         # The user's account
  &api-auth=xyz1234543983jeflgnwefgdgd       # The signed hash code.

  &url=  # Replace with URL of your file
  &projectid=abc123                          # (optional) specify a workspace for the doc to be uploaded to
  &nocache=0                                 # (optional) set to 1 if you want subsequent GETs to reupload
  &hide="nto-dnl"                            # (optional) list of GUI components to hide, separated by '-'

// Fetch the result from an HTTP GET of the above URL
// The response will contain the plain text string "OK " + a link relative to the uploaded document
OK pdfnotate.php?d=2015-01-01&c=abcdef&ws=123456

// or, if there was a problem uploading the PDF it will return:
ERR {error message}

A note on caching

The 'simpleUpload.php' API call will first check whether the given URL has already been uploaded to the account, and if it has it will return the link immediately without reuploading the PDF. You can change this behaviour (e.g. for debugging) by setting the 'nocache=1' flag.

The 'simpleUpload.php' API call will first check whether the given URL has already been uploaded to the account, and if it has it will return the link immediately without reuploading the PDF. You can change this behaviour (e.g. for debugging) by setting the 'nocache=1' flag.

Step 2: Use the upload's response to build the src= link of an iframe

The src= link of the iframe will point at the 'loginAs.php' API call, which will allow a given user to view the document and make notes. It will first create the user account if it doesn't exist, log the user in and give access to the document by adding the user to the workspace.

The request will be signed as before, with the 'annotateUser' parameter being the email of the user who will be logged in. The 'pdfnotate.php?...' link returned above will also be supplied, along with the parameters that give access to the user and optional parameters for the creation of the account.

// The sample URL below shows the GET parameters.
// *NB* You need to URI encode the values (%40 inplace of @ etc)                   # The admin user for the account
  &api-requesttime=123456                    # the Unix timestamp (GMT)
  &         # The user's account
  &api-auth=xyz1234543983jeflgnwefgdgd       # The signed hash code.

  &loc=pdfnotate.php?d=2015-01-01&c=abcdef   # Link to the doc returned on upload (URL encoded)

  &add=1                                     # Add user to the workspace to access the doc
  &ws=123456                                 # Workspace ID, returned in the first call
  &role=3                                    # Workspace's role ID, permission to annotate

  &create=1                                  # (optional) create account
  &firstname=jill                            # (optional)
  &lastname=smith                            # (optional)
  &sig=jill                                  # (optional) signature used for every note

Custom document viewer page

The link to pdfnotate.php can have optional extra parameters to hide some of the buttons, if you don't need them for your application. To hide particular buttons, add the parameter 'hide' with a list of the things to hide, separated by '-'. The codes for the possible items in the hide string can be found below.

It is also possible to set server-wide hide options, by adding them to the file:

// Hide the home button and the 'sign out' option:
$defaultHide = "gti-sgo";

Full list of codes for hiding GUI components

  gti : Annotate logo (link to document index page)

  pch : Left-hand side bar
  cht : Chat button within the sidebar
  nod : Notes button within the sidebar
  fdt : Notes tab - Filter - date section
  ftg : Notes tab - Filter - tags section
  fau : Notes tab - Filter - authors section
  pod : Thumbnails button within the sidebar

  noa : 'View-only mode' icon (when document closed for new notes)

  flb : File menu
  vwb : View menu
  fht : Freehand button
  wnb : Window menu
  tlm : Tools menu
  zdh : Help button

  stm : Metadata option
  tgs : Document tags option
  dnl : Download option
  prn : Print option
  mtf : Export option
  sha : Share option

  zio : Zoom in/out options
  fwp : Fit width/page options
  rpm : Rotate page option

  fwb : Find option
  bkm : Bookmarks option
  mtc : Clipboard option

  dcb : Document breadcrumbs and title
  dbc : Only document breadcrumbs, show document title

  ntf : Notifications icon
  usr : Username dropdown
  uhm : Home link within username dropdown
  ush : Share with me link within username dropdown
  ust : Settings link within username dropdown
  uso : Sign out link within username dropdown

Note dialog

  msc : mark style chooser (border colour and shape for graphical notes and text annotation type for text annotations, finer tune available, see below)
  ntc : Note To: chooser
  ttr : tag editor
  tgc : tag chooser and editor
  ltb : Link to button
  nrb : Disable note reply button

Note dialog for text highlights

  sl + style id: hide certain text annotation style options

  List of style ids
  h : highlight
  u : underline
  i : insert
  s : strikethrough
  e : edit

  e.g. "slh" for highlight, "slh-slu" for highlight and underline

Setting initial zoom percentage

To set the initial zoom percentage, set initzoom to the percentage. There is a minimum zoom scale of 25% and a maximum of 150%. e.g. to start at 75% -


start as 'fit width'

Opening on a page, a note, or the index view

To open a document at a particular page, or with a particular note highlighted, you can append a fragment to the URL like:

To open on the note index page:
To open on a given page number:
To open on a given page number with a note highlighted:

Setting initial note display style

To set the initial note display style, use the parameter nds to one of 'm' - margin, 'b' - box, 'f' - footer or 'h' - hidden. e.g. to start with margin notes:



This guide covers the essentials of the Annotate API. It demonstrates how to create a workspace, upload a document to it, create user accounts and finally add them to the workspace. Sample code is available here, see 'test_tutorial.php', which is meant to be run from the command line.

Step 1: Creating a workspace

The workspace must be created by a fully licensed user, as only these can create workspaces. For the purpose of this tutorial, the owner will be the api-user, so his email will be supplied as the annotate-user parameter. The POST request looks as follows:               # The admin user for the account
    &api-requesttime=123456                # the Unix timestamp (GMT)
    &      # The user's account
    &api-auth=xyz1234543983jeflgnwefgdgd   # The signed hash code.

HTTP POST parameters:
    &name=Test workspace                   # the name for the new workspace

On success, the response will look like "OK 115500865". This is the workspace ID which must be supplied when uploading documents to Annotate.

Step 2: Uploading a document to the newly created workspace

The annotate-user supplied must have uploading permissions within that workspace. Permissions are controlled through workspace roles. We will use the api-user, which was used to create the workspace and therefore has all permissions. You can grant all permissions to additional workspace members by assigning them the role 'Admin' within that workspace.

There are several ways to upload a document, as described in the Upload document section. We will supply a URL of a sample document.

<form method='POST' action='uploadDocument.php?

     <input name='ws' value='115500865'>                       # the workspace ID

     <input name='url' value=''>
     <input name='urlfilename' value='sample.pdf'>             # (optional) filename to use

On success, the response will look like "OK 2016-09-30 mdfZ0n1v". The date and code pair will uniquely identify the document. A list of documents uploaded to a given workspace can be retrieved using listDocuments.php

Step 3: Creating a user account

User accounts on your local Annotate server can be fully licensed or annotating users, the former of which are able to create workspaces and upload documents.

We will use createAccount.php, which will create an annotating user account. The user can be upgraded using updateAccount.php.               # The admin user for the account
    &api-requesttime=123456                # the Unix timestamp (GMT)
    &   # The user's account
    &api-auth=xyz1234543983jeflgnwefgdgd   # The signed hash code.

On success, the response will look like "OK - created account for".

Step 4: Adding new user to the workspace

Only workspace members are able to access the documents within it. A user can be added using apiUpdateWorkspaceUsers.php. Besides the workspace ID, it is necessary to supply the role ID which will be assigned to the user and will determine his permissions. A list of workspace roles can be retrieved using apiGetWorkspaceDetails.php.               # the admin user for the account
    &api-requesttime=123456                # the Unix timestamp (GMT)
    &      # the api-annotateuser has to match the apiuser
    &api-auth=xyz1234543983jeflgnwefgdgd   # the signed hash code.

HTTP POST parameters:
    &ws=115500865                      # the workspace to be updated
    &action=add                            # the action to be performed
    &              # the user's email
    &roleid=3                              # workspace role id, see apiGetWorkspaceDetails.php

On success, the response will look like "OK 2 added". "2" is the ID of the user within the workspace.

Note: Steps 3 and 4 can also be done on the fly using loginAs.php.