API
Everything the web app does goes through this HTTP API, so you can script it too: create and move tickets, read boards and sprints, run reports. It’s the API the terminal app and AI agents use.
Authentication
Send an API key as a bearer token: Authorization: Bearer tl_…. Personal and agent keys are yours: make them in Your settings → API keys. Service keys belong to the organization, and its admins make them in the organization’s settings.
- Personal keys (
tl_p_…) are you: you see and change what you can in the web app, and what they change is recorded as you. - Agent keys (
tl_ai_…) are your AI agent, working alongside you: they can be assigned tickets, and their work is recorded as the agent. Others see it with your name (Claude (you)). - Service keys (
tl_s_…) belong to the organization, for scripts and integrations such as Bugfixes, and their writes carry the key’s name. Everyone in the organization sees each one’s name, the first few characters of the key, when it was last used and when it expires; the key itself is shown once, when it’s made. - A read-only key gets
403for anything butGET. Keys expire; their expiry shows next to them. - Each key has a per-minute limit, in every response’s
X-RateLimit-LimitandX-RateLimit-Remaining. Over it:429withRetry-After. - Times are UTC. Send
Tracklines-Timezone: Europe/London(any IANA zone) for day-based rules, such as a sprint’s last day, to use your day.
The API’s description, for code generators and API clients: openapi.yaml (also as JSON). Base URL: https://api.tracklin.es/api.
The details, from the API’s description
Complete HTTP API for projects, boards, tickets, roadmaps, billing, and integrations. The API also supports the BugFixes workflow: create in a board column or project backlog, append conversation updates, and close a board ticket by moving it to the board's last (Done) column.
API-key requests are rate limited per key (default 300 a minute, set with API_KEY_RATE_LIMIT): over the limit answers 429 with Retry-After, and every response carries X-RateLimit-Limit / X-RateLimit-Remaining. MCP tool calls count like the REST calls they make. Browser sessions aren't limited.
Most private endpoints accept a Clerk session token or a TrackLin.es (tl_ai_… for AI agents, tl_s_… for services, tl_p_… for personal keys; older keys are tl_…)
or BugFixes (bf_…) agent API key as a Bearer token. Projects, boards and
roadmaps belong to a Clerk organization: a session sees its active
organization's data, and a request without an active organization gets
403. API keys act inside the organization they were created in. Personal and
agent keys belong to the person who made them: a personal key is them, so its
writes are theirs; an agent key is their AI agent, so its writes are the agent's
(shown with its owner's name to everyone else). Service keys belong to the
organization, are made by its admins, and attribute writes to the key's name. A read-only key gets 403 for anything but GET. API-key management
requires a Clerk session. Stripe webhooks use Stripe-Signature.
Timestamps are stored and returned in UTC. Day-based rules (a sprint's last
day) use the caller's timezone from the optional Tracklines-Timezone
header (an IANA name such as Europe/London); without it, or for an unknown
zone, UTC.
On a sprint's last day (in that timezone, or once ends_at has passed) its
scope is locked: moving a ticket into it answers 409, and a ticket created
in one of its board's columns goes to the bottom of the project backlog
instead (the 201 body has redirected_to_backlog: true).
IDs are UUIDs. Error responses are plain-text messages.
Endpoints
Contact
The public contact form; messages go to the TrackLin.es team.
post/enquiriesSend a contact-form message to the TrackLin.es team(public)
Public, no account needed, and rate limited per address. Filed in the ChewedFeed HQ enquiry inbox.
204Sent.400A required field is missing429Too many messages from this address.502The enquiry system couldn't be reached; try again.503Enquiries aren't configured.
Projects
Create, list, inspect, update, and delete projects.
get/projectsList projects accessible to this agent
post/projectsCreate a project
get/projects/{projectId}Get a project with its boards and roadmaps
patch/projects/{projectId}Update a project
key_prefix (optional) renames every ticket key in the project (TL-123 → TRK-123); only organization admins can change it. Use 2–6 capital letters or digits starting with a letter; 409 if another project in the org uses it.
projectIdpath, required string
delete/projects/{projectId}Permanently delete a project and its boards, tickets, roadmaps, repos, and intake
This action cannot be undone and permanently deletes all work under the project. Organization admin session required.
projectIdpath, required string
get/projects/{projectId}/labelsLabels in use in a project, most used first
get/projects/{projectId}/liveStream the project's hosted refinement session
Server-sent events, like the board stream: the latest retained event on connect, then updates, with a heartbeat every 20 seconds. Events are refinement-session (the whole session) and refinement-session-ended.
projectIdpath, required string
get/projects/{projectId}/live/refinementThe project's running refinement session, with who's here
projectIdpath, required string
200The session. HostedSession404No session running503Valkey is unavailable.
post/projects/{projectId}/live/refinementStart a refinement session and host it
projectIdpath, required string
201Started; the caller is the host. HostedSession404Resource not found or not owned by this API key. Error409A session is already running; join it instead.503Valkey is unavailable.
delete/projects/{projectId}/live/refinementEnd the session for everyone (host only)
projectIdpath, required string
204Ended.403Only the host can end it.404No session running.
put/projects/{projectId}/live/refinement/focusSay what the host has open, so followers see it (host only)
projectIdpath, required string
200The session. HostedSession403Only the host can set it.404No session running.
put/projects/{projectId}/live/refinement/hostHand over hosting, or take over from a host who has gone
The host can hand over to anyone in the session. Anyone in the session can make themselves host once the host hasn't been seen for a minute.
projectIdpath, required string
200The session with its new host. HostedSession400That person isn't in the session.403Only the host can hand over (or take over from a host who has gone).404No session running.
post/projects/{projectId}/live/refinement/joinJoin the session, and stay in it
Call every 30 seconds while taking part; anyone not seen for a minute drops off the list. A host gone for two minutes ends the session.
projectIdpath, required string
200The session HostedSession404No session running.
post/projects/{projectId}/live/refinement/leaveLeave the session
projectIdpath, required string
204Left.
get/projects/{projectId}/reposRepositories linked to the project
projectIdpath, required string
200Linked repositories. ProjectRepo[]
post/projects/{projectId}/reposLink a repository the org's connection can see (org admins); one project per repo in an org
projectIdpath, required string
201Linked. ProjectRepo400The connection can't see that repository.409Already linked to a project in this org
delete/projects/{projectId}/reposUnlink a repository (org admins)
projectIdpath, required stringproviderquery, required string (github | bitbucket)full_namequery, required string
204Unlinked.404Resource not found or not owned by this API key. Error
Organizations
Inspect membership in the active Clerk organization.
get/google-chatWhether a Google Chat space is set up (any member); never returns its URL
project_idquery string · The project whose space this is; leave out for the whole organization's space.
200The space's settings. object
put/google-chatPost notifications into a Google Chat space (org admins)
url is the space's incoming webhook (https://chat.googleapis.com/v1/spaces/…); leave it out to change only the kinds. Defaults: created, review, approval, over_capacity, finished, wont_do. A project's space gets its events; the org's gets every project's.
project_idquery string · The project whose space this is; leave out for the whole organization's space.
204Saved.400Not a Google Chat webhook URL403Not a signed-in org admin.
delete/google-chatStop posting to a Google Chat space (org admins)
project_idquery string · The project whose space this is; leave out for the whole organization's space.
204Removed.
post/google-chat/testPost a test message to a Google Chat space (org admins)
204Sent.409No space set up.502Google Chat refused it (the reason is in the body).
get/integrations/google-calendarGoogle Calendar (TL-404) for the org; any member. The admin email is for org admins only.
200Whether the server offers it and the org is connected, plus what a Workspace admin authorizes for domain-wide delegation. object
put/integrations/google-calendarConnect Google Calendar (org admins). Google is asked first, so only a working connection is saved.
204Connected.400Bad input403Not an org admin.502Google couldn't be reached.503The server has no Google service account.
delete/integrations/google-calendarDisconnect Google Calendar (org admins); the time away it found is deleted with it.
204Disconnected.
post/integrations/google-calendar/syncSync time away from Google Calendar now (org admins); it also runs hourly and right after connecting.
200The connection404Not connected.503The server has no Google service account.
post/integrations/google-calendar/testCheck Google still lets us read the org's calendars (org admins).
204Access works.400Google refused; the message says why.404Not connected.
get/integrations/{provider}Whether the org is connected to a provider (any org member); never returns the secret
providerpath, required string (bugfixes)
200Connection status. object403Not a signed-in org admin.
put/integrations/{provider}Connect the org to a provider (org admins). Bugfixes takes an access token from its settings.
The token is sealed (AES-GCM) with the server's TRACKLINES_SECRET_KEY before it's stored; 503 when the server has no key.
providerpath, required string (bugfixes)
204Connected.400Invalid request body or field values. Error403Not a signed-in org admin.503Integrations aren't configured on this server.
delete/integrations/{provider}Disconnect the org from a provider (org admins)
For GitHub, ?installation=<id> disconnects just that account; without it, every GitHub account is disconnected.
providerpath, required string (bugfixes)installationquery string · GitHub only: the installation (account) to disconnect.
204Disconnected.403Not a signed-in org admin.404That GitHub account isn't connected to this org.
post/integrations/{provider}/completeFinish connecting after the provider sends the admin back
providerpath, required string (github | bitbucket)
204Connected.400The state expired or is for another org.403Not a signed-in org admin.502The provider refused (e.g. not this app's installation
get/integrations/{provider}/connectWhere an org admin goes to install the GitHub App or authorize Bitbucket
The link carries a sealed state tied to the org (15 minutes). 503 when the provider isn't configured on the server.
providerpath, required string (github | bitbucket)
200The link. object403Not a signed-in org admin.503Provider not configured.
get/integrations/{provider}/reposRepositories the org's connection can see (org admins), first 100
providerpath, required string (github | bitbucket)
200Repositories. object[]409Not connected.
get/invitationsList pending invitations sent to the caller
Uses the caller's verified Clerk email addresses and does not require an active organization.
200Pending invitations addressed to the caller. Invitation[]401Missing, invalid, or revoked API key. Error403API keys cannot inspect or accept invitations.502Clerk could not load the caller's verified email addresses.
get/organizations/adminsList the organization's TrackLin.es admins
Organizations and their members live in Clerk; TrackLin.es only records which members are admins. The first time an org's admins are needed, one is chosen from Clerk and stored: the user who created the organization if they are still a member, otherwise the longest-standing member with the Clerk org:admin role, otherwise the longest-standing member. granted_by records which rule applied (clerk:creator, clerk:admin-role, clerk:earliest-member) or the admin who granted it. If Clerk can't be reached nothing is stored and the next request tries again (502); an organization with no members gets no admin (409).
200Admins oldest first OrgAdmin[]403No active organization.409The organization has no members to make admin.502Clerk could not be reached.
put/organizations/admins/{userId}Make an organization member an admin
Org admins only, signed in with a Clerk session (API keys are refused). The user must be a Clerk member of the organization.
userIdpath, required string
204Granted (or already an admin).403Not a signed-in org admin.422The user is not a member of this organization.
delete/organizations/admins/{userId}Remove an admin
Org admins only, signed in with a Clerk session. The last admin can't be removed.
userIdpath, required string
204Removed.403Not a signed-in org admin.409That is the organization's last admin.
get/organizations/invitationsList invitations created for the active organization
Org admins only. Pending, accepted, and revoked TrackLin.es invitations are returned newest first; expired pending invitations are reported as expired.
200Organization invitations. Invitation[]401Missing, invalid, or revoked API key. Error403Requires a signed-in organization admin.
post/organizations/invitationsInvite an email address to an organization, team, or project
Org admins only. Clerk sends an organization invitation with the org:member role. For team or project scope, the recipient must accept through Clerk and then call the accept operation below with a signed-in session using the invited, verified email address. Team invites create ordinary team membership; project invites create project membership. Organization members retain read access to all organization projects.
201Invitation created. accept_url is the Clerk-hosted invitation link. Invitation400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error402The plan's member limit is reached (Free and Pro: 20, counting invitations not yet accepted); upgrade to Enterprise.403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error409The email address is already an organization member.502Clerk could not create or validate the invitation.
get/organizations/invitations/{id}Inspect an organization invitation
idpath, required string
200Invitation details. Invitation401Missing, invalid, or revoked API key. Error403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error
delete/organizations/invitations/{id}Revoke a pending invitation
idpath, required string
post/organizations/invitations/{id}/acceptComplete an accepted Clerk invitation in TrackLin.es
The invitee must first accept the Clerk invitation. This endpoint then verifies the Clerk invitation, active organization membership, and a verified matching email. Team and project invitations create the corresponding local membership; team invitations never assign team leader authority. API keys are refused.
idpath, required string
204Invitation accepted and local membership applied.401Missing, invalid, or revoked API key. Error403The caller is an API key or does not own the invited verified email address.404Resource not found or not owned by this API key. Error409Clerk invitation or organization membership is not accepted yet.410Invitation expired, revoked, or already accepted by another user.502Clerk could not verify the invitation or recipient.
get/organizations/meThe caller's roles in their active organization
200Whether the caller is an org admin, and the teams they lead. object403No active organization.502Clerk could not be reached to resolve the first admin.
get/organizations/membersList active organization members
Returns role-free member details for the caller's active organization. Results are paginated.
limitquery integeroffsetquery integer
200A page of active organization members. OrganizationMemberPage400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error403No active organization in the caller's session or API key.502The identity provider could not return organization membership.
post/webhooks/bitbucket/{link}Bitbucket repository webhook, one per linked repo(public)
TrackLin.es creates this webhook itself when an org admin links a Bitbucket repo (POST /projects/{id}/repos), with its own random secret (sealed, never shown) and this URL naming the link; unlinking removes it. Deliveries are verified with that link's secret (X-Hub-Signature) and only affect that link's org and repo: pullrequest:* events (X-Event-Key) link PRs to the tickets they mention; MERGED closes them, once.
linkpath, required string · The linked repo the webhook was made for.
204Accepted.400Bad signature.404Unknown or unlinked repo.
post/webhooks/clerkReceive a Clerk webhook event (delivered by Svix)(public)
Authenticates the raw payload with the Svix headers and CLERK_WEBHOOK_SECRET (timestamps more than 5 minutes off are rejected). organizationMembership.created completes the new member's pending TrackLin.es invitations to that org (team and project access included) for their verified emails (TL-518); organizationMembership.deleted removes the user's admin and team rows in that org and revokes their API keys for it; user.deleted does the same across every org; organization.deleted removes the org's admins and teams and revokes its API keys (projects, boards and tickets are kept). Other event types are acknowledged and ignored. Safe to replay.
svix-idheader, required stringsvix-timestampheader, required string · Unix seconds.svix-signatureheader, required string · Space-separated "v1,<base64>" signatures.
204Event applied or ignored.400Invalid body or signature.503CLERK_WEBHOOK_SECRET is not set
post/webhooks/githubGitHub App webhook (X-Hub-Signature-256 with GITHUB_WEBHOOK_SECRET)(public)
pull_request events link PRs to the tickets whose keys they (or their commits) mention (in repos linked to a project of an org connected through that installation); merging closes them, once. An uninstalled app disconnects the org. A 5xx makes GitHub redeliver.
204Accepted.400Bad signature.503Not configured.
Teams
Manage role-free teams, team membership, and project links inside the active organization.
put/boards/{boardId}/teamSet the team that runs a board
Org admins only, signed in with a Clerk session. The team's leaders' authority is scoped to the boards it runs. The team must belong to the organization and already be linked to the board's project; removing the team from the project unlinks it from that project's boards. null unlinks.
boardIdpath, required string
204Board updated.403Not a signed-in org admin.404Resource not found or not owned by this API key. Error422The team isn't linked to the board's project.
get/projects/{projectId}/teamsList teams linked to a project
projectIdpath, required string
200Project teams Team[]
put/projects/{projectId}/teams/{teamId}Link a same-organization team to a project
projectIdpath, required stringteamIdpath, required string
204Team linked.403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error
delete/projects/{projectId}/teams/{teamId}Unlink a team from a project
projectIdpath, required stringteamIdpath, required string
204Team unlinked.403Requires a signed-in organization admin.
get/teamsList teams in the active organization
post/teamsCreate a team
patch/teams/{teamId}Update a team
delete/teams/{teamId}Delete a team
teamIdpath, required string
204Team and its memberships deleted.403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error
put/teams/{teamId}/leaders/{userId}Make a team member a leader
Org admins only, signed in with a Clerk session. A team can have several leaders; leaders must be on the team, and removing them from the team ends their leadership.
teamIdpath, required stringuserIdpath, required string
204They now lead the team.403Not a signed-in org admin.404Resource not found or not owned by this API key. Error422The user is not on the team.
delete/teams/{teamId}/leaders/{userId}Stop a member leading the team (they stay on it)
teamIdpath, required stringuserIdpath, required string
204No longer a leader.403Not a signed-in org admin.404Resource not found or not owned by this API key. Error422The user is not on the team.
get/teams/{teamId}/membersList team members and which of them lead the team
teamIdpath, required string
200Team members TeamMember[]404Resource not found or not owned by this API key. Error
put/teams/{teamId}/members/{userId}Add an active organization member to a team
teamIdpath, required stringuserIdpath, required string
204Membership recorded.403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error422User is not an active member of the organization.502Could not verify membership with the identity provider.
delete/teams/{teamId}/members/{userId}Remove a member from a team
teamIdpath, required stringuserIdpath, required string
204Membership removed.403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error
Tickets
Create, inspect, move, and close tickets.
get/assigneesList organization members and AI keys available for assignment
Only members of the active organization and its active AI keys are listed. Service/API keys are excluded.
200Assignable identities. object[]401Missing, invalid, or revoked API key. Error502Could not load organization members from Clerk.
post/columns/{columnId}/ticketsCreate a ticket in a board column
The ticket joins the board's open sprint. On that sprint's last day it goes to the bottom of the project backlog instead: still 201, with column_id null, redirected_to_backlog: true, and any estimate dropped.
columnIdpath, required string
get/mentionsTickets that @mention the caller
One row per ticket, newest mention first. Mentions are recorded when a comment names someone, and stay when the notification is cleared.
limitquery integerhide_donequery boolean · true leaves out done and closed tickets.
200Mentioned tickets. object[]400limit is not 1–100.401Missing, invalid, or revoked API key. Error
get/mentions/pageTickets that @mention the caller, a page at a time
The same list as /mentions, with how many there are in all.
pagequery integerper_pagequery integerhide_donequery boolean · true leaves out done and closed tickets.
200One page of mentioned tickets. object400page or per_page is out of range.401Missing, invalid, or revoked API key. Error
get/projects/{projectId}/backlogList a project's backlog
projectIdpath, required stringtypequery string (bug | feature | task | epic | spike)
post/projects/{projectId}/backlogCreate a ticket directly in a project's backlog
Creates a ticket without requiring a board or column. The ticket is attributed to the API key's agent name and defaults to type task if type is omitted.
projectIdpath, required stringprojectIdpath, required string
get/projects/{projectId}/backlog/pageOne page of the backlog, filtered and ordered by the server
By default ready tickets come first, then blocked ones, ordered by backlog position. sort=newest or sort=oldest orders the full filtered backlog by creation date before pagination. The counts field is per type and respects label and priority filters, but not the type filter, for type tabs. Label counts cover the whole backlog. The unpaged list stays at /projects/{projectId}/backlog.
epicquery string · Only this epic's sub-tickets, at any depth.priorityquery string (low | medium | high | urgent)[] · Any of these priorities; counts follow it.projectIdpath, required stringtypequery string (bug | feature | task | epic | spike)sortquery string (manual | newest | oldest) · Order tickets globally before pagination. Defaults to the manual backlog order.labelquery string[] · Repeatable; a ticket matches if it has any of them (case-insensitive).pagequery integerper_pagequery integer
get/projects/{projectId}/epicsA project's epics, with progress and size
Epics are large pieces of work: they stay in the backlog (never on a board or in a sprint), take no estimate, and can't sit anywhere inside another epic. Work joins one by becoming its sub-ticket. Progress counts the epic's direct sub-tickets (duplicates left out); size sums their estimates as points. Open epics come first, by backlog position.
projectIdpath, required string
get/projects/{projectId}/open-ticketsTickets that aren't done, across boards and backlog, in pick order
Every ticket in the project that isn't in its board's Done column, plus the backlog, without descriptions. Ordered the way work is picked: tickets in their board's open sprint first, then priority (urgent first), then on a board before in the backlog, then oldest. The MCP tool list_open_tickets maps to this.
projectIdpath, required stringassigneequery string (unassigned | me) · unassigned, or me (the caller's agent name or user id). Omit for everyone.blockedquery string (exclude) · exclude leaves out tickets waiting on unfinished work.priorityquery string (low | medium | high | urgent)[] · Any of these priorities.
get/searchSearch the organization's tickets
Full-text over titles (weighted highest), descriptions and comments. Every word must match, as a prefix, so it works while typing; punctuation is ignored. A ticket key in q (TL-123, case-insensitive) returns just that ticket. Ranked by relevance, then most recently changed. Without q it lists by recent change, filtered. The MCP tool search_tickets maps to this.
epicquery string · Only this epic's sub-tickets, at any depth.qquery stringprojectquery stringboardquery stringtypequery string (bug | feature | task | epic | spike)[] · Any of.priorityquery string (low | medium | high | urgent)[] · Any of.labelquery string[] · Any of (case-insensitive).assigneequery string · me (the caller), unassigned, or a user id / agent name.statusquery string (open | done)matchquery string (any) · any: a ticket matching any word (of three letters or more) will do, most matches first, for spotting duplicates despite typos. Default: every word must match.limitquery integerpagequery integer · 1-based, with limit. "My work" is assignee=me&status=open across every board and project.
post/short-linksA short link for a TrackLin.es link
For "Copy link": shortens a link to this site (tickets, boards…) through 1tn. The same link always gives the same short link. Links to any other site are refused.
200The short link. object400Not a link to this site.401Missing, invalid, or revoked API key. Error502The shortener couldn't be reached or refused it.
get/ticket-keys/{key}A ticket by its key (e.g. TL-123, case-insensitive), same as GET /tickets/{ticketId}
MCP tools also accept keys wherever they take a ticket_id, parent_id or ticket_ids.
keypath, required string
200The ticket with where it lives. TicketDetail401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error
get/tickets/{ticketId}Get a ticket and its board context
Use board_id to look up the Done column before closing a ticket.
ticketIdpath, required string
200Ticket details. TicketDetail401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error
patch/tickets/{ticketId}Update a ticket
title is required; empty type and priority retain their current values.
ticketIdpath, required string
delete/tickets/{ticketId}Delete a ticket
put/tickets/{ticketId}/assigneeAssign or unassign a ticket
Set assignee to an active member of the caller's organization or an active AI key name; set it to null to unassign. Server/service keys cannot be assignees.
ticketIdpath, required string
post/tickets/{ticketId}/backlogMove a ticket into its project's backlog
Agents cannot move tickets awaiting human review into the backlog.
ticketIdpath, required string
200Moved, with its open sub-tickets on the board: {also_moved: [{id, key, title}]}. If one can't go, nothing moves and the error names it.204Ticket moved to backlog.401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error409An agent attempted to bypass a pending human review.
put/tickets/{ticketId}/blocked-byReplace the tickets blocking this ticket
Use an empty ticket_ids array to clear dependencies. Dependencies must be in the same project and cannot form a cycle. A done ticket can keep or drop its blockers but not gain new ones (409).
ticketIdpath, required string
get/tickets/{ticketId}/bugfixesThe Bugfixes panel for a ticket Bugfixes created
The linked crash report's live details, fetched server-side with the org's Bugfixes connection and cached for a minute. regressed is true when the report is open and was seen after the ticket was done. When the org hasn't connected Bugfixes (reason not_connected) or Bugfixes can't be reached (unavailable), available is false but the link is still returned.
ticketIdpath, required string
200The panel. object404The ticket didn't come from Bugfixes.
post/tickets/{ticketId}/claimAssign an unassigned ticket to the caller
AI keys and signed-in people may claim tickets. Server/service keys cannot claim work.
ticketIdpath, required string
post/tickets/{ticketId}/duplicateClose a ticket as a duplicate
Marks ticketId as a duplicate of another ticket in the same project, closes it, and links it from the original. Optionally copies labels and comments to the original. Duplicate tickets do not count toward sprint velocity or carry-over. MCP tool mark_duplicate accepts ticket keys or ids.
ticketIdpath, required string
put/tickets/{ticketId}/externalLink a ticket to where an integration made it (API keys only), e.g. a Bugfixes report
The same object can be sent as external when creating a backlog ticket with an API key. A null body clears it.
ticketIdpath, required string
204Linked.400Invalid reference (source must be bugfixes; project and id required; url http(s)).403Only integrations (API keys) set references.404Resource not found or not owned by this API key. Error
get/tickets/{ticketId}/groupOpen sub-tickets that would move with a ticket leaving its board
When a ticket leaves its board (to the backlog, or another board), its sub-tickets on that board that aren't done, at any depth, go with it. Finished ones stay as the board's history; ones on other boards belong to those teams. Empty for a backlog ticket.
ticketIdpath, required string
get/tickets/{ticketId}/historyA ticket's lifecycle history, oldest first
Each stage the ticket entered, or skipped on the way to a later one: the backlog, a board column (named as it was then), and resolved (done, wont_do or duplicate). Created straight on a board, its backlog and any earlier columns are skipped; moving forward skips the columns jumped over. Append-only and recorded with each change. Same access as reading the ticket; resolved tickets keep their history. History starts from when it was introduced (October 2026).
ticketIdpath, required string
put/tickets/{ticketId}/labelsReplace a ticket's labels
post/tickets/{ticketId}/moveMove a ticket to another column
To close a ticket, first get its board, then move it to the column with the greatest position (the board's Done column). position may be zero. Moving a ticket into a sprint (from the backlog, another board or outside the sprint) on the sprint's last day answers 409; moves inside the sprint, to Done, or out of it are allowed.
ticketIdpath, required string
200Either an agent (API key) moved it into Done on a board with agent review: it waits in the column before Done for a person's review (see /tickets/{ticketId}/review). Or it moved to another board and its open sub-tickets on the old board came too (also_moved); if one can't, nothing moves and the error names it. object204Ticket moved.400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error409The target board's sprint ends today, so its scope is locked.500Unexpected server error. Error
get/tickets/{ticketId}/orgWhich of my organizations a ticket is in
For a ticket link opened while another organization is active, so the app can offer to switch. Answers only a signed-in person who belongs to the ticket's organization; anyone else (and API keys) gets 404, as if it didn't exist.
ticketIdpath, required string
200The ticket's organization. object404Resource not found or not owned by this API key. Error
put/tickets/{ticketId}/planned-sprintPlan a backlog ticket into a planned sprint, sized on that board's scale
The ticket stays in the backlog until the planned sprint starts. Without an estimate it keeps one already on the same scale. planned_sprint_id null takes it out of the plan (and clears the estimate). Going over capacity is allowed; the sprint then needs approval before it can start.
ticketIdpath, required string
204Planned.400Estimate isn't on the board's scale.404Resource not found or not owned by this API key. Error409The ticket is on a board
get/tickets/{ticketId}/prsPull requests linked to the ticket, newest first
A PR in a repo linked to the ticket's project is linked when its branch, title, description or commit messages mention the ticket's key (TL-123; only the project's own prefix counts). Kept up to date by the provider webhooks. When a linked PR is merged, the ticket moves to its board's Done column (or is resolved in the backlog) with a "Closed by" comment.
ticketIdpath, required string
200Linked PRs. object[]
post/tickets/{ticketId}/releaseClear the caller's assignment from a ticket
AI keys and signed-in people may release ticket assignments. Server/service keys cannot.
ticketIdpath, required string
post/tickets/{ticketId}/resolveClose a ticket as done or won't do
Marks a backlog ticket resolved without putting it on a board. A resolved ticket counts as done everywhere (blockers, sub-ticket progress, the done flag), leaves backlog pages and open-ticket lists, keeps its links and stays readable by id. Resolving again is a no-op; moving it onto a board reopens it. If it was the last unfinished sub-ticket of its parent, the parent is completed too (resolved if it's in the backlog, moved to its board's Done column if it's on a board), and so on up the chain. A ticket on a board is moved to the board's Done column with the resolution (moving it out again reopens it), for people and service keys (e.g. Bugfixes when a report is resolved or ignored). AI keys finish board tickets by moving them instead (409), so a board's agent review still applies. MCP tool resolve_ticket. With resolution wont_do it's closed as decided against (the ticket's resolution says so; the reason, if given, is added as a comment). No body resolves it as done. Agents cannot resolve tickets that have entered human review.
ticketIdpath, required string
post/tickets/{ticketId}/reviewApprove an agent's finished ticket (to Done) or send it back (to the first column)
People only. The comment, if given, is added to the ticket by the reviewer.
ticketIdpath, required string
Boards
Team boards and columns.
get/board-templatesBoard templates (built-ins, then the organization's own)
200Templates BoardTemplate[]401Missing, invalid, or revoked API key. Error
post/board-templatesSave an organization board template
Org admins only, signed in with a Clerk session. Send a full template, or {name, from_board_id} to copy an existing board's columns, WIP limits, style and estimate scale.
201Template saved. BoardTemplate400Invalid request body or field values. Error403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error
delete/board-templates/{templateId}Delete an organization template (built-ins can't be deleted)
templateIdpath, required string
204Deleted.400Built-in templates can't be deleted.403Requires a signed-in organization admin.404Resource not found or not owned by this API key. Error
get/boards/{boardId}Get board columns and tickets
Use the column with the greatest position as the Done column when closing a ticket.
boardIdpath, required string
patch/boards/{boardId}Rename a board or change its settings
Provide any of name, estimate_scale and style. Changing estimate_scale clears the estimates of tickets on the board, except those in closed sprints (kept for velocity history). Switching to kanban ends the open sprint, if any: it closes with no successor, keeps its done tickets, and its unfinished tickets stay on the board outside any sprint. Kanban boards can't start sprints and show only Done tickets finished in the last 14 days.
boardIdpath, required string
delete/boards/{boardId}Delete a board
post/boards/{boardId}/archiveArchive a used board (board managers)
Takes it off the project's boards; its history stays. Refused (409) while it has unfinished tickets. Its open sprint closes with no next sprint (finished tickets stay with it for velocity); its planned sprints are dropped and their tickets go back to the backlog unplanned. No tickets can be added to or moved onto an archived board (409).
boardIdpath, required string
204Archived.403Not an org admin or this board's team leader.404Resource not found or not owned by this API key. Error409It still has unfinished tickets.
get/boards/{boardId}/flowCycle time and weekly throughput for finished tickets
Returns tickets completed during the requested rolling window of UTC calendar weeks, including empty throughput weeks. Cycle-time percentiles include only tickets with a recorded started_at; older tickets without an exact start time remain in throughput but have a null cycle_days.
boardIdpath, required stringweeksquery integer · Number of UTC calendar weeks including the current week.
get/boards/{boardId}/liveStream standup and refinement changes for a board
Server-sent events fan out across backend instances through Valkey. Connects with the latest retained event, then streams updates; sends a heartbeat comment every 20 seconds. Events are named standup, standup-ended, or refinement.
boardIdpath, required string
get/boards/{boardId}/live/standupGet the current board standup session
boardIdpath, required string
200The active standup session LiveStandup401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error503Valkey is unavailable.
post/boards/{boardId}/live/standupStart or replace the board standup session
boardIdpath, required string
201Started session LiveStandup400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error503Valkey is unavailable.
post/boards/{boardId}/live/standup/actionAdvance, jump, pause, resume, or end the current standup
boardIdpath, required string
200Updated session LiveStandup204Session ended.400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error503Valkey is unavailable.
put/boards/{boardId}/live/standup/focusSay what the host has open and how the board is filtered (host only)
boardIdpath, required string
200The session. HostedSession403Only the host can set it.404No standup running.
put/boards/{boardId}/live/standup/hostHand over hosting (e.g. to the next speaker), or take over from a host who has gone
boardIdpath, required string
200The session with its new host. HostedSession400That person isn't in the standup.403Only the host can hand over (or take over from a host who has gone).404No standup running.
post/boards/{boardId}/live/standup/joinJoin the standup, and stay in it (call every 30 seconds)
boardIdpath, required string
200The session HostedSession404No standup running.
post/boards/{boardId}/live/standup/leaveLeave the standup
boardIdpath, required string
204Left.
get/boards/{boardId}/live/standup/sessionWho's in the standup and what the host has open
Starting a standup makes the starter its host; ending it ends the session. Events (standup-session, standup-session-ended) go out on the board's live stream.
boardIdpath, required string
200The session. HostedSession404No standup running503Valkey is unavailable.
post/boards/{boardId}/restorePut an archived board back on its project (board managers)
boardIdpath, required string
204Restored.404Resource not found or not owned by this API key. Error
get/projects/{projectId}/archived-boardsA project's archived boards, most recently archived first
projectIdpath, required string
200Boards. Board[]
post/projects/{projectId}/boardsCreate a team board, optionally from a template
projectIdpath, required string
Columns
Board workflow columns.
post/boards/{boardId}/columnsAdd a column to a board
The new column goes just before the last column (Done), which stays last. A ticket is done when it's in the board's last column.
boardIdpath, required string
put/boards/{boardId}/columnsSet a board's whole column layout at once
The edit board page's save. Columns in order, each an existing column (id) or a new one (no id), with its name and WIP limit. The first (To do) and last (Done) stay first and last, and take no WIP limit. Columns left out are deleted, refused (409) while they hold tickets in the current sprint. All or nothing. Board managers only.
boardIdpath, required string
put/boards/{boardId}/columns/orderReplace a board's column order
Every column exactly once, with the last column (Done) still last; moving it is a 400, since a ticket is done when it's in the last column.
boardIdpath, required string
patch/columns/{columnId}Rename a column and/or set its work-in-progress limit
columnIdpath, required string
delete/columns/{columnId}Delete an empty column
Attachments
UploadThing attachment metadata.
get/attachments/retentionThe org's file clean-up
Files due to be deleted (warned, soonest first) and files the clean-up has deleted (newest first, the latest 500). Org admins only; API keys can't.
200Due and deleted files. object401Missing, invalid, or revoked API key. Error403Not an org admin
delete/attachments/{attachmentId}Delete an attachment and its stored file
post/attachments/{attachmentId}/keepKeep a year-old file for another year
Files are warned about a year after they're added (or last kept) and deleted at 13 months unless kept. Org admins and the file's uploader can keep it; API keys can't.
attachmentIdpath, required string
get/tickets/{ticketId}/attachmentsList ticket attachments
ticketIdpath, required string
200Attachment records. Attachment[]401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error
post/tickets/{ticketId}/attachmentsRecord an uploaded UploadThing file on a ticket
The file must already be uploaded; this endpoint stores its metadata and does not upload bytes.
ticketIdpath, required string
201Attachment recorded. Attachment400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error409Ticket is in its Done column.
Sprints
Board sprints.
get/boards/{boardId}/planned-sprintsA board's planned sprints with their tickets and capacity
boardIdpath, required string
200Planned sprints, next first (at most 2). PlannedSprint[]401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error
post/boards/{boardId}/planned-sprintsPlan a sprint after the current one (org admins and the board team's leaders)
boardIdpath, required string
201The board's planned sprints. PlannedSprint[]400Invalid request body or field values. Error403Not an org admin or the board team's leader.409Already 2 planned sprints
get/boards/{boardId}/reports/sprintsHow a board's sprints went over a period (TL-397): sprints that closed in it plus the open one, totals, the period before for trends, and a one-line summary. Never per person.
boardIdpath, required stringfromquery string · First day (in the caller's Tracklines-Timezone); default 90 days ago.toquery string · Last dayformatquery string (json | md | markdown) · md (or markdown): the report as a Markdown download (text/markdown, attachment) with Mermaid charts, for wikis and docs (TL-399).
200The report. Sprints from before reports were recorded have estimated (planned = what they held at close) and no added/removed. object400Bad dates.404Not your organization's board.
get/boards/{boardId}/sprintsList a board's sprints
post/boards/{boardId}/sprintsStart a sprint and pull in unassigned board tickets
boardIdpath, required string
201Sprint started. Sprint400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error403Requires a signed-in organization admin or the linked team's leader.404Resource not found or not owned by this API key. Error409Board already has an open sprint, or is a kanban board.
get/boards/{boardId}/velocityVelocity per closed sprint, plus the open sprint's burn data
A closed sprint's completed value is its tickets that were done when it closed (closing carries the rest into the next sprint). Unit is points on a board with an estimate scale (t-shirt sizes count XS=1, S=2, M=3, L=5, XL=8; unestimated tickets count 0), otherwise tickets.
boardIdpath, required string
get/org/reports/sprintsEvery project in the organization together (TL-398), by project. Organization admins only (people, not API keys).
fromquery string · First day (in the caller's Tracklines-Timezone); default 90 days ago.toquery string · Last dayformatquery string (json | md | markdown) · md (or markdown): the report as a Markdown download (text/markdown, attachment) with Mermaid charts, for wikis and docs (TL-399).
200The roll-up. ReportRollup403Not an organization admin.
patch/planned-sprints/{plannedSprintId}Change a planned sprint's length and/or goal (org admins and the board team's leaders)
plannedSprintIdpath, required string
200The board's planned sprints. PlannedSprint[]400Invalid request body or field values. Error403Not an org admin or the board team's leader.404Resource not found or not owned by this API key. Error
delete/planned-sprints/{plannedSprintId}Remove a planned sprint; its tickets stay in the backlog, unplanned and unestimated
plannedSprintIdpath, required string
204Removed; a later planned sprint moves up.403Not an org admin or the board team's leader.404Resource not found or not owned by this API key. Error
post/planned-sprints/{plannedSprintId}/approveAccept a planned sprint's current total over capacity (org admins and the board team's leaders, signed in)
Records the total, who and when. If the planned total later goes above it, the sprint needs approval again.
plannedSprintIdpath, required string
200The planned sprint. PlannedSprint403Not an org admin or the board team's leader404Resource not found or not owned by this API key. Error
get/projects/{projectId}/reports/sprintsA project's sprint boards together (TL-398): each board's report, points and tickets totalled apart, predictability across both, and calendar months. Anyone in the organization.
projectIdpath, required stringfromquery string · First day (in the caller's Tracklines-Timezone); default 90 days ago.toquery string · Last dayformatquery string (json | md | markdown) · md (or markdown): the report as a Markdown download (text/markdown, attachment) with Mermaid charts, for wikis and docs (TL-399).
200The roll-up. ReportRollup400Bad dates.404Not your organization's project.
get/sprints/{sprintId}One sprint, read-only, with its burn data and tickets
For a closed sprint, tickets are the ones it finished (closing moves the rest to the next sprint) and burn.total is what it held when it closed; sprints closed before scope was recorded fall back to what they finished and carried_over is null. burn.ends_at is the close time. For an open sprint, everything reflects now.
sprintIdpath, required string
patch/sprints/{sprintId}Change the length of an open or scheduled sprint
sprintIdpath, required string
post/sprints/{sprintId}/closeClose a sprint; optionally start or schedule the next one
With no body, the sprint just closes: unfinished tickets stay on the board, out of any sprint, until the next POST /boards/{id}/sprints, which takes them in along with a waiting planned sprint (and its goal). Give next_length_days and/or next_starts_at to open the next sprint at once instead, as auto-close does when a sprint runs out: unfinished tickets carry over, and a planned sprint (refinement) within capacity or approved becomes the next sprint (its length unless next_length_days is given, and its tickets, which join the first column). A planned sprint over capacity without approval isn't promoted and stays planned.
sprintIdpath, required string
200The next sprint, started or scheduled (when asked for). Sprint204Closed; no next sprint started.400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error403Requires a signed-in organization admin or the linked team's leader.404Resource not found or not owned by this API key. Error
Live
Hosted standups and refinements that people join and follow.
get/live/sessionsThe organization's running standups and refinements
Every hosted standup (on a board) and refinement (on a project) running in the caller's organization, in one request. Archived boards are left out. Empty when live sessions are off.
200Running sessions, by project. object[]401Missing, invalid, or revoked API key. Error
Roadmaps
Project roadmaps and linked ticket progress.
post/projects/{projectId}/roadmapsCreate a roadmap in a project
Org admins and the project's team leaders. Visibility defaults to team; public needs a signed-in org admin (API keys get 403).
projectIdpath, required string
get/roadmapsList the caller's roadmaps
get/roadmaps/{roadmapId}Get a roadmap
Public roadmaps are readable without a token; login_only and team roadmaps require a signed-in member of the owning organization. Non-owner readers see progress counts but not linked-ticket IDs or titles.
roadmapIdpath, required string
put/roadmaps/{roadmapId}Update a roadmap
Org admins and the project's team leaders. Leaving out visibility keeps it. Changing visibility to or from public needs a signed-in org admin (API keys get 403).
roadmapIdpath, required string
delete/roadmaps/{roadmapId}Delete a roadmap
put/roadmaps/{roadmapId}/itemsReplace a roadmap's item list
Existing item IDs are updated in place; IDs omitted from the submitted list are removed. Unknown or omitted IDs create items.
roadmapIdpath, required string
put/roadmaps/{roadmapId}/items/{itemId}/ticketsReplace tickets linked to a roadmap item
Linking an epic counts its sub-tickets towards the item's progress (not the epic itself), worked out on every read, so ones added to the epic later count too.
roadmapIdpath, required stringitemIdpath, required string
get/roadmaps/{roadmapId}/updatesList dated progress updates
Uses the roadmap's public, login-only, or team visibility rules. Responses contain only update status, note, and date; linked ticket and organization details are never included.
roadmapIdpath, required string
200Updates oldest first. RoadmapUpdate[]401Missing, invalid, or revoked API key. Error403Roadmap is restricted to its owner.404Resource not found or not owned by this API key. Error
post/roadmaps/{roadmapId}/updatesPublish a dated progress update
Appends an immutable update to a roadmap owned by the caller's active organization.
roadmapIdpath, required string
201Update published. RoadmapUpdate400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error
API keys
Manage AI agent and server/service API keys with a Clerk session.
get/keysList the caller's active API keys, and the organization's service keys
post/keysCreate a TrackLin.es API key
The plaintext key is returned only once. This route requires a Clerk session. Only organization admins can create service keys.
201Key created; save the returned `key` securely now. CreatedApiKey400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error403Only organization admins can create service keys.500Unexpected server error. Error
Billing
Stripe subscription and webhook operations.
get/plansThe plans, their project limits and prices (public)(public)
200Free, Pro and Enterprise. projects null means unlimited; price is in the currency's smallest unit, from Stripe, and missing when it can't be read. object[]
get/subscriptionGet the active organization's plan and project limit
The plan belongs to the organization; everyone in it sees it, and its admins buy and manage it.
200Subscription status. object401Missing, invalid, or revoked API key. Error503Billing status unavailable.
post/subscription/checkoutCreate a Stripe checkout session for the active organization's plan
For an organization that isn't paying yet. One already on a paid plan changes plan instead (POST /subscription/plan).
200Stripe Checkout URL. URLResponse401Missing, invalid, or revoked API key. Error403Not a signed-in admin of the active organization.409The organization is already subscribed.502Stripe is unavailable.503Billing is not configured or unavailable.
post/subscription/planChange the organization's paid plan (Pro ↔ Enterprise), prorated
Moves the existing Stripe subscription to the other plan's price; Stripe prorates the difference. Org admins only.
204Changed; the new project limit applies straight away.400Unknown plan.403Not a signed-in admin of the active organization.409No subscription to change (check out instead)502Stripe is unavailable.503Billing (or that plan) isn't configured.
post/subscription/portalCreate a Stripe billing portal session for the active organization
200Stripe Billing Portal URL. URLResponse401Missing, invalid, or revoked API key. Error403Not a signed-in admin of the active organization.409The organization has no Stripe subscription (free502Stripe is unavailable.503Billing is not configured or unavailable.
post/webhooks/stripeReceive a Stripe webhook event(public)
Authenticates the raw payload with Stripe-Signature. The handler is mounted at this path both under /api and at the API root.
Stripe-Signatureheader, required string
200Event acknowledged.400Invalid body or signature.502Stripe is unavailable while processing the event.503Billing is unavailable.
System
Health check.
get/healthCheck API and backing services health(public)
200API, PostgreSQL, and Valkey are healthy. string503Database or Valkey is unavailable.
get/openapi.jsonThis API description, as JSON(public)
Public; no key needed. The same document as /openapi.yaml.
200The OpenAPI document. object
get/openapi.yamlThis API description, as YAML(public)
Public; no key needed. Built into the API, so it always matches what's running.
200The OpenAPI document. string
Intake
post/intake-submissions/{submissionId}/dismissClose a pending request without making a ticket
submissionIdpath, required string
204Dismissed.404Resource not found or not owned by this API key. Error409Already triaged.
post/intake-submissions/{submissionId}/promoteTurn a pending request into an unassigned backlog ticket
submissionIdpath, required string
200Promoted IntakeSubmission404Resource not found or not owned by this API key. Error409Already triaged.
get/intake/{token}Whether a request form link is open (public, no account)(public)
Says nothing else about the project.
tokenpath, required string · The project's unlisted form link token.
200Open. object404Closed or unknown link.
post/intake/{token}Send a bug report or feature request through a project's form (public, no account)(public)
Held for the team to triage; never put on a board. Limits: 5 per sender per hour, 200 per project per day. A filled-in website field (hidden from people) marks a bot: accepted and dropped.
tokenpath, required string · The project's unlisted form link token.
202Received.400Invalid request.404Closed or unknown link.429Too many requests from this sender.
get/projects/{projectId}/intakeThe project's request form, open (with its link token) or closed
projectIdpath, required string
200Settings. IntakeSettings404Resource not found or not owned by this API key. Error
put/projects/{projectId}/intakeOpen or close the request form; rotate replaces the link (the old one stops working)
Org admins only, in a signed-in session. API keys get 403, even an admin's.
projectIdpath, required string
200Settings. IntakeSettings403Not a signed-in org admin.404Resource not found or not owned by this API key. Error
get/projects/{projectId}/intake/submissionsRequests sent through the form, newest first (pending by default)
projectIdpath, required stringstatusquery string (pending | promoted | dismissed)
200Submissions. IntakeSubmission[]404Resource not found or not owned by this API key. Error
MCP
post/mcpModel Context Protocol endpoint for bots
MCP over Streamable HTTP (stateless; responses are application/json). Send JSON-RPC MCP messages (initialize, tools/list, tools/call). Authenticate like the REST API (tl_ API key or Clerk session). Each tool calls the matching REST route with the caller's credentials, so results, permissions and errors are the same as REST; a failed call is a tool result with isError and the REST status in its text. GET and DELETE return 405 (no sessions or server-initiated streams).
200JSON-RPC response. object202Notification accepted.401Missing, invalid, or revoked API key. Error
Notifications
get/me/google-chatWhether your Google Chat is linked for personal messages
The TrackLin.es Google Chat app sends your own notifications (assigned, mentioned, review…) to your chat with it, once you've linked your account from the link it sends you.
200available is false when the server has no Chat app. object401Missing, invalid, or revoked API key. Error
delete/me/google-chatStop personal messages in Google Chat
204Unlinked.401Missing, invalid, or revoked API key. Error
post/me/google-chat/linkLink your Google Chat with the one-time code the Chat app sent you
Signed-in sessions only (not API keys). Linking again replaces the earlier link.
200Linked; the app confirms in Google Chat.400The code expired (30 minutes) or was already used.403Not a signed-in session.503The Chat app isn't set up on this server.
get/notificationsThe signed-in user's inbox in the active organization, unread first, then newest
Events: assigned (someone assigned you a ticket), mention (@you in a comment), finished (a ticket you raised was finished or resolved), wont_do (a ticket you raised was closed as won't do, with the reason), review (an agent finished a ticket on a board you manage), sent_back (your ticket was sent back from review), approval (a planned sprint on a board you manage went over capacity). Nobody is told about their own actions; only people (not agents) get notifications. MCP tool list_notifications.
pagequery integerper_pagequery integer
200Notifications. Notification[]403Not signed in to an organization.
delete/notifications/readRemove all your notifications that are already read
204Cleared.
post/notifications/read-allMark all your notifications read
204Read.
get/notifications/unread-countHow many notifications are unread
200The count. object
delete/notifications/{notificationId}Remove one of your notifications
notificationIdpath, required string
204Removed.404Resource not found or not owned by this API key. Error
post/notifications/{notificationId}/readMark one of your notifications read
notificationIdpath, required string
204Read.404Resource not found or not owned by this API key. Error
get/pushWhether this server sends Web Push, and the VAPID public key browsers subscribe with
200Push settings. object
post/push/subscriptionsTurn on notifications for this device (signed-in people only)
The browser's PushSubscription. The endpoint must be https on a known push service (Google, Mozilla, Apple, Microsoft): the server sends to it. The same browser subscribing again takes the row over. The device then gets every notification from the inbox, even with TrackLin.es closed.
204Subscribed.400Not a browser push subscription.403API keys can't subscribe devices.503Push isn't set up on this server.
delete/push/subscriptionsTurn off notifications for this device
204Unsubscribed (your own devices only).
post/webhooks/google-chatRequests from the TrackLin.es Google Chat app (Google only)(public)
Signed by Google for this endpoint (Authorization Bearer ID token); anything else is refused.
200The app's reply.401Not a request from Google Chat for this app.503The Chat app isn't set up on this server.
Schemas
ApiKey
clientstring | null (claude-code | claude-desktop | codex | cursor | vscode | gemini | other | ) · AI keys only. The client the agent runs in.created_atstringdisplaystring · What it is called: "Claude", an other agent's label (or "AI agent"), or a service key's name. Others see AI keys with their owner: "Claude (keloran)".expires_atstring · After this the key is refused (401 "api key expired").idstringkindstring (ai | service | personal) · AI keys can be assigned tickets; service keys are for servers and integrations; personal keys act exactly as their owner (their own scripts and displays).labelstring | null · AI keys with client "other": what the agent is called.last_used_atstring | nullnamestring · The id its work is recorded under. Generated for AI keys (e.g. claude-482193); chosen for service keys. Only a label for personal keys, whose work is recorded as their owner.prefixstringread_onlyboolean · Only GET, HEAD and OPTIONS; anything else is 403 "this api key is read-only".
Attachment
content_typestringcreated_atstringcreated_bystringdelete_onstring · Set once the file is a year old: the day it is deleted unless kept.idstringkeystringnamestringsizeintegerticket_idstringurlstring
AttachmentInput
content_typestringkeyrequired stringnamerequired stringsizeintegerurlrequired string · Must be an https UploadThing URL on utfs.io or a *.ufs.sh host.
Board
agent_reviewboolean · Agents' moves into Done wait for a person's review.archived_atstring | null · Archived boards are off the project page and take no new work.columnsrequired Column[]created_atstringdeletableboolean · Never used (no tickets, no sprints): DELETE works; otherwise archive it.descriptionstringestimate_scaleEstimateScalehidden_doneinteger · Kanban only. Done tickets finished more than 14 days ago, left off the board.idrequired stringnamerequired stringowner_clerk_idstringproject_idrequired stringsprintSprintstylestring (sprints | kanban)team_idstring | null · The team whose leaders run this board.updated_atstring
BoardFlow
median_cycle_daysrequired number | null · Median elapsed days for tickets with a known start.p85_cycle_daysrequired number | null · 85th percentile elapsed days for tickets with a known start.throughputrequired object[] · Counts by UTC Monday week start, including weeks with no completed tickets.ticketsrequired object[] · Finished tickets in the date window, oldest completion first.weeksrequired integer · Number of UTC calendar weeks included
BoardInput
descriptionstringnamerequired stringtemplate_idstring · A template from GET /board-templates (built-in ids start with builtin:). Default builtin:simple (To do, In progress, Done). Unknown id: 400.
BoardSummary
created_atstringdescriptionstringestimate_scaleEstimateScaleidstringnamestringowner_clerk_idstringproject_idstringstatsobject · Only on a project's boards. Counts the tickets the board shows (the open sprint's, or all if it has never run one). in_progress = neither the first column nor done; urgent = open urgent tickets.team_idstring | nullupdated_atstring
BoardTemplate
builtinrequired booleancolumnsrequired object[] · In board order; the last column is Done.estimate_scalerequired EstimateScaleidrequired string · builtin:simple, builtin:scrum, builtin:kanban, or an org template's UUIDnamerequired stringstylerequired string (sprints | kanban)
BugfixTicket
board_idrequired stringbodyrequired stringcolumn_idrequired stringcreated_atrequired stringcreated_byrequired stringidrequired stringpriorityrequired stringtitlerequired string
Column
idstringnamestringpositionintegerticketsTicket[]wip_limitinteger | null
Comment
authorrequired string · BugFixes API key name.bodyrequired stringcreated_atrequired stringidrequired stringparent_comment_idstring | nullticket_idrequired string
CreateApiKeyInput
clientstring (claude-code | claude-desktop | codex | cursor | vscode | gemini | other) · Required for AI keys. Their name is generated from it.expires_in_daysinteger · How long the key works.kindrequired string (ai | service | personal)labelstring · AI keys with client "other": what the agent is called (optional).namestring · Required for service and personal keys; ignored for AI keys.read_onlyboolean · The key can only read.
CreateBacklogTicket
descriptionstringlabelsLabelsprioritystring (low | medium | high | urgent)titlerequired stringtypestring (bug | feature | task | epic | spike)
CreateBugfixTicket
board_idrequired stringbodystring · Initial ticket description.column_idrequired stringlabelsLabelsprioritystring (low | medium | high | urgent) · Defaults to medium.titlerequired string
CreateComment
bodyrequired stringparent_comment_idstring | null · Omit or set null for a top-level appended update; set to a comment ID to reply in a thread.
CreatedApiKey
- ApiKey & object
EpicProgress
An epic's progress and size, from its direct sub-tickets (duplicates left out).
doneintegerpointsnumber · Sum of their estimates as points (T-shirt sizes converted).totalintegerunestimatedinteger
Error
Plain-text error message.
- string
EstimateScale
How a board estimates tickets. fibonacci 1,2,3,5,8,13,21; tshirt XS,S,M,L,XL; powers 1,2,4,8,16; linear 1-5; none turns estimates off.
- string (none | fibonacci | tshirt | powers | linear)
ExternalRef
idrequired string · e.g. the Bugfixes report id.projectrequired string · The source's project id.sourcerequired string (bugfixes)urlstring
HostedFocus
What the host has open, shown to followers.
filtersobject · e.g. labelsticketIdstring · The ticket open; null when none.
HostedSession
focusHostedFocushoststring · The host's user id.peoplestring[] · User ids in the session now (seen in the last minute)startedAtinteger · Unix milliseconds.
IntakeSettings
can_manageboolean · The caller may open, close or reset it: a signed-in org admin.openbooleantokenstring | null · The form is at /request/{token}.
IntakeSubmission
contactstringcreated_atstringdecided_atstring | nulldecided_bystring | nulldetailsstringidstringproject_idstringstatusstring (pending | promoted | dismissed)ticket_idstring | nullticket_keystring | nulltitlestringtypestring (bug | feature)
Invitation
accept_urlstring · Clerk-hosted invitation URL, returned by create and list/get.accepted_atstringaccepted_bystringcreated_atrequired stringemail_addressrequired stringexpires_atrequired stringidrequired string · TrackLin.es invitation id used by inspect, revoke, and accept routes.invited_byrequired stringorg_idrequired stringscoperequired string (organization | team | project)statusrequired string (pending | accepted | revoked | expired)target_idstring
InvitationInput
email_addressrequired stringscoperequired string (organization | team | project)target_idstring · Required for team and project scopes; omit for organization scope.
Labels
Free-form labels. Trimmed; blanks and case-insensitive duplicates dropped (first spelling wins). Omit to leave unchanged; [] clears.
- string[]
LiveStandup
indexrequired integer · Current participant; people.length is the clean-board review.peoplerequired object[]runningSincerequired integer | null · Unix milliseconds when the clock resumed; null while paused.spentMsrequired integer · Elapsed time before the current run.startedByrequired string
MoveTicket
column_idrequired stringestimatestring · Size the ticket on the way in, on the target board's estimate_scale. Required when the ticket enters a board with an estimate scale (from the backlog or another board) and has no estimate on that same scale; 400 otherwise.positioninteger
NameInput
namerequired string
Notification
actorstring · User id or agent name.board_idstring | nullcreated_atstringidstringkindstring (assigned | mention | finished | wont_do | review | sent_back | approval)read_atstring | nulltextstring · Reads after the actor, e.g. "assigned you TL-12: Fix login".ticket_idstring | nullticket_keystring | null
OrgAdmin
created_atstringgranted_bystring · clerk:creator, clerk:admin-role, clerk:earliest-member, or the user id of the admin who granted ituser_idstring
OrganizationMember
first_namestringidentifierrequired string · Public identifier provided by Clerk, usually an email address.image_urlstringlast_namestringuser_idrequired string
OrganizationMemberPage
limitrequired integermembersrequired OrganizationMember[]offsetrequired integertotal_countrequired integer
PlannedSprint
board_idstringcapacityobjectgoalstring · Carried into the sprint when it starts.idstringlength_daysintegernumberinteger · The sprint number it will getpositioninteger · 1 = the next sprintticketsTicket[]
Progress
doneintegertotalinteger
Project
boardsBoardSummary[]created_atrequired stringdescriptionrequired stringidrequired stringkey_prefixrequired string · Ticket keys are <key_prefix>-<number>. Worked out from the name on create (its capitals, e.g. TrackLin.es → TL), unique in the org.namerequired stringroadmapsRoadmap[]statsobject · Only on the project list. Open and done count board tickets (done = in the board's last column); urgent counts open urgent tickets on boards or in the backlog.updated_atrequired string
ProjectInput
descriptionstringkey_prefixstring · Update only; lower case is upper-cased. Omitted = unchanged.namerequired string
ProjectRepo
created_atstringdefault_branchstringfull_namestringproviderstring (github | bitbucket)
ReportMix
countintegertypestring (bug | feature | task | epic | spike)valuenumber
ReportOverall
goals_metintegergoals_setintegerpredictabilitynumber | nullsprintsinteger
ReportRollup
boardsobject[] · A project's sprint boards (not kanban or archived): each a board report.fromstringidstring · The project; absent for the organization.monthsobject[]namestringoverallReportOverallpointsReportTotals | null · Boards that estimate.previousReportOverallprojectsReportRollup[] · The organization's projects, each a roll-up without its boards.summarystringticketsReportTotals | null · Boards that count tickets.tostring
ReportTotals
addednumber | nullcarriednumber | nulldonenumbermixReportMix[]plannednumberpredictabilitynumber | nullsprintsinteger · Closed sprints counted.velocitynumber | null
Roadmap
can_manageboolean · The caller can change this roadmap (an org admin, or a leader of a team with a board in its project). Always false for readers outside its organization.created_atstringdescriptionstringidstringitemsRoadmapItem[]progressProgressproject_idstringtitlestringupdated_atstringvisibilitystring (public | login_only | team)
RoadmapInput
descriptionstringtitlerequired stringvisibilitystring (public | login_only | team)
RoadmapItem
- RoadmapItemInput & object
RoadmapItemInput
descriptionstringidstring · Omit to create a new item.manual_statusstring (not_started | in_progress | done)start_datestring | null · Gantt bar start date.target_datestring | nulltitlerequired string
RoadmapUpdate
authorstring · Who posted it, by name (an email-only name without its domain; an agent by its own name). Missing on updates from before TL-413.healthrequired string (on_track | at_risk | off_track)idrequired stringnoterequired stringpublished_atrequired string
RoadmapUpdateInput
healthrequired string (on_track | at_risk | off_track)noterequired string · Plain text or Markdown; rendered as safe text by clients.
Sprint
board_idstringclosed_atstring | nullends_atstringgoalstring · One line the sprint is for; empty = none.idstringlength_daysintegernumberintegerstarts_atstring
Team
created_atrequired stringdescriptionrequired stringidrequired stringnamerequired stringorg_idrequired stringupdated_atrequired string
TeamInput
descriptionstringnamerequired string
TeamMember
leaderbooleanuser_idstring
Ticket
Agent origin is recorded in created_by and rendered in the ticket UI; assignment is shown in assigned_to.
assigned_tostring | nullblockedrequired booleancolumn_idrequired string | null · Null while in the backlog.created_atstringcreated_byrequired stringdescriptionrequired stringdone_atstring | null · When it reached its board's Done column or was resolved; null while open.duplicate_of_idstring | null · The original ticket when this ticket is marked as a duplicate.duplicate_of_keystring | null · Key of the original ticket.duplicate_of_titlestring | null · Title of the original ticket.estimatestring | null · On the board's estimate_scale. Cleared when the ticket moves to the backlog or to a board with another scale.external_sourcestring | null · Where an integration made it (bugfixes); null for our own tickets.external_urlstring | null · Link to it there (e.g. the Bugfixes report).idrequired stringkeystring · The project's key prefix and the ticket's number in the project, assigned on create. For branch names, PR titles and search.labelsstring[] · Free-form labels, sorted case-insensitively.planned_sprint_idstring | null · A backlog ticket planned into a board's upcoming sprint (refinement).positionrequired integerpriorityrequired string (low | medium | high | urgent)project_idrequired stringresolutionstring | null (done | wont_do | duplicate | ) · How a resolved backlog ticket was closed.resolved_atstring | null · Set when the ticket was resolved in the backlog (POST /tickets/{id}/resolve); it then counts as done.review_requested_atstring | null · Set while an agent's finished ticket waits for a person's review (in the column before Done).sprint_idstring | nullstarted_atstring | null · First move out of the board's first column; null for historical tickets without a known start.titlerequired stringtyperequired string (bug | feature | task | epic | spike)updated_atstring
TicketChild
assigned_tostring | nullboard_idstring | null · Null when the sub-ticket is in the backlog.board_namestring | nulldonebooleanestimatestring | nullidstringkeystring | nulltitlestringtypestring (bug | feature | task | epic | spike)
TicketDependency
donebooleanidstringtitlestring
TicketDetail
Ticket properties plus board, column, sprint, and completion context.
assigned_tostring | nullattachment_countinteger · Files on the ticket (list them with GET /tickets/{ticketId}/attachments): designs, screenshots, logs.blockedbooleanblocked_byTicketDependency[]blocksTicketDependency[]board_idstring | nullboard_namestring | nullchildrenTicketChild[]column_idstring | nullcolumn_namestring | nullcreated_bystringdescriptionstringdonebooleanduplicate_of_idstring | nullduplicate_of_keystring | nullduplicate_of_titlestring | nullduplicatesobject[]epicEpicProgressepic_idstring | null · The epic this ticket is part of, at any depth (epics don't nest).estimatestring | nullestimate_scaleEstimateScaleidstringparent_idstring | nullpositionintegerprioritystring (low | medium | high | urgent)project_idstringresolutionstring | null (done | wont_do | duplicate | )sprint_idstring | nullsprint_numberinteger | nulltimebox_daysinteger | null · A spike's timebox, in days (with timebox_hours). Null when not a spike or not yet timed.timebox_hoursinteger | null · A spike's timebox hours (0–23).titlestringtypestring (bug | feature | task | epic | spike)
TicketInput
descriptionstringestimatestring · A value on the board's estimate_scale (400 otherwise). Required when creating on a board with an estimate scale; omitted = unchanged on update, and it can't be cleared there. Not allowed on backlog tickets.labelsLabelsprioritystring (low | medium | high | urgent)timebox_daysinteger · A spike's timebox days (TL-502). Send with timebox_hours; both 0 clears the timebox, both omitted = unchanged. Only meaningful on spikes (set via update).timebox_hoursinteger · A spike's timebox hours (0–23). Send with timebox_days.titlerequired stringtypestring (bug | feature | task | epic | spike)
URLResponse
urlrequired string
Velocity
capnumber · A sprint's capacity: velocity (last 3 closed sprints) + 10%. Null without closed sprints.currentrequired object | null · The open sprint, or null. total is the sprint's current scope (scope changes aren't tracked over time).sprintsrequired object[] · Closed sprints, oldest first.unitrequired string (points | tickets)
Comments
Append updates to a ticket conversation.
get
/tickets/{ticketId}/commentsList a ticket's conversationticketIdpath, required string200Comments in chronological order. Comment[]401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Errorpost
/tickets/{ticketId}/commentsAppend an update to a ticketAdds a top-level comment to the ticket conversation. Use this for progress reports or follow-up findings; it does not replace the ticket description.
ticketIdpath, required string201Comment appended. Comment400Invalid request body or field values. Error401Missing, invalid, or revoked API key. Error404Resource not found or not owned by this API key. Error500Unexpected server error. Error