SpedySpedy Docs

Delivery Trace

One ordered timeline of how a ticket actually got delivered -- claims, reports, comments, status moves, the branch, the PR, pipelines and booked time.

The delivery trace answers "how did this ticket get done, and who did what" in one ordered list. It aggregates records other modules already own — it stores nothing of its own and changes nothing.

Get the Trace

GET /api/v1/boards/{boardId}/tickets/{ticketId}/delivery-trace

Requires the tickets:view permission and access to the board. Accepts an agent's access token, so a loop can read the story before deciding whether to start.

Customers cannot read it403, by role, not by filtering the payload. The trace is engineering process (which machine held the ticket, how long its lease ran, how many minutes are still drafts); customers see time through the deliberately coarser customer overview.

Example Response

{
  "ticketId": "tkt_abc123",
  "displayId": "WEB-42",
  "branch": "feature/web-42-login-styling",
  "items": [
    {
      "at": "2026-09-05T08:00:00Z",
      "kind": "claimed",
      "label": "Claimed for 30 minutes via claude-code",
      "actor": {
        "id": "usr_agent123",
        "name": "Nightly Refactor",
        "isAgent": true,
        "clientName": "claude-code"
      },
      "ref": null,
      "refType": null
    },
    {
      "at": "2026-09-05T08:26:00Z",
      "kind": "reported",
      "label": "Reported needs_review",
      "actor": { "id": "usr_agent123", "name": "Nightly Refactor", "isAgent": true, "clientName": "claude-code" },
      "ref": "https://github.com/acme/web/pull/312",
      "refType": "url"
    }
  ],
  "gaps": ["coding_tool_handoff"]
}

Fields

FieldTypeDescription
ticketIdstringThe ticket
displayIdstring | nullSpeaking key, e.g. WEB-42
branchstring | nullSource branch of the linked pull request, if there is one
itemsobject[]Ascending by at
gapsstring[]Sources not represented in this trace, so a reader can tell "did not happen" from "not recorded"

Item

FieldTypeDescription
atstringISO 8601 timestamp
kindstringSee the list below. Treat an unknown kind as "render the label" — the list grows
labelstringShort, already-composed English label. Machine output, not translated
actor.idstring | nullSpedy user id; null for actors known only by name (a git author, the system)
actor.namestringDisplay name, or System when nothing acted
actor.isAgentbooleanWhether the actor is an agent user
actor.clientNamestring | nullThe MCP client the action came through, e.g. claude-code. null for the web UI
refstring | nullWhat the item points at: an absolute URL, a ticket key, a branch name, a status name
refType"url" | "key" | nullHow to treat ref

Kinds

claimed · heartbeat · claim_released · claim_expired · reported · first_agent_comment · first_human_comment · status_changed · branch · pr_opened · pr_review · pr_merged · pipeline · time_entry

Time entries appear including drafts, marked as drafts. Claims carry the client they were taken through, which is why a claim row can read "via claude-code" rather than just naming the actor.

Gaps

gaps is the honest part. A trace that simply omitted an unrecorded source would look identical to a trace where nothing happened, and it would silently start leaking the next time a source is added. Anything Spedy does not record at all is named here instead — today that always includes coding_tool_handoff.

In the product

The same timeline is the collapsible Delivery trace section on a ticket's Activity tab, and the same data drives the Agents overview. Over MCP a loop reads it with the tickets_delivery_trace tool.