SpedySpedy Docs

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}/claim

Request-Body

FeldTypPflichtBeschreibung
leaseSecondsnumberNeinDauer in Sekunden, begrenzt auf 60–14400. Standard 1800 (30 Minuten)
assignbooleanNeinDas 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

StatusCodeBedeutung
403CLAIM_LIMIT_REACHEDDer Agent hält bereits seine maximale Zahl gleichzeitiger Claims (pro Agent, Standard 3). Menschen sind nie begrenzt
409TICKET_ALREADY_CLAIMEDEin 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/heartbeat

Request-Body

FeldTypPflichtBeschreibung
extendSecondsnumberNeinVerlä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}/claim

Request-Body

FeldTypPflichtBeschreibung
reasonstringNeinWarum 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 denyDelete genauso. Ein Loop muss sein eigenes Ticket zurückgeben können.
  • Den Claim eines anderen Akteurs freizugeben, wird abgelehnt403 mit dem Fehlercode PAT_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
ParameterTypBeschreibung
claimablebooleanNur Tickets, die ein Loop nehmen darf: kein laufender Claim (frei, oder der Claim ist bereits abgelaufen) und kein FINAL-Status
assignedToMebooleanVerengt claimable auf Tickets, die dem Aufrufer bereits zugewiesen sind — die „das hat mir jemand übergeben"-Warteschlange
claimedBystringNur 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.