Getting Started

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);


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.