The API Compact Guide is the detailed end-to-end guide to the API – with all the concepts and concrete request/response examples. The complete content of the presentation is right here on this page; in addition, you can view it as a PDF, PowerPoint or online.
Language & server: The original presentation is written in English and names projectfacts example servers (
sync.projectfacts.de). For teamspace the same API applies under your own server address (for examplehttps://app1.teamspace.de). The code and JSON examples are taken over unchanged.
Download
- View the API Compact Guide (PDF) — PDF, 31 pages (~290 KB)
- Download the API Compact Guide (PowerPoint) — PPTX (~240 KB)
- View online (Google Slides)
projectfacts API Compact Guide — version 0.36, requires projectfacts 6.00 or newer (as of 9 March 2021).
Overview
The guide walks you through the following topics:
- Buzzwords – which standards and best practices are used.
- Authorisation – what can be done via the API.
- Authentication – the security concept of the API with its three routes:
- login authentication
- token authentication
- interface authentication
- The projectfacts API explorer – the tool for exploring the API.
- A worked example – updating an organisation’s address.
- Things to keep in mind – notes for practice.
Buzzwords: what you should know beforehand
Before you start, you should be familiar with these topics:
- REST
- JSON
- HATEOAS
- Basic Auth
Authorisation: what is possible via the API
In principle, an API user has the same rights as in the web interface (projectfacts/teamspace):
- Anyone who may delete projects in the web interface can also do so via the API. There is no way to prevent that.
- Likewise, you cannot stop this user from developing their own app to delete projects: you cannot generally forbid a user from using the API.
- Anyone who may not book times in the web interface cannot do so via the API either.
Exception: Using the API via projectfacts interfaces (interface authentication) follows its own rules – more on that later.
Authentication: the security concept of the API
You cannot simply use the API with your projectfacts password. If the password were sent with every request, several problems would arise:
- Send the password with every request? ➜ risk of it being intercepted. 😬
- A client gets compromised? ➜ change the password ➜ all clients would be disconnected. 😞
- The password would have to be stored on the client. 😲
That is why there are three dedicated authentication routes.
Login authentication
The only resource you can address with your projectfacts credentials (login authentication) is the device resource. It has to be created when you first sign in from your app:
POST https://sync.projectfacts.de/api/device/
{
"email": "user@provider.com",
"password": "projectfactsPassword",
"deviceName": "YourAppName",
"deviceType": "de.fivepoint.other"
}
If there is a user with a matching email and password, the server responds with the new device resource. It contains the device ID and the API token, with which you authenticate all subsequent requests:
{
"_id": "10001234",
"token": "D1C2B3A4",
"deviceName": "NameOfYourApp",
[...]
}
Important: Your app must store this data – never the original projectfacts password! You get the token only this one time.
Token authentication
All regular requests are secured via Basic Auth with the device ID and device token – they represent both the user of your app and the device used.
This is how you build the Authorization header in JavaScript:
// API request in JavaScript
var credsB64 = window.btoa(device._id + ':' + device.token)
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Authorization', 'Basic ' + credsB64);
xhr.onload = function(response) { /*...*/ };
xhr.send();
Your app’s local storage holds:
{
"_id": "10001234",
"token": "D1C2B3A4",
[...]
}
A device’s authorisation can be revoked by deleting the device within projectfacts (“My devices”). Other devices/apps are not affected by this.
Interface authentication
For non-personal apps that do not act “on behalf of” a user (for example a check-in/out terminal):
- Basic Auth with an interface ID and password.
- The password is set in the projectfacts configuration (interfaces).
- No user permissions – the rights follow from the settings of the interface.
- A restriction of the connecting IP address is possible.
The projectfacts API explorer
You reach the API explorer at:
https://sync.projectfacts.de/htdocs/apps/apiexplorer/index.html
…or at the root of your pf server + /htdocs/....
It is an HTML5/AngularJS app with which you can:
- browse our API,
- see how resources and relations are represented,
- learn how to use queries,
- follow the communication via the browser’s developer tools.
You will find the JSON representations as TypeScript classes at:
https://sync.projectfacts.de/api/api/dto.ts
Do take a look at this!
Example: updating an organisation’s address
We search our app for “Example corp” and update its address. This requires the following steps:
GETon/api/contactincluding a search query.GETon the desired contact resource.GETon the contact’s main address.PUTon that address.
Step 1 — search for the contact
We search for “Example corp.” (the user typed “examp” into our app). To do so, we run a GET with the device ID and token as the Basic Auth header:
GET https://sync.projectfacts.de/api/contact?caption*=examp
The parts of the URL:
| Part | Meaning |
|---|---|
contact | resource to be searched (contact resource). |
caption | field that is filtered on (caption of the element). |
*= | the “contains” operator – as opposed to = “equals”. |
examp | search value (the = operator is not case-sensitive). |
Result (excerpt):
{
"size": 3, "offset": 0, "limit": 100,
"items": [
{"caption": "Max TexaMP", "href": "https://…/api/contact/1234", "value": 1234},
{"caption": "Ben Examplename", "href": "https://…/api/contact/2345", "value": 2345},
{"caption": "Example corp.", "href": "https://…/api/contact/3456", "value": 3456}
]
}
HATEOAS: From here you continue via the supplied
href– you do not assemble IDs yourself, you follow the link.
Step 2 — load the contact
We load “Example corp.” (the user selected the third match):
GET https://sync.projectfacts.de/api/contact/3456
Result (excerpt):
{
"_id": 3456, "_idKey": "39", "_lastModifiedDate": "2015-11-19T10:44:26.000+0000",
"active": true,
"person": false,
"caption": "Example corp.",
"firstname": null,
"lastname": "Example corp.",
"description": "Address outdated\nMoved?",
"mainAddress": {"caption": ";;Examplestreet 7;Examplecity;;122456;Germany",
"href": "…/api/contactfield/173880993", "rel": "contactfield",
"title": "preferred postal address",
"value": 173880993, "idKey": "174"}
}
Three observations on this:
- Meta information starts with an underscore (
_id,_idKey,_lastModifiedDate). - Contacts can be people as well as organisations (
"person": false⇒ organisation). - Complex data appears as a link object (HATEOAS again): the
mainAddressis a reference. Itscaptionserves as a “preview” of the resource.
Step 3 — load the address field
We load the contactfield resource referenced as mainAddress:
GET https://sync.projectfacts.de/api/contactfield/173880993
Result (excerpt):
{
"_id": 173880993, "_idKey": "174",
"value": ";;Examplestreet 7;Examplecity;;122456;Germany",
"customlabel": null,
"type": {"caption": "Address", "value": "ADR",
"href": "https://ws-cp/api/enum/contactfieldtype/ADR",
"optionsUrl": "…/api/enum/contactfieldtype"},
"subtype": {"caption": "Work", "value": "WORK",
"href": "…/api/enum/contactfieldsubtype/WORK",
"optionsUrl": "https://ws-cp/api/enum/contactfieldsubtype"},
"contact": {"href": "…/api/contact/3456", "title": "Contact",
"value": 3456, "idKey": "39"}
}
On this:
- The address is in vCard format, including the option for a custom label (
customlabel). - The type of the contact field is
ADR– as in vCard. typeandsubtypeare again link objects that point to the details. At the same time they link a collection of possible values (optionsUrl).
Step 4 — save the address
After changing the value, we save the modified contactfield resource via PUT:
PUT https://sync.projectfacts.de/api/contactfield/173880993
Request data (excerpt):
{
"_id": 173880993, "_idKey": "174",
"value": ";;Saalbaustraße 27;Darmstadt;;64283;Germany",
"customlabel": null,
"type": {"caption": "Address", "value": "ADR",
"href": "…/api/enum/contactfieldtype/ADR",
"optionsUrl": "…/api/enum/contactfieldtype"},
"subtype": {"caption": "Work", "value": "WORK",
"href": "…/api/enum/contactfieldsubtype/WORK",
"optionsUrl": "…/api/enum/contactfieldsubtype"},
"contact": {"href": "…/api/contact/28883816", "title": "Contact",
"value": 28883816, "idKey": "39"}
}
Simple data types such as strings (here
value) can be changed without any trouble.
Bonus — updating link objects
How do you update the link objects (for example subtype)? Only the value matters. So you do not need to send the complete link object back – it is enough to set the value:
{
"_id": 173880993, "_idKey": "174",
"value": ";;Saalbaustraße 27;Darmstadt;;64283;Germany",
"customlabel": null,
"type": {"caption": "Address", "value": "ADR",
"href": "…/api/enum/contactfieldtype/ADR",
"optionsUrl": "…/api/enum/contactfieldtype"},
"subtype": "HOME",
"contact": {"href": "…/api/contact/28883816", "title": "Contact",
"value": 28883816, "idKey": "39"}
}
…this works too! Instead of the complete link object for
subtype, the plain value"HOME"is enough.
Things to keep in mind
- Our API helps you with caching – use it!
- The first thing an app should do on startup is load its device resource – that way you know from the outset whether the token is still valid.
- Remember to store the token securely in your app.
- Our API is not yet complete, but is extended with further resources from time to time.
- Our projectfacts chat is another example, implemented in TypeScript.
- If you want to develop a public app, get in touch – we will support you!
Cheat sheet: query filters (collections)
Filters are appended to a collection URL as query parameters:
| Type | Syntax | Example | Meaning |
|---|---|---|---|
| Match (equals) | …?field=value | name="bob ross"&car=volvo&age=3 | quotation marks only where needed (strings only). |
| Contains | …?field*=val | name*="bo"&car=vol | names that contain “bo”. |
| Range | …?field=1..3 | name="alice".."bob"&age=20..30 | values 20, 30 and in between. |
| Or | …?field=A,B,C | name=alice,"bob ross",charlie | all Alices, Charlies and Bob Rosses. |
| Not empty | …?field | name=bob&car | Bobs that have a car. |
| Empty | …?!field | name=bob&!car | Bobs that have no car. |
Cheat sheet: matrix parameters (collections)
Matrix parameters control the sorting, scope and resolution of a collection:
| Function | Syntax | Meaning |
|---|---|---|
| Sort ascending | ...;sort=name?... | sort elements by name, ascending. |
| Sort descending | ...;sort=!name?... | sort elements by name, descending. |
| Result size | ...;limit=100?... | load the first 100 elements after the offset. |
| Result offset | ...;offset=200?... | skip the first 200 elements. |
| Subtree | ...;parent=1234?... | load children of element 1234 (tree structures only). |
| Depth (possibly SLOW!) | …;depth=1?... | resolve the first generation of references (SLOW!). |
| Show hints | …;showhints=true?... | show the available sort options (sort by xyz). |
Cheat sheet: matrix parameters (elements)
| Function | Syntax | Meaning |
|---|---|---|
| 7-day caching header | …;lck=value?... | the response is returned with a 7-day caching header. |
"value" should represent some kind of version of the resource. As long as it does not change, your HTTP client uses its cache instead of loading the resource from the server again.
Tip: Use the
"_lastModifiedDate"from the collection resource to make sure you get a fresh element when it has been changed on the server.
Notes
- The local files (PDF/PowerPoint) are a copy of the state at the time of editing. The online version may be more up to date.
- The guide is originally written in English and names projectfacts example servers (
sync.projectfacts.de); for teamspace the same API applies under your own server address.
Related topics
- API – introduction API Introduction
- API address structure (URLs) API Concept
- Field reference: expense & document fields API Reference