Data REST API

Deze documentatie beschrijft de Tarasa Data REST API: een set HTTP-endpoints waarmee externe systemen data uit de applicatie kunnen lezen, schrijven, bijwerken en verwijderen. De endpoints zitten onder /api/data/v2/... en werken op het niveau van een tabel.

Alle endpoints vereisen authenticatie — zie Authenticatie voor de drie ondersteunde methodes.

Endpoints

In de tabel hieronder is {table} de naam van een tabel, zoals geconfigureerd in de Admin UI (bijvoorbeeld Visit of Customer).

Afhankelijk van de configuratie voor de klant, zijn er verschillende tabellen beschikbaar, met verschillende velden. De tabellen en velden uit dit document zijn slechts voorbeelden.

Methode Pad Beschrijving
GET /api/data/v2/{table} Lijst documenten van deze tabel (gepagineerd)
POST /api/data/v2/{table}/query Lijst documenten via POST (zelfde body als query params)
GET /api/data/v2/{table}/{id} Eén document op basis van entity-id
POST /api/data/v2/{table} Nieuw document aanmaken
POST /api/data/v2/{table}/bulk Meerdere documenten aanmaken in één call
POST /api/data/v2/{table}/{id} Document volledig vervangen
PATCH /api/data/v2/{table}/{id} Document gedeeltelijk bijwerken
DELETE /api/data/v2/{table}/{id} Document verwijderen
POST /api/data/v2/{table}/aggregate Aggregaten berekenen (count/sum/avg/min/max)

GET /api/data/v2/{table} — Lijst documenten

Query-parameters (allemaal optioneel):

Parameter Beschrijving
filter Filter-expressie. Zie Filter.
orderBy Sortering. Zie OrderBy.
project Beperk welke velden er teruggegeven worden. Zie Projectie.
pageSize Aantal records per pagina (default 50).
continuationToken Token om de volgende pagina op te halen. Bij voorkeur via header X-ContinuationToken, omdat tokens lang kunnen zijn.

Response (200):

De response bevat een lijst entities met daarin de resultaten. Hierin zit altijd de metadata (namelijk entityId, createdDate, editedDate, createdByUserId, createdByUserName, editedByUserId, editedByUserName), en de velden die opgevraagd zijn. Hieronder staat een voorbeeld van een fictieve tabel (met velden license_plate, postal_code).

{
  "continuation": "abc123...",   // null als er geen volgende pagina is
  "totalCount": null,
  "entities": [
    {
      "entityId": "a4277d7f-b37d-...",
      "createdDate": 1715000000000,
      "editedDate": 1715000000000,
      "createdByUserId": "...",
      "createdByUserName": "...",
      "editedByUserId": "...",
      "editedByUserName": "...",
      "license_plate": "AB-12-CD",
      "postal_code": "1234AB"
    },
    ...
  ]
}

Statuscodes:

POST /api/data/v2/{table}/query — Lijst via POST

Identiek aan de GET-variant, maar de query-parameters staan in de JSON-body. Handig als de filter- of project-expressie te lang wordt voor een URL.

Body:

{
  "filter": "license_plate EQ \"AB-12-CD\"",
  "orderBy": "editedDate:desc",
  "project": "license_plate,date,postal_code",
  "pageSize": 200,
  "continuationToken": "..."
}

GET /api/data/v2/{table}/{id} — Eén document

{id} is de entityId (GUID).

Query-parameters: project (zie verderop).

Response:

POST /api/data/v2/{table} — Aanmaken

Body: JSON-object met de velden van het document (zonder metadata-velden zoals entityId, createdDate).

Response:

POST /api/data/v2/{table}/bulk — Meerdere aanmaken

Body: JSON-array van te creëren documenten (zelfde vorm als bij de gewone create-endpoint, dus zonder metadata-velden).

[
  { "license_plate": "AB-12-CD", "postal_code": "1234AB" },
  { "license_plate": "XY-99-ZZ", "postal_code": "5678EF" }
]

Response (200): JSON-array met één element per inputdocument, in dezelfde volgorde. Elk element is óf een succes (met de aangemaakte entity), óf een fout (met validatiefouten voor dat specifieke document). De aanroep is niet atomair: documenten zonder fouten worden aangemaakt, ook als andere documenten in dezelfde batch falen.

[
  {
    "entityId": "a4277d7f-b37d-...",
    "createdDocument": {
      "entityId": "a4277d7f-b37d-...",
      "createdDate": 1715000000000,
      "license_plate": "AB-12-CD",
      "postal_code": "1234AB"
    }
  },
  {
    "status": "BadRequest",
    "errorDescription": null,
    "errors": [
      { "fieldName": "license_plate", "message": "Ongeldig formaat" }
    ]
  }
]

Onderscheid succes vs fout op basis van de aanwezigheid van entityId/createdDocument (succes) of status/errors (fout). status is een van Error, NotPermitted, BadRequest.

Een non-200 response betekent dat de hele bulk-call is afgewezen (bijvoorbeeld 403 als de gebruiker geen create-rechten heeft); in dat geval is er geen enkel document aangemaakt.

POST /api/data/v2/{table}/{id} — Vervangen

Body: JSON-object met de volledige nieuwe versie. Velden die ontbreken in de body worden ook leeg/null in het opgeslagen document, mits dit mag van de validatie-regels.

Statuscodes:

PATCH /api/data/v2/{table}/{id} — Gedeeltelijk bijwerken

Body: JSON-object met alleen de velden die je wilt wijzigen. Velden die je niet meestuurt blijven onveranderd.

Verder identiek aan de POST-request.

DELETE /api/data/v2/{table}/{id} — Verwijderen

Statuscodes:

POST /api/data/v2/{table}/aggregate — Aggregeren

Bereken een aggregaat over alle documenten van een tabel, optioneel gegroepeerd.

Body:

{
  "field": "amount",        // veld waarop de aggregatie wordt uitgevoerd
  "function": "sum",        // count | sum | average | min | max
  "groupBy": "year,branch", // optioneel: comma-separated lijst van groepeervelden
  "filter": "is_paid EQ true"  // optioneel
}

Restricties:

Response (200): Array met één object per groep. De aggregaatwaarde staat onder de naam van het veld waarop geaggregeerd is (in dit voorbeeld amount). Bij geen groupBy is dat dus één object met alleen dat ene veld.

[
  { "year": 2024, "branch": "Amsterdam", "amount": 12345 },
  { "year": 2024, "branch": "Rotterdam", "amount": 6789 }
]

Pagination

Lijst-endpoints geven 50 records per pagina terug. Pas dit aan met pageSize. Als er meer resultaten zijn, bevat de response een continuation token. Vraag de volgende pagina op door dat token terug te sturen:

De continuation token mag alleen gebruikt worden met gelijke parameters (filter, orderBy, project).

Als er geen volgende pagina is, bevat de response continuation null.


OrderBy: sorteren

Syntax: <veldpad>[:asc|:desc]. Default is oplopend (asc).

Voorbeelden:

Je kunt sorteren op metadata-velden, en alle velden binnen de tabel. In sommige gevallen kan je ook sorteren op velden in gerelateerde documenten.


Filter: filteren

De filter-parameter accepteert een expressie met de volgende structuur:

<veldpad> <operator> <waarde>

Combineer met AND / OR en haakjes. Voorbeeld:

filter=(is_paid_visit EQ false AND license_plate STRSTART "AB") OR amount GT 100

Velden

Operatoren

Operator Tekstvariant Werkt op Betekenis
== EQ alle types gelijk
!= NEQ alle types niet gelijk
> GT nummer, datum groter dan
< LT nummer, datum kleiner dan
>= GEQ nummer, datum groter dan of gelijk
<= LEQ nummer, datum kleiner dan of gelijk
& BAND int bitwise AND
| BOR int bitwise OR
^ BXOR int bitwise XOR
IN alle types waarde zit in array
NIN alle types waarde zit niet in array
BETWEEN nummer, datum tussen twee waarden (inclusief)
STRCON string, nummer bevat substring (case-insensitive)
STRSTART string, nummer begint met substring (case-insensitive)
STREND string, nummer eindigt op substring (case-insensitive)
STREQ string, nummer string-gelijkheid, case-insensitive
ISNULL alle types veld is leeg/null
ISNOTNULL alle types veld is niet null

ISNULL en ISNOTNULL worden geschreven zonder waarde, bijv. filter=editedDate ISNULL. In plaats van ISNULL kan je ook == null (of EQ null) gebruiken, en vergelijkbaar is mogelijk met ISNOTNULL.

Waardes

Booleaanse combinaties

Voorbeelden

is_paid_visit EQ false
license_plate STRSTART "AB"
editedDate GT 1715000000000
status IN ["Open","Pending"]
amount BETWEEN [100, 200]
editedDate ISNOTNULL
(branch EQ "A" OR branch EQ "B") AND is_paid EQ true

Projectie: deelselectie van velden

Met project haal je alleen specifieke velden op. Dit maakt requests sneller en goedkoper.

project=license_plate,date,is_paid_visit,postal_code
project=*                              # alles (default als project niet wordt meegegeven)
project=customer.*                     # alle velden van de gerelateerde customer
project=firstname,lastname,address.*

Metadata-velden zoals entityId, createdDate, editedDate zitten altijd in het resultaat.


Best practices

Alle data van een tabel ophalen

Loop met continuation-tokens. Maak pageSize zo groot mogelijk binnen wat redelijk is (200–1000) om het aantal requests te beperken:

loop:
  GET /api/data/v2/Visit?pageSize=500
      with header X-ContinuationToken: <vorig token>
  verwerk response.entities
  als response.continuation == null: stop
  anders: continuation = response.continuation

Incrementeel synchroniseren

Vraag de hele tabel maar één keer in zijn geheel op, en doe daarna periodiek (bijv. ieder uur of iedere dag) een delta-call op editedDate:

GET /api/data/v2/Visit
    ?filter=editedDate GT 1715000000000
    &orderBy=editedDate:asc
    &pageSize=500

Sla het hoogste editedDate van wat je hebt verwerkt op, en gebruik dat als ondergrens voor de volgende sync. Houd er rekening mee dat een document opnieuw kan opduiken (bij update), dus je consumer moet idempotent zijn: behandel records als "upsert" (insert-or-update op entityId).

Alle datums zijn UNIX-epoch in milliseconden (sinds 1970-01-01 UTC).

Alleen ophalen wat je nodig hebt

Gebruik project als je veel records ophaalt maar maar enkele velden gebruikt. Dat scheelt zowel bandbreedte als response-tijd.

Rate limiting

Er wordt per gebruiker gemonitord hoeveel database load veroorzaakt wordt. Bij overschrijding van wat redelijk is nemen we contact op met de gebruiker. Langdurige overschrijding kan leiden tot blokkade.

Het doen van overdadig veel requests verslechtert de performance voor andere gebruikers van de applicatie.

Foutafhandeling

Bij 4xx fouten heeft het geen zin de request opnieuw te proberen.