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.

Elke LeanTrack heeft dezelfde uitleg ingebouwd: Mijn account → API-sleutels → Hoe de API werkt, met je eigen adres erin.

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/.

VerzoekWat het doet
GET meDe gebruiker van de sleutel: naam, rol, taal, welke onderdelen aan staan; en welke projecten de sleutel bereikt ("all", of hun ID's).
GET projectsDe 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 ticketsTickets, 50 per pagina, nieuwste wijziging eerst. Filters: project, status, assignee, priority, q, tag, group, page.
POST ticketsEen 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}/commentsEen reactie toevoegen.
PATCH comments/{id}Je eigen reactie wijzigen.
DELETE comments/{id}Je eigen reactie verwijderen.
POST tickets/{id}/attachmentsBestanden 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}/watchEen ticket volgen.
DELETE tickets/{id}/watchEen 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

VeldWaarde
labeltekst, verplicht
project_idgetal, verplicht bij aanmaken (niet voor een sub-ticket)
prefixde prefixcode, bijvoorbeeld WEB (of prefix_id), verplicht bij aanmaken
descriptionHTML
resolutionHTML
statuseen statuscode uit GET projects/{id}; nieuwe tickets beginnen in de eerste
prioritylow, mid, high
assignee_ideen gebruikers-ID uit GET projects/{id}, of null voor niemand
tagseen lijst met namen, bijvoorbeeld ["bug", "factuur"]
groupseen lijst met groep-ID's van het project van het ticket
parent_idalleen bij aanmaken: maakt er een sub-ticket van
blocks_parenttrue/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."
    }
}
StatusBetekenis
400De body is geen JSON-object.
401Geen sleutel, een onbekende of ingetrokken sleutel, of de gebruiker van de sleutel is gedeactiveerd.
403Niet toegestaan, bijvoorbeeld de reactie van iemand anders wijzigen.
404Bestaat niet, of de gebruiker van de sleutel mag het niet zien (bewust hetzelfde antwoord). Ook als de API uit staat.
409Het ticket is verwijderd: haal het eerst terug.
422Een controle is mislukt; errors zegt welk veld en waarom.
429Meer dan 120 verzoeken in een minuut met deze sleutel. Retry-After zegt hoeveel seconden je moet wachten.

Goed om te weten