Zum Hauptinhalt springen
Hilfecenter

API Compact Guide

Der vollständige projectfacts/teamspace API Compact Guide – komplett auf dieser Seite zum Lesen, mit allen Konzepten, Request-/Response-Beispielen und Cheat-Sheets, sowie als PDF und PowerPoint zum Herunterladen.

Der API Compact Guide ist die ausführliche Gesamtanleitung zur API – mit allen Konzepten und konkreten Request-/Response-Beispielen. Der komplette Inhalt der Präsentation steht hier auf dieser Seite; zusätzlich kannst du ihn als PDF, PowerPoint oder online ansehen.

Sprache & Server: Die Originalpräsentation ist in englischer Sprache verfasst und nennt projectfacts-Beispiel-Server (sync.projectfacts.de). Für teamspace gilt dieselbe API unter deiner eigenen Server-Adresse (z. B. https://app1.teamspace.de). Die Inhalte unten sind ins Deutsche übertragen; die Code- und JSON-Beispiele sind unverändert übernommen.

Neuer als der Guide: Der Guide stammt von 2021. Seit Version 26.2 beschreibt jeder Server seine API zusätzlich maschinenlesbar unter <server>/api/openapi.json, mit allen Adressen, Filtern und Feldern. Wie du sie nutzt, zeigt Die OpenAPI-Beschreibung nutzen. Was der Guide nicht kennt, etwa den Matrix-Parameter evaluate und Filter auf einen Datumsbereich, ergänzen die Hinweise unter den Cheat-Sheets.

Herunterladen

projectfacts API Compact Guide — Version 0.36, benötigt projectfacts 6.00 oder neuer (Stand: 2021-03-09).

Überblick

Der Guide führt durch die folgenden Themen:

  1. Buzzwords – welche Standards und Best Practices verwendet werden.
  2. Autorisierung – was über die API gemacht werden kann.
  3. Authentifizierung – das Sicherheitskonzept der API mit seinen drei Wegen:
    • Login-Authentifizierung
    • Token-Authentifizierung
    • Interface-Authentifizierung
  4. Der projectfacts API-Explorer – das Werkzeug zum Erkunden der API.
  5. Durchgängiges Beispiel – die Adresse einer Organisation aktualisieren.
  6. Was zu beachten ist – Hinweise für die Praxis.

Buzzwords: Was du vorher kennen solltest

Bevor es losgeht, solltest du mit diesen Themen vertraut sein:

  • REST
  • JSON
  • HATEOAS
  • Basic Auth

Autorisierung: Was über die API möglich ist

Grundsätzlich hat ein API-Nutzer dieselben Rechte wie in der Weboberfläche (projectfacts/teamspace):

  • Wer in der Weboberfläche Projekte löschen darf, kann das auch über die API tun. Das lässt sich nicht verhindern.
  • Ebenso kannst du diesen Nutzer nicht daran hindern, eine eigene App zum Löschen von Projekten zu entwickeln: Du kannst einem Nutzer die Nutzung der API nicht grundsätzlich verbieten.
  • Wer in der Weboberfläche keine Zeiten buchen darf, kann das auch über die API nicht.

Ausnahme: Die Nutzung der API über projectfacts-Interfaces (Interface-Authentifizierung) folgt eigenen Regeln – dazu später mehr.

Authentifizierung: Das Sicherheitskonzept der API

Du kannst die API nicht einfach mit deinem projectfacts-Passwort verwenden. Würde man das Passwort bei jeder Anfrage mitschicken, entstünden mehrere Probleme:

  • Passwort bei jeder Anfrage übertragen? ➜ Risiko, dass es abgefangen wird. 😬
  • Ein Client wird kompromittiert? ➜ Passwort ändern ➜ alle Clients würden getrennt. 😞
  • Das Passwort müsste auf dem Client gespeichert werden. 😲

Deshalb gibt es drei eigene Authentifizierungswege.

Login-Authentifizierung

Die einzige Ressource, die du mit deinen projectfacts-Zugangsdaten (Login-Authentifizierung) ansprechen kannst, ist die device-Ressource (Gerät). Sie muss beim ersten Anmelden aus deiner App heraus angelegt werden:

POST https://sync.projectfacts.de/api/device/
{
  "email": "user@provider.com",
  "password": "projectfactsPassword",
  "deviceName": "YourAppName",
  "deviceType": "de.fivepoint.other"
}

Gibt es einen Nutzer mit passender E-Mail und passendem Passwort, antwortet der Server mit der neuen device-Ressource. Sie enthält die Device-ID und den API-Token, mit denen du alle folgenden Anfragen authentifizierst:

{
  "_id": "10001234",
  "token": "D1C2B3A4",
  "deviceName": "NameOfYourApp",
  [...]
}

Wichtig: Diese Daten muss deine App speichern – niemals das ursprüngliche projectfacts-Passwort! Den Token bekommst du nur dieses eine Mal.

Token-Authentifizierung

Alle regulären Anfragen werden per Basic Auth mit Device-ID und Device-Token abgesichert – sie repräsentieren sowohl den Nutzer deiner App als auch das verwendete Gerät.

So baust du den Authorization-Header in JavaScript auf:

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

Dabei liegt im lokalen Speicher deiner App:

{
  "_id": "10001234",
  "token": "D1C2B3A4",
  [...]
}

Die Berechtigung eines Geräts kann entzogen werden, indem das Gerät innerhalb von projectfacts gelöscht wird („Meine Geräte”). Andere Geräte/Apps sind davon nicht betroffen.

Interface-Authentifizierung

Für nicht-personenbezogene Apps, die nicht „im Namen” eines Nutzers handeln (z. B. ein Check-In/Out-Terminal):

  • Basic Auth mit Interface-ID und -Passwort.
  • Das Passwort wird in der projectfacts-Konfiguration (Interfaces) gesetzt.
  • Keine Nutzer-Berechtigungen – die Rechte ergeben sich aus den Einstellungen des Interfaces.
  • Eine Einschränkung der verbindenden IP-Adresse ist möglich.

Der projectfacts API-Explorer

Den API-Explorer erreichst du unter:

https://sync.projectfacts.de/htdocs/apps/apiexplorer/index.html

…oder unter der Wurzel deines pf-Servers + /htdocs/....

Es handelt sich um eine HTML5-/AngularJS-App, mit der du:

  • unsere API durchsuchst,
  • siehst, wie Ressourcen und Relationen dargestellt werden,
  • lernst, wie man Queries nutzt,
  • die Kommunikation über die Entwicklertools des Browsers verfolgst.

Die JSON-Repräsentationen als TypeScript-Klassen findest du unter:

https://sync.projectfacts.de/api/api/dto.ts

Schau dir das unbedingt einmal an!

Beispiel: Die Adresse einer Organisation aktualisieren

Wir suchen in unserer App nach „Example corp” und aktualisieren deren Adresse. Dazu sind folgende Schritte nötig:

  1. GET auf /api/contact inklusive Such-Query.
  2. GET auf die gewünschte contact-Ressource.
  3. GET auf die Hauptadresse des Kontakts.
  4. PUT auf diese Adresse.

Schritt 1 — Kontakt suchen

Wir suchen nach „Example corp.” (der Nutzer hat „examp” in unsere App getippt). Dazu führen wir ein GET mit Device-ID und Token als Basic-Auth-Header aus:

GET https://sync.projectfacts.de/api/contact?caption*=examp

Die Bestandteile der URL:

BestandteilBedeutung
contactZu durchsuchende Ressource (contact resource).
captionFeld, auf das gefiltert wird (Bezeichnung des Elements).
*=Operator „enthält” – im Gegensatz zu =ist gleich”.
exampSuchwert (der Operator = ist nicht case-sensitiv).

Ergebnis (Auszug):

{
 "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: Über das mitgelieferte href geht es hier weiter – du baust keine IDs selbst zusammen, sondern folgst dem Link.

Schritt 2 — Kontakt laden

Wir laden „Example corp.” (der Nutzer hat den dritten Treffer ausgewählt):

GET https://sync.projectfacts.de/api/contact/3456

Ergebnis (Auszug):

{
 "_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;Deutschland",
                "href": "…/api/contactfield/173880993", "rel": "contactfield",
                "title": "preferred postal address",
                "value": 173880993, "idKey": "174"}
}

Dazu drei Beobachtungen:

  • Meta-Informationen beginnen mit einem Unterstrich (_id, _idKey, _lastModifiedDate).
  • Kontakte können Personen wie auch Organisationen sein ("person": false ⇒ Organisation).
  • Komplexe Daten erscheinen als Link-Objekt (wieder HATEOAS): Die mainAddress ist ein Verweis. Ihr caption dient als „Vorschau” der Ressource.

Schritt 3 — Adressfeld laden

Wir laden die contactfield-Ressource, die als mainAddress referenziert ist:

GET https://sync.projectfacts.de/api/contactfield/173880993

Ergebnis (Auszug):

{
 "_id": 173880993, "_idKey": "174",
 "value": ";;Examplestreet 7;Examplecity;;122456;Deutschland",
 "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"}
}

Dazu:

  • Die Adresse liegt im vCard-Format vor, inklusive der Möglichkeit für ein eigenes Label (customlabel).
  • Der Typ des Kontaktfelds ist ADR – wie in vCard.
  • type und subtype sind wieder Link-Objekte, die auf die Details verweisen. Sie verlinken zugleich eine Sammlung möglicher Werte (optionsUrl).

Schritt 4 — Adresse speichern

Nachdem wir den Wert geändert haben, speichern wir die geänderte contactfield-Ressource per PUT:

PUT https://sync.projectfacts.de/api/contactfield/173880993

Request-Daten (Auszug):

{
 "_id": 173880993, "_idKey": "174",
 "value": ";;Saalbaustraße 27;Darmstadt;;64283;Deutschland",
 "customlabel": null,
 "type": {"caption": "Address", "value": "ADR",
         "href": "…/api/enum/contactfieldtype/ADR",
         "optionsUrl": "…/api/enum/contactfieldtype"},
 "subtype": {"caption": "Arbeit", "value": "WORK",
            "href": "…/api/enum/contactfieldsubtype/WORK",
            "optionsUrl": "…/api/enum/contactfieldsubtype"},
 "contact": {"href": "…/api/contact/28883816", "title": "Contact",
            "value": 28883816, "idKey": "39"}
}

Einfache Datentypen wie Strings (hier value) lassen sich problemlos ändern.

Wie aktualisiert man die Link-Objekte (z. B. subtype)? Nur der value zählt. Du musst also nicht das komplette Link-Objekt zurückschicken – es genügt, den value zu setzen:

{
 "_id": 173880993, "_idKey": "174",
 "value": ";;Saalbaustraße 27;Darmstadt;;64283;Deutschland",
 "customlabel": null,
 "type": {"caption": "Adresse", "value": "ADR",
         "href": "…/api/enum/contactfieldtype/ADR",
         "optionsUrl": "…/api/enum/contactfieldtype"},
 "subtype": "HOME",
 "contact": {"href": "…/api/contact/28883816", "title": "Contact",
            "value": 28883816, "idKey": "39"}
}

…so funktioniert es auch! Statt des vollständigen Link-Objekts für subtype reicht der reine Wert "HOME".

Was zu beachten ist

  • Unsere API unterstützt dich beim Caching – nutze es!
  • Das Erste, was eine App beim Start tun sollte, ist, ihre device-Ressource zu laden – so weißt du von Anfang an, ob der Token noch gültig ist.
  • Denke daran, den Token sicher in deiner App zu speichern.
  • Unsere API ist noch nicht vollständig, wird aber von Zeit zu Zeit um weitere Ressourcen erweitert.
  • Unser projectfacts-Chat ist ein weiteres Beispiel, umgesetzt in TypeScript.
  • Wenn du eine öffentliche App entwickeln möchtest, kontaktiere uns – wir unterstützen dich!

Cheat-Sheet: Query-Filter (Sammlungen)

Filter werden als Query-Parameter an eine Sammlungs-URL gehängt:

TypSyntaxBeispielBedeutung
Match (gleich)…?field=valuename="bob ross"&car=volvo&age=3Anführungszeichen nur bei Bedarf (nur Strings).
Contains (enthält)…?field*=valname*="bo"&car=volNamen, die „bo” enthalten.
Range (Bereich)…?field=1..3name="alice".."bob"&age=20..30Werte 20, 30 und dazwischen.
Or (oder)…?field=A,B,Cname=alice,"bob ross",charlieAlle Alices, Charlies und Bob Rosses.
Not empty (nicht leer)…?fieldname=bob&carBobs, die ein Auto besitzen.
Empty (leer)…?!fieldname=bob&!carBobs, die kein Auto besitzen.

Ergänzung zum Guide, Datumsbereich: Der Range-Filter gilt auch für Datumsfelder, beide Grenzen gehören dazu. Die Zeiten eines Mitarbeiters in einer Woche liefert /api/time?worker=54967&date=2026-09-07..2026-09-13. Mehr dazu in Aufbau der API-Adressen (URL-Struktur).

Cheat-Sheet: Matrix-Parameter (Sammlungen)

Matrix-Parameter steuern Sortierung, Umfang und Auflösung einer Sammlung:

FunktionSyntaxBedeutung
Aufsteigend sortieren...;sort=name?...Elemente nach name aufsteigend sortieren.
Absteigend sortieren...;sort=!name?...Elemente nach name absteigend sortieren.
Ergebnisgröße...;limit=100?...Die ersten 100 Elemente nach dem Offset laden.
Ergebnis-Offset...;offset=200?...Die ersten 200 Elemente überspringen.
Teilbaum...;parent=1234?...Kinder von Element 1234 laden (nur Baumstrukturen).
Tiefe (evtl. LANGSAM!)…;depth=1?...Erste Generation von Referenzen auflösen (LANGSAM!).
Hinweise anzeigen…;showhints=true?...Verfügbare Sortierungen anzeigen (sort by xyz).

Ergänzung zum Guide, evaluate: In Abfragen der Projektliste taucht zusätzlich ;evaluate=… auf, etwa ;evaluate=isBookable. Er filtert nicht, sondern ergänzt jeden Eintrag um das Feld evaluation mit true oder false. Offiziell beschrieben ist er bisher nicht. Wann du ihn statt eines Filters nimmst, steht in Aufbau der API-Adressen (URL-Struktur).

Cheat-Sheet: Matrix-Parameter (Elemente)

FunktionSyntaxBedeutung
7-Tage-Caching-Header…;lck=value?...Die Antwort wird mit einem 7-Tage-Caching-Header zurückgegeben.

„value" sollte eine Art Version der Ressource abbilden. Solange sie sich nicht ändert, verwendet dein HTTP-Client seinen Cache, statt die Ressource erneut vom Server zu laden.

Tipp: Verwende das "_lastModifiedDate" aus der Sammlungs-Ressource, um sicherzustellen, dass du ein frisches Element erhältst, wenn es auf dem Server geändert wurde.

Hinweise

  • Die lokalen Dateien (PDF/PowerPoint) sind eine Kopie des Stands zum Redaktionszeitpunkt. Die Online-Version kann aktueller sein.
  • Der Guide ist im Original in englischer Sprache verfasst und nennt projectfacts-Beispiel-Server (sync.projectfacts.de); für teamspace gilt dieselbe API unter deiner eigenen Server-Adresse.

Verwandte Themen