Ticket-Claims
Einen exklusiven, ablaufenden Claim auf ein Ticket nehmen, damit nie zwei Loops dasselbe bearbeiten.
Ein Claim ist ein exklusiver, ablaufender Anspruch auf ein Ticket. Er macht das Pull-Modell erst sicher: Ein Loop nimmt sich ein Ticket, arbeitet und gibt es zurück — und wenn der Loop stirbt, läuft der Claim von selbst ab, statt das Ticket dauerhaft zu blockieren.
Diese Routen liegen unter demselben Board-Präfix und denselben Board-Zugriffsregeln wie die Ticket-Routen und akzeptieren das Access-Token eines Agenten — genau dafür sind sie da.
Claimen, Verlängern und Freigeben brauchen jeweils die Berechtigung tickets:edit.
Ticket claimen
POST /api/v1/boards/{boardId}/tickets/{ticketId}/claimRequest-Body
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| leaseSeconds | number | Nein | Dauer in Sekunden, begrenzt auf 60–14400. Standard 1800 (30 Minuten) |
| assign | boolean | Nein | Das Ticket dem Claimenden zuweisen, wenn es unzugewiesen ist. Nimmt nie einen bestehenden Verantwortlichen weg. Standard false |
Beispiel-Antwort (201)
{
"ticket": {
"id": "tkt_abc123",
"displayId": "WEB-42",
"title": "Login-Seite: Styling reparieren",
"boardId": "brd_web",
"statusId": "sts_def456",
"assigneeId": "usr_agent123"
},
"claim": {
"claimedBy": {
"id": "usr_agent123",
"name": "Nightly Refactor",
"isAgent": true,
"agentOwnerId": "usr_abc123"
},
"claimedAt": "2026-09-05T08:00:00Z",
"claimExpiresAt": "2026-09-05T08:30:00Z",
"claimHeartbeatAt": null,
"claimCount": 4
},
"alreadyHeld": false,
"assigned": true
}alreadyHeld ist true, wenn du den Claim schon hieltest und dieser Aufruf ihn nur verlängert hat — ein Loop, der nach einem Neustart sein eigenes Ticket wieder claimt, ist kein Konflikt.
Der Claim hält fest, über welchen Client er genommen wurde. Deshalb kann die Delivery-Spur in der Claim-Zeile „über claude-code" anzeigen, statt nur den Akteur zu nennen. Über MCP kommt dieser Name aus dem initialize-Handshake des Clients; über REST gibt es keinen Handshake, dort kommt er aus dem clientLabel des Tokens. In beiden Fällen ist er Anzeige-Metadatum, nie eine Berechtigung.
Fehler
| Status | Code | Bedeutung |
|---|---|---|
| 403 | CLAIM_LIMIT_REACHED | Der Agent hält bereits seine maximale Zahl gleichzeitiger Claims (pro Agent, Standard 3). Menschen sind nie begrenzt |
| 409 | TICKET_ALREADY_CLAIMED | Ein anderer Akteur hält einen laufenden Claim. details nennt Halter und Ablaufzeit, damit ein Loop warten oder weiterziehen kann |
Claim verlängern (Heartbeat)
POST /api/v1/boards/{boardId}/tickets/{ticketId}/claim/heartbeatRequest-Body
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| extendSeconds | number | Nein | Verlängert auf so viele Sekunden ab jetzt, begrenzt auf 60–14400. Standard 1800 |
Antwortet mit 200 und dem claim-Objekt von oben. Ruf ihn deutlich vor Ablauf auf — etwa alle ⅓ der Claim-Dauer.
Er kann einen abgelaufenen Claim bewusst nicht wiederbeleben. Bis dahin gehört das Ticket womöglich einem anderen Loop, und es stillschweigend zurückzunehmen hieße, zwei Loops auf ein Ticket zu setzen. Dann lieber neu claimen.
Fehler: 409 CLAIM_NOT_HELD (kein laufender Claim auf dem Ticket) · 409 TICKET_ALREADY_CLAIMED (jetzt hält ihn jemand anderes).
Claim freigeben
DELETE /api/v1/boards/{boardId}/tickets/{ticketId}/claimRequest-Body
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| reason | string | Nein | Warum der Claim freigegeben wird. Landet in der Ticket-Historie (max. 500 Zeichen) |
Antwortet mit 200 und { "released": true }. Erlaubt für den Halter, den Eigentümer des haltenden Agenten und Organisations-Admins — ein Loop, der ohne Freigabe gestorben ist, darf nicht den Support brauchen.
Tokens mit denyDelete
Agenten-Tokens tragen standardmäßig denyDelete, und dies ist eine DELETE-Route. Die Regel ist enger als die Methode:
- Den eigenen Claim freizugeben, funktioniert immer — mit
denyDeletegenauso. Ein Loop muss sein eigenes Ticket zurückgeben können. - Den Claim eines anderen Akteurs freizugeben, wird abgelehnt —
403mit dem FehlercodePAT_DENY_DELETE. Einem fremden Loop ein Ticket wegzunehmen, ist ein administrativer Akt, keine Loop-Routine.
Claimbare Arbeit finden
Die Claim-Filter liegen auf der board-übergreifenden Issues-Liste, damit Mensch und Loop dieselbe Warteschlange sehen:
GET /api/v1/issues?claimable=true
GET /api/v1/issues?claimable=true&assignedToMe=true
GET /api/v1/issues?claimedBy=me| Parameter | Typ | Beschreibung |
|---|---|---|
| claimable | boolean | Nur Tickets, die ein Loop nehmen darf: kein laufender Claim (frei, oder der Claim ist bereits abgelaufen) und kein FINAL-Status |
| assignedToMe | boolean | Verengt claimable auf Tickets, die dem Aufrufer bereits zugewiesen sind — die „das hat mir jemand übergeben"-Warteschlange |
| claimedBy | string | Nur Tickets, die aktuell von dieser User-ID gehalten werden, oder me für den Aufrufer |
Ein abgelaufener Claim zählt sofort als claimbar, ohne auf den Aufräum-Job zu warten. claimedBy=me ohne auflösbaren Aufrufer trifft nichts, statt zu „alle geclaimten Tickets" zu verwässern.
Dieselben drei Filter gibt es an den MCP-Tools tickets_list und tickets_search, auf einer gemeinsamen Implementierung — ein Loop, der die eine Oberfläche abfragt und über die andere claimt, sieht nie eine andere Menge.
Ablauf
Der Aufräum-Job räumt abgelaufene Claims binnen etwa einer Minute weg und hinterlässt eine Notiz am Ticket. Dabei verschickt Spedy die Benachrichtigung TICKET_CLAIM_EXPIRED an den Verantwortlichen des Tickets und an den Eigentümer des Agenten — ein Loop, der ständig stirbt, wird sichtbar, ohne dass jemand ein Dashboard beobachten muss.
Jede Ticket-Antwort trägt einen claim-Block, der null ist, wenn das Ticket frei ist oder wenn der Claim abgelaufen ist. Niemand muss Zeitstempel vergleichen, um zu wissen, ob ein Ticket verfügbar ist.
Agenten
Agenten-User ohne Seat anlegen, Leitplanken konfigurieren, eingeschränkte Access-Tokens erzeugen und die organisationsweite Agenten-Übersicht lesen.
Delivery-Spur
Eine geordnete Chronik, wie ein Ticket tatsächlich geliefert wurde -- Claims, Meldungen, Kommentare, Statuswechsel, Branch, PR, Pipelines und gebuchte Zeit.