API
Met de API kan andere software met de tickets in je LeanTrack werken:
projecten en tickets lezen, tickets aanmaken en wijzigen, reacties en
bestanden toevoegen. Hij praat JSON over HTTPS, onder
/api/v1/ van het adres van je LeanTrack.
Een sleutel
Maak een sleutel onder Mijn account → API-sleutels. Kopieer hem meteen: hij wordt maar één keer getoond. Een sleutel werkt namens jou: hij ziet dezelfde projecten en mag hetzelfde als jij, en alles wat hij wijzigt staat op jouw naam in de geschiedenis.
Maak voor software een aparte gebruiker (bijvoorbeeld "Merxo") die alleen de projecten ziet die nodig zijn, log één keer in als die gebruiker en maak daar de sleutel. Admins zien de sleutels van elke gebruiker onder Gebruikers en kunnen ze intrekken; een gebruiker deactiveren stopt de sleutels van die gebruiker. De API kan helemaal uit onder Instellingen → Onderdelen.
Bij het maken van een sleutel kun je hem beperken tot een deel van je
projecten: hij bereikt dan alleen die, en alleen zolang jij ze nog ziet.
Zonder beperking bereikt hij elk project dat jij ziet, ook die je later
krijgt. GET me vertelt welke projecten een sleutel bereikt.
De sleutel meesturen
Elk verzoek stuurt de sleutel mee in de header Authorization:
curl -H "Authorization: Bearer ltk_…" https://tickets.example.com/api/v1/me{
"id": 7,
"username": "Merxo",
"role": "user",
"language": "en",
"leantrack_version": "0.20.45",
"key": { "name": "Merxo", "projects": [3, 5] },
"features": { "subtickets": true, "priority": true, "assignee": true, "api": true }
}
Endpoints
Allemaal onder /api/v1/.
| Verzoek | Wat het doet |
|---|---|
GET me | De gebruiker van de sleutel: naam, rol, taal, welke onderdelen aan staan; en welke projecten de sleutel bereikt ("all", of hun ID's). |
GET projects | De projecten die de gebruiker van de sleutel mag zien. |
GET projects/{id} | Eén project, met de prefixen, statussen (op volgorde), groepen, gebruikers aan wie je kunt toewijzen en de gebruikte tags. |
GET tickets | Tickets, 50 per pagina, nieuwste wijziging eerst. Filters: project, status, assignee, priority, q, tag, group, page. |
POST tickets | Een ticket aanmaken (of een sub-ticket, met parent_id). |
GET tickets/{id} | Eén ticket met alles: omschrijving, reacties, bijlagen, sub-tickets, volgers en geschiedenis. |
PATCH tickets/{id} | Een ticket wijzigen: alleen de velden die je meestuurt. |
DELETE tickets/{id} | Een ticket verwijderen (terughalen kan op de pagina Verwijderd). |
POST tickets/{id}/comments | Een reactie toevoegen. |
PATCH comments/{id} | Je eigen reactie wijzigen. |
DELETE comments/{id} | Je eigen reactie verwijderen. |
POST tickets/{id}/attachments | Bestanden bijvoegen (multipart-formulier, veld file of files[]). |
GET attachments/{id} | Een bijlage downloaden. |
DELETE attachments/{id} | Een bijlage weghalen (die van jou, of elke als admin). |
POST tickets/{id}/watch | Een ticket volgen. |
DELETE tickets/{id}/watch | Een ticket niet meer volgen. |
Voorbeelden
Tickets opvragen
Zonder status krijg je de tickets die niet gesloten zijn;
status=all geeft ze allemaal. assignee neemt een
gebruikers-ID, none of me; q zoekt
zoals het zoekveld bovenin.
curl -H "Authorization: Bearer ltk_…" "https://tickets.example.com/api/v1/tickets?project=3&assignee=me"{
"tickets": [
{
"id": 42,
"key": "WEB-42",
"label": "Invoice PDF shows the wrong VAT",
"project": { "id": 3, "name": "Webshop" },
"prefix": "WEB",
"status": { "code": "in_progress", "label": "In Progress" },
"priority": "high",
"assignee": { "id": 7, "username": "Merxo" },
"reporter": { "id": 2, "username": "anna" },
"parent_id": null,
"tags": ["invoice"],
"created_at": "2026-10-19T09:12:00Z",
"updated_at": "2026-10-19T14:03:27Z",
"closed_at": null,
"deleted": false,
"url": "https://tickets.example.com/ticket/42"
}
],
"page": 1,
"pages": 1,
"per_page": 50,
"total": 1
} GET tickets/{id} geeft hetzelfde plus description,
resolution, groups, comments,
attachments (met een download-url),
subtickets, watchers en history.
Een ticket aanmaken
curl -X POST -H "Authorization: Bearer ltk_…" -H "Content-Type: application/json" \
-d '{"project_id": 3, "prefix": "WEB", "label": "Customer cannot log in",
"description": "<p>Since this morning.</p>", "priority": "high", "tags": ["login"]}' \
https://tickets.example.com/api/v1/tickets Het antwoord is 201 met het hele nieuwe ticket, zoals bij
GET tickets/{id}.
Een ticket wijzigen
Stuur alleen mee wat verandert. Dezelfde controles als in het bewerkformulier gelden: een ticket kan bijvoorbeeld niet gesloten worden zolang een blokkerend sub-ticket nog open staat.
curl -X PATCH -H "Authorization: Bearer ltk_…" -H "Content-Type: application/json" \
-d '{"status": "review", "assignee_id": null}' https://tickets.example.com/api/v1/tickets/42
Reageren, bijvoegen, volgen
curl -X POST -H "Authorization: Bearer ltk_…" -H "Content-Type: application/json" \
-d '{"body": "<p>Fixed in release 2.4.</p>"}' https://tickets.example.com/api/v1/tickets/42/comments
curl -X POST -H "Authorization: Bearer ltk_…" -F "file=@screenshot.png" \
https://tickets.example.com/api/v1/tickets/42/attachments
curl -X POST -H "Authorization: Bearer ltk_…" https://tickets.example.com/api/v1/tickets/42/watch
Velden van een ticket
| Veld | Waarde |
|---|---|
label | tekst, verplicht |
project_id | getal, verplicht bij aanmaken (niet voor een sub-ticket) |
prefix | de prefixcode, bijvoorbeeld WEB (of prefix_id), verplicht bij aanmaken |
description | HTML |
resolution | HTML |
status | een statuscode uit GET projects/{id}; nieuwe tickets beginnen in de eerste |
priority | low, mid, high |
assignee_id | een gebruikers-ID uit GET projects/{id}, of null voor niemand |
tags | een lijst met namen, bijvoorbeeld ["bug", "factuur"] |
groups | een lijst met groep-ID's van het project van het ticket |
parent_id | alleen bij aanmaken: maakt er een sub-ticket van |
blocks_parent | true/false, alleen sub-tickets: het hoofdticket kan niet dicht zolang dit sub-ticket open staat |
Velden die de API niet kent worden geweigerd (422), zodat een
tikfout niet onopgemerkt blijft. Een ticket kan niet naar een ander
hoofdticket verhuizen — op de pagina's ook niet.
Fouten
Elke fout is JSON met een melding, en bij controles de velden die niet kloppen:
{
"message": "Give the ticket a label. Choose a prefix.",
"errors": {
"label": "Give the ticket a label.",
"prefix_id": "Choose a prefix."
}
} | Status | Betekenis |
|---|---|
400 | De body is geen JSON-object. |
401 | Geen sleutel, een onbekende of ingetrokken sleutel, of de gebruiker van de sleutel is gedeactiveerd. |
403 | Niet toegestaan, bijvoorbeeld de reactie van iemand anders wijzigen. |
404 | Bestaat niet, of de gebruiker van de sleutel mag het niet zien (bewust hetzelfde antwoord). Ook als de API uit staat. |
409 | Het ticket is verwijderd: haal het eerst terug. |
422 | Een controle is mislukt; errors zegt welk veld en waarom. |
429 | Meer dan 120 verzoeken in een minuut met deze sleutel. Retry-After zegt hoeveel seconden je moet wachten. |
Goed om te weten
- Limiet: 120 verzoeken per minuut per sleutel. Elk
antwoord heeft
X-RateLimit-LimitenX-RateLimit-Remaining. - HTML: omschrijvingen en reacties zijn HTML en worden
net zo opgeschoond als die uit de editor:
p, br, strong, b, em, i, u, s, h1–h3, ul, ol, li, blockquote, pre, codeen links blijven staan, de rest wordt weggehaald. - Datums zijn UTC, bijvoorbeeld
2026-10-19T14:03:27Z. - Meldingen van de API zijn altijd in het Engels.
- E-mails aan volgers gaan bij wijzigingen via de API net zo de deur uit als bij wijzigingen op de pagina's.
- Op Apache met PHP als FastCGI geeft de meegeleverde
.htaccessde headerAuthorizationdoor aan PHP; andere webservers moeten dat ook doen.