API-Leitfaden für Entwickler
So lest und schreibt ihr docket über die GraphQL-API: Listen, Filter und Seiten, Zeit buchen, Tickets anlegen, Rechnungen lesen und wie ein Fehler aussieht.
Dieser Leitfaden zeigt die Aufrufe, mit denen die meisten Automationen anfangen. Wie ihr die API einschaltet und einen Schlüssel anlegt, steht unter API und Webhooks. Jedes Beispiel wird gegen dockets Schema geprüft, sobald sich docket ändert.
Die Grundlagen
- Eine Adresse:
POST https://euerworkspace.docket.rocks/graphqlmitAuthorization: Bearer dkt_…und einem JSON-Body{ "query": "…", "variables": { … } }. - Ein Schlüssel ist eine Person. Er sieht und darf, was seine Besitzerin oder sein Besitzer in
der App darf: Der Schlüssel eines Projektmanagers liest Rechnungen, der eines Zeiterfassers sieht
nur die eigenen Zeiten. Was die Rolle nicht erlaubt, kommt als
nulloder als Fehler zurück, nie als fremde Daten. - Die Referenz ist das Schema:
GET /graphql/schemaliefert es als Text, mit einer Beschreibung an jeder Abfrage, jeder Mutation und den Objekten, die sie zurückgeben. Die Beschreibungen sind englisch. Ladet es in euren GraphQL-Client, dann ergänzt er Felder von selbst. - IDs sind UUIDs, dieselben in der API, in Webhook-Ereignissen und in den Adressen der App.
- Daten sind
JJJJ-MM-TTin der Zeitzone eures Workspace. Dauern sind Minuten. Beträge sind netto in der Währung eures Workspace, außer ein Feld sagt brutto.
Anmelden
Für eigene Integrationen gibt es einen Weg: einen persönlichen API-Schlüssel im Header
Authorization. Keine Cookies, kein Login-Aufruf, kein Token, das erneuert werden muss. Ein
Schlüssel gilt, bis die Person ihn löscht oder archiviert wird oder ein Admin die API ausschaltet.
dockets eigene Programme melden sich anders an: die Web-App mit einer Sitzung nach E-Mail, Passwort und zweitem Faktor, die Mac-App mit denselben Zugangsdaten und KI-Apps über eine Zustimmung im Browser (siehe KI-Apps verbinden). Diese Wege gehören dockets Clients und können sich ohne Ankündigung ändern. Baut nicht darauf und schreibt kein Passwort in ein Skript: Ein Schlüssel lässt sich einzeln löschen und kann nie Nutzer, Einstellungen oder andere Schlüssel ändern.
Jede Abfrage und jede Mutation mit Beispielaufruf und Antwort, lesbar ohne Schlüssel, steht in der API-Referenz.
Wer bin ich
query {
viewer {
id
firstname
lastname
role
}
}
Listen: filtern, sortieren, blättern
Jede Liste nimmt dieselben Argumente.
query Projects($search: String, $page: Int) {
total: projects(filter: { search: $search, archived: false }) {
count
}
projects(filter: { search: $search, archived: false }, orderBy: ["title"], perPage: 50, page: $page) {
edges {
node {
id
pnr
title
slug
customer {
id
company
}
}
}
}
}
- Summen kommen aus einem zweiten Feld ohne
perPage(obentotal): Auf einer geblätterten Liste sehencountundsumnur die Seite. Fragtpage1, 2, 3 ab, bis ihrtotal.countEinträge habt. orderBynimmt Feldnamen, ein-davor sortiert absteigend (["-date"]).- Datumsfilter nehmen Vergleiche als Text:
">= 2026-10-01","<= 2026-10-31"oder einen einzelnen Tag. - ID-Filter nehmen Listen, die meisten haben einen Zwilling
…Not, der ausschließt.
Zeiteinträge eines Monats
query Time($project: [ID!], $dates: [String!]) {
total: timetrackings(filter: { projectIds: $project, dates: $dates }) {
count
minutes: sum(field: "duration")
}
timetrackings(filter: { projectIds: $project, dates: $dates }, orderBy: ["date"], perPage: 100, page: 1) {
edges {
node {
id
date
duration
description
user {
id
firstname
lastname
}
service {
id
title
}
ticket {
id
number
title
}
invoice {
id
number
}
}
}
}
}
Mit "dates": [">= 2026-10-01", "<= 2026-10-31"]. filter: { billing: [OPEN] } listet, was noch
keine Rechnung abgerechnet hat. invoice nennt die Rechnung eines abgerechneten Eintrags (für
Schlüssel, die Rechnungen sehen dürfen).
Zeit buchen
Zeit wird auf ein Ticket eines Projekts gebucht, mit einer Leistung (der Art der Arbeit) und einer Dauer in Minuten, in Schritten von 15.
mutation Book($input: CreateTimetrackingInput!) {
createTimetracking(input: $input) {
timetracking {
id
date
duration
}
errors
}
}
{
"input": {
"projectId": "…",
"ticketId": "…",
"serviceId": "…",
"date": "2026-11-12",
"duration": 90,
"description": "Startseite: Hero und Navigation"
}
}
Ohne userId gehört die Zeit der Person, der der Schlüssel gehört. Die IDs findet ihr vorher über
projects, die tickets eines Projekts und services.
Ein Ticket anlegen
mutation NewTicket($input: CreateTicketInput!) {
createTicket(input: $input) {
ticket {
id
number
title
}
errors
}
}
Mit { "input": { "projectId": "…", "title": "Kontaktformular prüfen", "text": "Das Formular antwortet mit einem Fehler." } }.
text ist einfacher Text, Leerzeilen trennen Absätze. Kommentare nehmen ihn auch (createTicketComment).
Rechnungen
query Invoices($year: Int) {
invoices(filter: { year: $year, statuses: ["published", "paid"] }, orderBy: ["-date"], perPage: 50, page: 1) {
edges {
node {
id
number
title
status
date
dueDate
}
}
}
}
Rechnungen lesen nur Projektmanager und Admins. Wollt ihr in dem Moment erfahren, in dem eine Rechnung veröffentlicht oder bezahlt wird, nehmt einen Webhook, statt immer wieder zu fragen.
Dateien
Wo die API eine Datei nennt (invoice { downloadUrl } an Fremdkosten, attachments { url } an
einem Ticket), gibt sie einen Pfad in eurem Workspace. Ruft ihn mit demselben Schlüssel ab:
GET https://euerworkspace.docket.rocks/rails/active_storage/blobs/redirect/…/beleg.pdf
Authorization: Bearer dkt_…
Die Antwort leitet zur Datei weiter, euer Werkzeug muss also Weiterleitungen folgen. Ein Schlüssel bekommt die Dateien, die seine Person in der App öffnen darf: einen Beleg nur mit dem Schlüssel eines Projektmanagers oder Admins. Ohne Schlüssel, oder mit einem, der es nicht darf, ist die Antwort 404. Dateiabrufe zählen zu den 120 Anfragen pro Minute des Schlüssels.
Wenn etwas schiefgeht
| Was ihr bekommt | Was es heißt |
|---|---|
| HTTP 401 | Der Schlüssel fehlt, ist falsch oder gelöscht, oder die API ist für den Workspace ausgeschaltet. |
| HTTP 429 | Mehr als 120 Anfragen in einer Minute mit diesem Schlüssel. Wartet und versucht es erneut. |
HTTP 200 mit errors ganz oben in der Antwort |
Die Abfrage passt nicht zum Schema, oder die Person hinter dem Schlüssel darf das nicht. |
HTTP 200 mit errors in der Antwort der Mutation |
Der Datensatz wurde nicht gespeichert. errors nennt jedes Feld mit seinen Meldungen, wie das Formular in der App sie zeigen würde. |
Eine Mutation, die geklappt hat, antwortet mit errors: null. Fragt errors immer mit ab und
prüft es.
Was ein Schlüssel nicht kann
Workspace-Einstellungen, Nutzer und ihre Rollen, Verbindungen zu Jira und KI-Apps sowie API-Schlüssel und Webhooks selbst ändert ihr nur in der App. Diese Mutationen sagen das im Schema.
Verwandt: API und Webhooks · Benutzerrollen