Free trial

Overview

The ProWorkflow API is built on HTTPS and RESTful principles. This means it:

  • Uses resource-oriented URLs.
  • Uses standard HTTP verbs to View/Add/Edit/Delete data.
  • Uses standard HTTP methods to pass parameters and authentication.
  • Responds with standard HTTP response codes to indicate success or errors.
  • Accepts and returns JSON.

We have provided documentation for all available calls but before you dive straight in please take a few minutes to read this Introduction section and the Getting Started Guide. We'll keep it short and we promise it'll be time well spent!

URL

All API URLs begin with the following base:

https://api.proworkflow.net

HTTPS is mandatory, requests using HTTP will be rejected.

Authentication

The API uses TWO mandatory authentication mechanisms, you must provide the API key for your account AND your username/password in a Basic Authorization header (you may also substitute your email address for your username).

Your API Key can be supplied as either a URL or body parameter or via an "apikey" header (we recommend the latter method).

The example below shows a simple CURL request using both methods.

URL Parameter:
curl --user name:password https://api.proworkflow.net/contacts?apikey=11111-22222-33333-44444-55555

Request Header:
curl --user name:password --header "apikey: 11111-22222-33333-44444-55555" https://api.proworkflow.net/contacts


If you don't know what your API key is then it can be found in your Client Area or by submitting an authenticated GET request to the login call with your account URL as the "url" parameter:

https://api.proworkflow.net/login?url= YOURACCOUNTURL

If this call does not return the API Key then please contact the account owner as they have chosen not to make it available.

Permissions

A user using the API has the same permissions they would have in the main ProWorkflow application. If you have the relevant permissions then you can check permissions for another user in advance and show/hide sections of your application accordingly but you should ensure you allow for a user's permissions being changed (or login access being revoked) whilst your application is being used and respond appropriately if a request receives a 401 or 403 response.

The 'View Work' permissions from the main application are supported so a user can only view items in the API if they could view them in the main application. For accounts using the Advanced plan, users with the relevant permissions can view items from another Division but the API does not support adding/editing/deleting items in another Division.

JSON

The API returns JSON for all responses and requires JSON for all POST & PUT requests. Data must be appropriately escaped and stringified before being sent and you will usually want to parse and unescape any responses before using them.

There are a wide range of libraries available to handle both of these tasks with minimal effort so we recommend using one of those unless you have no alternative (or just enjoy that type of thing).

IDs

Each item in a ProWorkflow account is identified by an ID number which is used to uniquely identify the item no matter what changes might be made to its name, description or other details.

IDs will always be returned when you request a list of items.

When requesting a single item or updating one via the API, you do so by specifying the ID of the item as part of the request URL.

When you add an item, the API will return the ID of the new item.

Me

In many calls you can use the value "me" rather than the contact ID of the user making the request. This can be very useful when adding time records, completing tasks or assigning work. Refer to the documentation for specific calls for more information.

Dates

We use ISO8601 date/time format in all returned data.

For data/parameters sent to the API we require ISO8601 when a date and time is needed; when a date is required then we accept a YYYY-MM-DD format (ISO8601 without the time component).

In most calls (refer to specific documentation) you may specify a relative date instead, this takes the format +/-Xd/w/m/y corresponding to X days/weeks/months/years forwards (positive number) or backwards (negative number).

The example below shows the 3 most common ways to send a date or date/time:

Date/Time:
2014-05-14T12:55:42

Date:
2014-05-14

Relative Dates:
+3w (3 weeks from today)
-2d (2 days ago)

Rate Limiting

You are limited to 500 requests in a 30 second period for a single API Key, if you exceed this limit you will receive a 429 Response Code.

Every request you make will return X-RateLimit headers indicating the limit, remaining requests and the time until your limit is reset.

X-RateLimit-Limit:500
X-RateLimit-Remaining:477
X-RateLimit-Reset:30

If you think you have a genuine need to exceed this limit please contact us.

Record Limits

The API will return a maximum of 5,000 records, if your request exceeds this limit then only the first 5,000 records will be returned. We strongly suggest you design your application and requests in such a way that you will not come close to this limit. For optimal performance you should plan your requests to return no more than 1,000 records and typically no more than 500.

If your usage of the API is impacting on response times for other customers then we reserve the right to limit or remove your access to the API (we would, of course, contact you first to try to avoid taking that step).

You can limit the number of items returned and improve performance by using the paging options and we encourage you to use these options to deliver a better experience to your users.

Errors

The API returns standard HTTP response codes indicating success/errors, in most cases there will also be a Details object/array that includes more information.

  • 200 - Success (View/Edit/Delete Calls)
  • 201 - Success (Add Calls, ID/s of the new item/s will be returned)
  • 304 - Not Modified (When using If-None-Match header)
  • 400 - Bad Request - Further Details will be returned
  • 401 - Unauthorized - API key or username/password invalid
  • 403 - Forbidden - Permissions don't allow this request
  • 404 - Not Found - The requested item does not exist
  • 429 - Request Limit Exceeded
  • 500 - Server Error - Something went wrong at our end (let us know!)

Getting Started

So you've read the Introduction (if not then please do that first, we'll wait for you) and want to start building something. In this section we'll run over some useful tips then explain the core concepts for making each type of request.

Planning

10 minutes of planning can save you 10 hours of work later so even if you're only aiming to build a simple app, we suggest taking time to work out what data you will need to request and what data you may need to add/edit/delete.

Once that's done you'll be able to work out what calls you need, what filters to use and whether/how you might use the more advanced features like ETags, Web Hooks or Custom Fields (all covered in the Diving Deeper section).

If you need help at this stage then please contact us as we'd love to hear your plans and help make them a reality.

Testing

If you're relatively new to using APIs (and even if you're not) then these tools can be useful for testing your requests and troubleshooting if your requests are rejected or not working as you had expected.

  • Postman - A Google Chrome Extension for making HTTP/S requests from your browser
  • RequestBin - Inspect HTTP requests, useful to test why your requests are being rejected
  • JSONLint - Validates JSON data, useful to test why your POST/PUT data is rejected
  • ProWorkflow Support - Need a hand? Just let us know!

Basics

Every normal* request to the API must include your API Key and a Basic Authorization header comprised of your username and password.

The API uses JSON, you must set the "Content-Type" header as "application/json" when making POST or PUT requests.

* The "login" call also accepts an account URL as the "url" parameter instead of your API key. The "looknfeel" call requires only an API key, this allows you to adjust the look of your application before authenticating a user.

GET (View)

A GET request is used to return a list of items or a single item.

When requesting a list you can specify the fields to return for each item as well as using one or more filters to return only the data you need. Most calls have default filters (for example, the projects call defaults to only active projects) so check the documentation and use filters to ensure you get the data you're expecting.

Additionally you can specify a sort order and paging options for most calls which can be very helpful when you want to display only a small subset of a large dataset.

Let's look at some simple examples, for ease of reading we only show the request URL and response data, don't forget to include your API key and Basic Authorization headers if you want to try these for yourself.

List

Fetch Contacts:

REQUEST URL:
https://api.proworkflow.net/contacts

RESPONSE:
{
    "contacts":[
      {
          "companyid": 29,
          "companyname": "ABC Media",
          "firstname": "Amy",
          "id": 504,
          "lastname": "West",
          "type": "client"
      },
      ...
      ...
      ...
      {
          "companyid": 6,
          "companyname": "Adventure Rafting",
          "firstname": "Warren",
          "id": 375,
          "lastname": "Drake",
          "type": "client"
      }
   ],
    "count": 80,
    "status": "Success"
}

We submit a GET request to the "/contacts" URL, we did not provide any filters so we get all active contacts. We did not provide the "fields" parameter so we get the default fields for contacts (refer to the documentation for each call for details on default fields).

The returned data includes a status and count field and a contacts array containing each of the returned contacts. A list call will ALWAYS return an array of items even if there is only one item, the array will have the name of the collection at the end of the URL (in the example above this is called 'contacts').

Some calls (notably invoices, quotes & time) will also return a total field or array that contains the total value of the items.

Search (a simple Filter)

Fetch Contacts with a name like "warren":

REQUEST URL:
https://api.proworkflow.net/contacts?searchname=warren

RESPONSE:
{
    "contacts":[
      {
          "companyid": 29,
          "companyname": "ABC Media",
          "firstname": "Warren",
          "id": 504,
          "lastname": "Dinner",
          "type": "client"
      },
      {
          "companyid": 6,
          "companyname": "Adventure Rafting",
          "firstname": "Warren",
          "id": 375,
          "lastname": "Drake",
          "type": "client"
      }
   ],
    "count": 2,
    "status": "Success"
}

We submit a GET request to the "/contacts" URL using the "searchname" parameter to return only the contacts with a name like "warren" (note that search parameters are case-insensitive).

As we did not provide the "fields" parameter we get the default fields for contacts (refer to the documentation for each call for details on default fields).

Fields & Paging

Fetch Email Addresses for the first 5 Contacts:

REQUEST URL:
https://api.proworkflow.net/contacts?fields=email&pagesize=5&pagenumber=1

RESPONSE:
{
    "contacts":[
      {
          "email": "noreply@proworkflow.com",
          "id": 67
      },
      {
          "email": "",
          "id": 503
      },
      {
          "email": "noreply@proworkflow.com",
          "id": 456
      },
      {
          "email": "",
          "id": 501
      },
      {
          "email": "noreply@proworkflow.com",
          "id": 470
      }
   ],
    "count": 5,
    "status": "Success"
}

We submit a GET request to the "/contacts" URL using the "fields" parameter to return only email addresses (note that we always get the ID field). We also use the "pagesize" and "pagenumber" fields to return only the first 5 results.

Sorting

Fetch Names of the first 5 Contacts ordered Z-A by First Name:

REQUEST URL:
https://api.proworkflow.net/contacts?fields=name&pagesize=5&pagenumber=1&sortby=firstname&sortorder=desc

RESPONSE:
{
    "contacts":[
      {
          "firstname": "Zelda",
          "lastname": "Thorn",
          "id": 67
      },
      {
          "firstname": "Yasmin",
          "lastname": "Smith",
          "id": 503
      },
      {
          "firstname": "Willam",
          "lastname": "Harris",
          "id": 456
      },
      {
          "firstname": "Wallace",
          "lastname": "Link",
          "id": 501
      },
      {
          "firstname": "Timothy",
          "lastname": "Jones",
          "id": 470
      }
   ],
    "count": 5,
    "status": "Success"
}

We submit a GET request to the "/contacts" URL using the "fields" parameter to return only names and request only the first 5 results. This time we sort the results by First Name in reverse order (refer to the documentation for each call for details on available sort fields).

Single Item

Fetch a single Contact:

REQUEST URL:
https://api.proworkflow.net/contacts/504

RESPONSE:
{
    "contact":
      {
       "address1": "",
       "address2": "",
       "address3": "",
       "allowlogin": false,
       "apifields": [],
       "city": "",
       "companyid": 29,
       "companyname": "ABC Media",
       "country": "",
       "divisionid": 1,
       "email": "",
       "firstname": "Warren",
       "groups": [],
       "id": 504,
       "image": "",
       "lastmodified": "2014-05-04T16:50:36",
       "lastname": "Dinner",
       "mobilephone": "",
       "notes": [],
       "pending": false,
       "state": "",
       "tags": [],
       "teams": [],
       "title": "",
       "type": "client",
       "username": "",
       "workphone": "",
       "zipcode": ""
   },
    "count": 1,
    "status": "Success"
}

We request a single contact with ID 504, this returns all available fields for the contact.

Similar to the list call, we get a status and count field but unlike list calls, a single item call does NOT return an array of returned data but instead we get an object with the singular form of the collection name (in the example above this is called 'contact').

Subtotals

We've added an extra feature for the invoice, quote and time calls that lets you request subtotals for several criteria. This can be very useful when you are interested in total values rather than individual records, here is a simple example:

Fetch Time Subtotals for each Company:

REQUEST URL:
https://api.proworkflow.net/time?trackedfrom=2014-01-01&trackedto=2014-01-31&subtotals=company

RESPONSE:
{
    "status": "Success",
    "subtotals":[
      {
          "companyid": 393,
          "companyname": "0800 Flowers",
          "subtotal": 200
      },
      {
          "companyid": 1,
          "companyname": "Advanced Agency",
          "subtotal": 80
      },
      {
          "companyid": 4,
          "companyname": "No Limit Ltd",
          "subtotal": 100
      }
   ],
    "total": 380
}

We request the time tracked for January 2014 with subtotals for each Company.

Filters

There are a wide variety of filters available to let you request just the information you need, this keeps requests fast and means you spend less time tidying up the response data.

Please review the documentation for any calls you use especially if you will be making frequent requests. If you are making a lot of GET requests to maintain an up to date list then we recommend looking at using Web Hooks.

Caching

The API allows you to fetch a large number of items, we ask that you make requests sensibly and cache data where possible, especially when it is unlikely to change frequently.

The use of ETags, Web Hooks and the lastmodified filters can dramatically reduce bandwidth usage and make your app more responsive for your users.

If you anticipate putting heavy load on the API then please contact us and we will make efforts to help in advance (rather than potentially having to remove your access to the API when your app has gone live).

POST (Add)

A POST request is used to add an item or multiple items (adding multiple items is currently only supported by companies, contacts, tasks and when adding items to an invoice or quote).

Some fields are mandatory, others may be ignored if you do not wish to use them, refer to the documentation for each call for further details.

Remember that you must set the "Content-Type" header as "application/json" when making POST requests or your request will fail. Lets look at some simple examples:

Single Item

Add Contact:

REQUEST URL:
https://api.proworkflow.net/contacts

REQUEST BODY:
{
    "companyid": 29,
    "email": "noreply@proworkflow.com"
    "firstname": "Amy",
    "lastname": "West",
    "type": "client"
}

RESPONSE:
{
    "details":[
      {
          "id": 393,
          "name": "Adam West",
      }
   ],
    "message": "Contact/s Added",
    "status": "Success"
}

We submit a POST request to the "/contacts" URL with details of a single contact, in response we get the ID of the new contact. Note that the response will ALWAYS include the details array even when only one item is added.

Multiple Items

Add Contacts:

REQUEST URL:
https://api.proworkflow.net/contacts

REQUEST BODY:
[
   {
       "companyid": 29,
       "email": "noreply@proworkflow.com"
       "firstname": "Adam",
       "lastname": "West",
       "type": "client"
   },
   {
       "companyid": 29,
       "email": "noreply@proworkflow.com"
       "firstname": "Amy",
       "lastname": "West",
       "type": "client"
   }
]

RESPONSE:
{
    "details":[
      {
          "id": 393,
          "name": "Adam West",
      },
      {
          "id": 394,
          "name": "Amy West",
      }
   ],
    "message": "Contact/s Added",
    "status": "Success"
}

We submit a POST request to the "/contacts" URL with the details of two contacts, in response we get the IDs of the new contacts.

PUT (Edit)

A PUT request is used to edit a single item.

Remember that you must set the "Content-Type" header as "application/json" when making PUT requests or your request will fail.

In PUT requests, you only need to include fields you want to change, any other fields will retain their previous values. It is important to remember that submitting an empty value will clear a field so only submit an empty value if you are sure you want to remove the previous value.

Lets look at a simple example:

Single Item

Edit Contact:

REQUEST URL:
https://api.proworkflow.net/contacts/393

REQUEST BODY:
{
    "mobilephone": "01234567890",
    "workphone": "09876543210"
}

RESPONSE:
{
    "details":[
      {
          "id": 393
      }
   ],
    "message": "Contact Updated",
    "status": "Success"
}

We submit a PUT request to the "/contacts/393" URL to update the phone numbers for contact ID 393. In response we get the ID of the updated contact. Note that the response will ALWAYS include the details array.

DELETE

A DELETE request is used to delete a single item.

Note that some delete calls will automatically delete other items (for example deleting a Project will delete all associated Tasks), some provide the option of deleting other items (for example deleting a Company may optionally delete all associated Contacts). Refer to the specific call documentation for further details.

Lets look at a simple example:

Single Item

Delete Contact:

REQUEST URL:
https://api.proworkflow.net/contacts/393

RESPONSE:
{
    "details":[
      {
          "id": 393
      }
   ],
    "message": "Contact Deleted",
    "status": "Success"
}

We submit a DELETE request to the "/contacts/393" URL to delete contact ID 393. In response we get the ID of the deleted contact.

Diving Deeper

You've made it through the basics of the API, now we get to the really good stuff. In this section we'll cover a few more advanced concepts and features of the API that can really help move your app from good to great (and maybe even to awesome).

JSONP

The API supports JSONP but as we also support CORS we don't recommend using JSONP unless you have no alternative. JSONP is also limited to GET requests so is unlikely to be sufficient for most applications.

CORS

The API supports CORS to allow GET/POST/PUT/DELETE requests from non-ProWorkflow domains. This is supported automatically by most browsers and code libraries.

ETags

We support ETags and encourage you to use them to reduce bandwidth and improve responsiveness.

Every successful GET response will include an ETag header with a value which you should set in the If-None-Match header of subsequent requests to the same endpoint.

If the returned data has not changed then you will get an empty response from the API with a 304 HTTP Status Code.

Response header:
ETag:"1305223893"

Request header:
If-None-Match: "1305223893"

Polling

Keeping your application up to date is likely to be a key concern. There are several ways to ensure you are working with the most recent data from ProWorkflow.

The most basic approach is to request a full list of data from ProWorkflow on a regular basis. You can then process this list looking for new or updated items and update your application accordingly. This works but it is slow, requires extra processing and places unneccessary load on both client and server.

A minor improvment is to use ETags (covered above), in this case you will receive a 304 response if the list has not changed since you last requested it. The problem with this approach is that the list still has to be prepared on the server before a comparison can be made, this can be slow for large lists.

A better improvement is to use the "lastmodifiedfrom" filter which allows you to return only those items that have been added or updated since a specific date time (using relative date times can be very helpful as shown in the example below).

Fetch Recently Added/Updated Contacts:

REQUEST URL:
https://api.proworkflow.net/contacts?lastmodifiedfrom=15n

RESPONSE:
{
    "contacts":[
      {
          "companyid": 29,
          "companyname": "ABC Media",
          "firstname": "Amy",
          "id": 504,
          "lastname": "West",
          "type": "client"
      },
      ...
      ...
      ...
      {
          "companyid": 6,
          "companyname": "Adventure Rafting",
          "firstname": "Warren",
          "id": 375,
          "lastname": "Drake",
          "type": "client"
      }
   ],
    "count": 80,
    "status": "Success"
}

We submit a GET request to the "/contacts" URL with the "lastmodifiedfrom" filter set to "15n" which returns only those contacts added or updated in the last 15 minutes. This will execute much faster than requesting all contacts and means your application doesn't need to check a long list looking for changes.

For many applications, a simple polling approach might be sufficient but for applications where you need real-time updates there is a much better way...

Web Hooks

Web Hooks solve many of the problems associated with polling:

  • Removes the need for a repeating request
  • Removes the need to process lists checking for changes
  • Provides (near) real-time updates

A Web Hook allows you to register certain events in ProWorkflow, once registered we will send a POST request to a URL you provide when that event happens.

The POST request contains the ID of the item that triggered the event, you can then use that ID as needed, typically you would make a GET request for the item and then update a user interface or sync the item to another system.

A wide range of events are supported and more will be added, refer to the "settings/webhooks" call documentation for a full list.

Hooks are a much better alternative to the traditional polling method and do not count towards your API rate limit and are sent instantly.

The example below shows how this process works:

Add Web Hook:

REQUEST URL:
https://api.proworkflow.net/settings/webhooks

REQUEST BODY:
{
    "event": "newcontact",
    "url": "https://myapp.com/contactadded"
}

RESPONSE:
{
    "details":[
      {
          "id": 3,
      }
   ],
    "message": "Web Hook Added",
    "status": "Success"
}

Later, a Contact is added and a POST request is made automatically:

REQUEST URL:
https://myapp.com/contactadded

REQUEST BODY:
{
    "id": 395
}

We register a new web hook, this is fired when a contact is added and will send a POST request to https://myapp.com/contactadded. Sometime later a contact is added (either via the API or the main ProWorkflow application) and a POST request containing the new ID is sent to the URL.

Custom API Fields

The API gives you access to all of the standard fields for items in ProWorkflow but sometimes you may need to add new fields. The API lets you add new fields to the main item types (company, contact, invoice, project, quote, task & time).

Once you have added a field to an item type, you can add/edit that field for every item of that type and use the "apifields" filter to search based on the field values.

It is important to remember that these fields are only available via the API and are not available in the main ProWorkflow application.

The example below shows how to add a field, set its value for an item and carry out a search based on the new field.

Add API Field:

REQUEST URL:
https://api.proworkflow.net/settings/apifields

REQUEST BODY:
{
    "name": "dateofbirth",
    "type": "contact"
}

RESPONSE:
{
    "details":[
      {
          "id": 3,
          "name": "dateofbirth",
      }
   ],
    "message": "Field Added",
    "status": "Success"
}

Edit a Contact, setting the new field value:

REQUEST URL:
https://api.proworkflow.net/contacts/393

REQUEST BODY:
{
    "apifields":[
      {
          "id": 3,
          "value": 12-May-1975
      }
   ]
}

RESPONSE:
{
    "details":[
      {
          "id": 393
      }
   ],
    "message": "Contact Updated",
    "status": "Success"
}

Fetch Contacts with a dateofbirth like "may":

REQUEST URL:
https://api.proworkflow.net/contacts?apifields=3,may

RESPONSE:
{
    "contacts":[
      {
          "companyid": 29,
          "companyname": "ABC Media",
          "firstname": "Warren",
          "id": 393,
          "lastname": "Dinner",
          "type": "client"
      }
   ],
    "count": 2,
    "status": "Success"
}

We submit a POST request to the "/settings/apifields" URL adding a field called "dateofbirth". In response we get the ID of the new field, note that we use this ID in future requests NOT the name of the field.

We then submit a PUT request to the "/contacts/393" URL setting the field to a value of "12-May-1975".

We then submit a GET request to the "/contacts" URL using the "apifields" parameter to return only the contacts with a "dateofbirth" like "may" (note that search parameters are case-insensitive).

It is possible to search based on multiple API fields, refer to the documentation for specific calls for further information.

Need more?

If you have any feedback on the API or have plans that you feel you can't currently accomplish with the API then contact us. We're always making improvements and customer feedback drives every change we make.

Overview

This section provides links to guides, tools and code libraries that you might find helpful when planning, designing and developing your application. If you come across any other resources that you think we should include then please let us know.

Guides

There is no shortage of books and online resources dedicated to application development (whether in the context of an API or otherwise) but if you are looking for somewhere to start then these are all worth your time.

Tools

The set of development tools you use will largely depend on the platform and environment you are targeting but these are some tools we have found useful if you're developing a web application. We listed some Testing tools earlier if you want to check those out too.

  • Firebug - Almost essential for web development if you use Firefox
  • Web Developer - Chrome's built-in Developer Tools not cutting it? Try this
  • Google Code Playground - Experiment with Google's APIs
  • Pusher - A service for adding websockets to your application (very useful for real-time updates)
  • PhoneGap - Convert your HTML/CSS/JS into native apps that run on Mobile Platforms.

Libraries

These definitely aren't the only way to make REST requests but if you're not sure where to start then they might be useful.

Get started for free