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:
200 OK— pagina opgehaald400 Bad Request— ongeldige filter/orderBy/project403 Forbidden— geen leesrechten op deze tabel404 Not Found— onbekende tabel500 Internal Server Error
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:
200 OKmet het document in de body. De response bevat eenETag-header met het document-id.404 Not Found— document bestaat niet410 Gone— document is verwijderd403 Forbidden— geen toegang400 Bad Request— ongeldige query-parameters
POST /api/data/v2/{table} — Aanmaken
Body: JSON-object met de velden van het document (zonder metadata-velden zoals entityId, createdDate).
Response:
200 OKmet het aangemaakte document (inclusief gegenereerdeentityIden andere metadata)400 Bad Requestmet een lijst veldspecifieke validatiefouten403 Forbidden— geen create-rechten
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:
200 OK— bijgewerkt404 Not Found— document bestaat niet409 Conflict— er is intussen door iemand anders gewijzigd (op basis van ETag)400 Bad Request— validatiefouten403 Forbidden
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:
200 OK— verwijderd404 Not Found403 Forbidden409 Conflict— Er zijn verwijzingen naar dit document, en het kan daarom niet verwijderd worden.
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:
countwerkt op elk veldsum,average,min,maxwerken alleen op numerieke velden (int/decimal)
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:
- Aanbevolen: als HTTP-header
X-ContinuationToken: <token>(kan veel langer zijn dan een URL toestaat). - Alternatief: als query-parameter
continuationToken=<token>.
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:
orderBy=editedDate:desc— nieuwste eerstorderBy=license_plate— alfabetisch oplopend op kentekenorderBy=address.postal_code:desc— sorteren op een veld in een gerelateerd document
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
- Direct:
license_plate - Geneste velden:
address.postal_code, dit kan ook verder genest worden (bijv.customer.address.postal_code) - Foreign keys:
customer EQ "<entityId>"filtert op de FK - Metadata:
editedDate,createdDate,entityId,createdByUserId,createdByUserName,editedByUserId,editedByUserName
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
- Strings: tussen aanhalingstekens, bijv.
"AB-12-CD". Aanhalingstekens worden ge-escaped met\. - Getallen: kaal, bijv.
123of12.5. - Booleans:
trueoffalse. null(letterlijk).- Arrays (voor
IN/NIN): JSON-array, bijv.IN ["AB-12-CD","XY-99-ZZ"]. - Tuple voor
BETWEEN: tweelement-array, bijv.BETWEEN [100, 200]. - Datums: UNIX-epoch in milliseconden (number).
Booleaanse combinaties
AND(synoniem&&of+)OR(synoniem||)- Haakjes voor groepering
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.
- Comma-separated lijst van veldpaden.
- Dot-notatie voor geneste velden.
*als laatste segment om "alles onder dit pad" te selecteren.
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
400-fouten staat in de body welke parameter of welk veld ongeldig is. - Bij
403heeft de geauthenticeerde gebruiker niet de juiste rechten. - Bij
404bestaat de tabel niet (in v2-routes) of het document niet. - Bij
409is er een conflict (concurrent update of FK-referentie).
Bij 4xx fouten heeft het geen zin de request opnieuw te proberen.
- Bij
5xxis er een serverfout — wacht en probeer opnieuw, of neem contact op als het probleem aanhoudt.