List project tickets
Changed onList the project's Strategic Tickets, a page at a time — what the team is deciding on and working on, whether a person opened a ticket, your own automation did, or a Strategic Briefing did. By default the list is the board flattened: the columns in board order, and inside each column the order the team keeps — in triage that starts as the order an edition delivered its recommendations, most important first, newest edition on top, until somebody moves them. A ticket carries no order number of its own on this API; its place is where it sits in the list. Each row is a summary: every field except the Markdown description, which include=description adds. pagination.total counts every ticket matching every filter across all pages; pagination.hasMore says a next page exists — ask for page+1. byStatus counts the matches per column whatever status you passed, narrowed by every other filter, so limit=1 with no other filter is the board's census. One call answers each of these. What to start: status=triage,todo&sort=priority. Quick wins among a briefing's recommendations: add impactMin=3&maxMinutes=120 — that leaves out every ticket without an edition's estimate; for those, impactMin=3&effort=low. Overdue: status=triage,todo,in_progress&dueBefore={today}&sort=due. Due this week: status=triage,todo,in_progress&dueFrom={today}&dueBefore={the day after the week ends}. Changed since yesterday: activeSince={the start of the customer's yesterday, as ISO 8601 with its offset — a bare day is midnight UTC}&sort=activity; on a changed ticket, a statusChangedAt later than that means it changed column, and a lastActivityAt later than its updatedAt means a thread entry was written or edited after the ticket's own last change. Due dates are calendar days with no time zone, so pass the customer's own today. origin=briefing&briefingRunId={runId} is one edition's tickets, with {runId} taken from GET /v1/projects/{projectId}/strategic-briefing/history; they are returned whole whatever closed says, so the list matches the count the briefing states. To read neighbours for a move, use sort=board (the default) and the destination column alone: any other read can hide tickets that sit between two rows.
Request
- Base URL: https://api.competlab.com
- URL: https://api.competlab.com/v1/projects/{projectId}/tickets
- Auth: API key in header CL-API-Key
Path parameters
Project ID
Query parameters
Page number (1-indexed)
How many tickets per page, 1 to 100; 50 when omitted. Pass limit=1 when you only need pagination.total and byStatus.
Return only the tickets in these columns — one, or several separated by commas (status=todo,in_progress). Omit it for every column. byStatus ignores this filter, so its counts still cover every column, narrowed by every other filter you passed.
The order of the list. board (the default) is the board flattened: the columns in board order, each in the order the team keeps — and the only order to read neighbours for a move from. priority puts the highest impact first; among the same impact, the least effort first, and among the same effort the fewest minutes of the edition's estimate; then the earliest due date, then board order. A missing value sorts last at each step. due puts the earliest due date first, undated last. activity puts the most recently touched first (lastActivityAt). Any order but board mixes the columns; each ticket carries its status.
How much of done and dismissed to return. all (the default) returns them whole; recent returns only the tickets that reached those columns within the last 30 days. The other columns are always whole, whichever you pass, and a read narrowed to one edition is whole whatever you pass.
Who opened the ticket, never which dimension it is about (that is dimension): user — a person in the app; api — an API key, which is what everything you write here carries; briefing — a Strategic Briefing; ai_sources — reserved for a future AI Sources writer, which opens none today, so that value matches none.
Return only the tickets a Strategic Briefing opened for one part of its analysis, or none for the tickets no part claims — everything a person or an API key opened, and a briefing's ticket whose edition named none. agent-readiness is the key for Agent Adoption — how well a site is set up for AI agents to discover, access and read it. The key predates the name and does not change.
Return only the tickets one Strategic Briefing edition opened. Take the id from GET /v1/projects/{projectId}/strategic-briefing/history, or from meta.runId on a briefing read. Nothing a person or an API key opens carries an edition, so this narrows the list to that edition's tickets whether or not you also pass origin. An edition's tickets are returned whole, the finished and dismissed ones included however long ago they were closed — closed has no effect on this read — so the list matches the count the briefing states for that edition.
Return only the tickets one person owns — their user ID, from GET /v1/projects/{projectId}/tickets/assignees. Pass none for the tickets nobody owns. A ticket whose owner has left the organization reads as unassigned everywhere, but is still found here by their ID.
Return only the tickets carrying one label — its ID, from GET /v1/projects/{projectId}/tickets/labels. A project's list is empty until the project defines a label, in the app or through POST /v1/projects/{projectId}/tickets/labels.
Return the one ticket carrying this number on the board — what a person means by #14. It is a name and not an address: this narrows the list, and every endpoint that acts on a ticket still takes its id.
Return only the tickets whose title or description contains this text, compared without regard to case. It is matched literally — punctuation is text, not a pattern — and threads are not searched. A match in a description is weaker evidence than one in the title: a briefing's description quotes its grounding, so read the ticket before calling two tickets the same work.
Return only the tickets whose impact is at least this, 1 to 4 (1 Minor · 2 Moderate · 3 Significant · 4 Critical; 4 matters most). Tickets nobody sized are left out. A Strategic Briefing sets impact and effort on the tickets it opens, so a value there is the edition's estimate until a person changes it.
Return only the tickets sized as one of these — one, or several separated by commas (effort=low,medium). Tickets nobody sized are left out. This is the ticket's size word, not its minutes: a briefing's briefing.estimatedMinutes is the edition's own time estimate, neither is derived from the other, and a low ticket can be an afternoon — when a person asks for something quick, use maxMinutes.
Return only the tickets a Strategic Briefing opened whose edition estimated at most this many minutes of work. A ticket without an edition's estimate — everything a person or an API key opened, and a briefing's ticket the edition did not size — is left out; use effort for those.
Return only the tickets due on or after this calendar day, YYYY-MM-DD. Tickets with no due date are left out. A due date has no time zone, so pass the customer's own day.
Return only the tickets due strictly before this calendar day, YYYY-MM-DD. dueBefore=<today> with the open columns (status=triage,todo,in_progress) is everything overdue; with dueFrom it is a range. Tickets with no due date are left out. A due date has no time zone, so pass the customer's own day.
Return only the tickets something happened to at or after this moment — an edit, a move, or a new thread entry (lastActivityAt). ISO 8601 with its offset; a time without one is read as UTC, and YYYY-MM-DD is the start of that day in UTC. A deleted ticket is gone and does not appear.
What each ticket in the list carries. By default a list is an INDEX: every field except the ticket's own Markdown description, which is then not on the row at all. Pass include=description for the bodies — one ticket's description runs to thousands of characters, so a page fetched with them is large enough to be worth asking for on purpose. GET …/tickets/{ticketId} always returns the description whatever you pass here.
Response
Example response
{
"items": [
{
"id": "507f1f77bcf86cd799439011",
"number": 14,
"author": {
"userId": "507f1f77bcf86cd799439011",
"fullName": "Dana Ellis"
},
"briefing": {
"runId": "6a3950c06bda450a1372048e",
"estimatedMinutes": 90,
"extendsTicketId": "507f1f77bcf86cd799439012"
},
"title": "Get onto the 2026 buyer guide",
"labels": [
{
"id": "3f2a9c6e-1b77-4c1e-9a54-5c7b2f0a91d2",
"name": "Pricing page",
"color": "#3b82f6"
}
],
"statusChangedAt": "2026-09-15T10:00:00.000Z",
"assignee": {
"userId": "507f1f77bcf86cd799439011",
"fullName": "Dana Ellis"
},
"dueDate": "2026-10-01",
"impact": 3,
"commentCount": 2,
"deletable": true,
"createdAt": "2026-09-15T10:00:00.000Z",
"updatedAt": "2026-09-16T08:30:00.000Z",
"lastActivityAt": "2026-09-17T11:02:00.000Z",
"description": "The guide names three of our competitors and not us."
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 47,
"totalPages": 3,
"hasMore": true
},
"byStatus": {
"triage": 0,
"todo": 1,
"in_progress": 2,
"done": 3,
"dismissed": 4
}
}Changes
- info
removed the non-success response with the status
- info
- breaking
for the
queryrequest parameterclosed, default valueallwas added - breaking
the response property
//became optional for the status - breaking
removed the required property
from the response with the status - breaking
removed the required property
from the response with the status - info
added the new optional
queryrequest parameteractiveSince - info
added the new optional
queryrequest parameterdueBefore - info
added the new optional
queryrequest parameterdueFrom - info
added the new optional
queryrequest parametereffort - info
added the new optional
queryrequest parameterimpactMin - info
added the new optional
queryrequest parameterlimit - info
added the new optional
queryrequest parametermaxMinutes - info
added the new optional
queryrequest parameterpage - info
added the new optional
queryrequest parametersort - info
added the new enum value
noneto thequeryrequest parameterdimension - info
added the non-success response with the status
- info
added the required property
to the response with the status - info
added the required property
///////to the response with the status - info
added the required property
to the response with the status
- breaking