Lists

Create, read, update, and delete the lists that group records inside a workspace, using the RecordList queries and mutations.


Lists are the columns that organize records within a workspace — the lanes of a board, the sections of a table. In the API they are RecordList objects, and the records they hold are Record objects. Use the queries and mutations on this page to read lists, create and rename them, reorder them by position, mark every record in a list done or undone, and delete a list.

A workspace can hold at most 50 lists. Lists are ordered by their position (ascending) and are scoped to the workspace they belong to. Workspace and organization arguments accept either an ID or a slug.

Request

Read a single list

Use the recordList query to fetch one list by ID.

query GetRecordList {
  recordList(id: "list_123") {
    id
    uid
    title
    position
    isLocked
    todosCount
    project {
      id
      name
    }
  }
}

Read every list in a workspace

Use the recordLists query to fetch all lists in a workspace, ordered by position.

query GetWorkspaceLists {
  recordLists(projectId: "project_123") {
    id
    title
    position
    isLocked
    todosCount
  }
}

Search lists across an organization

recordListQueries.recordLists is the filtered, sorted, paginated reader. It returns a RecordListsPagination object (items + pageInfo). companyIds is required; everything else is optional.

query SearchRecordLists(
  $filter: TodoListsFilterInput!
  $sort: [TodoListsSort!]
  $skip: Int
  $take: Int
) {
  recordListQueries {
    recordLists(filter: $filter, sort: $sort, skip: $skip, take: $take) {
      items {
        id
        title
        position
        project {
          id
          name
        }
      }
      pageInfo {
        totalItems
        hasNextPage
        hasPreviousPage
      }
    }
  }
}
{
  "filter": {
    "companyIds": ["company_123"],
    "projectIds": ["project_123"],
    "search": "sprint"
  },
  "sort": ["position_ASC"],
  "skip": 0,
  "take": 20
}

Create a list

Use the createRecordList mutation. All three input fields are required — set position to control where the new list lands relative to existing ones.

mutation CreateRecordList {
  createRecordList(input: { projectId: "project_123", title: "Sprint 1", position: 1 }) {
    id
    title
    position
  }
}

Rename or reorder a list

Use the editRecordList mutation to change a list’s title, position, or locked state. Only todoListId is required; omitted fields are left unchanged.

mutation EditRecordList {
  editRecordList(
    input: { todoListId: "list_123", title: "Sprint 1 — In Progress", isLocked: true }
  ) {
    id
    title
    position
    isLocked
  }
}

Delete a list

Use the deleteRecordList mutation. The list and its records are moved to Trash together; both projectId and todoListId are required. Deletion is reversible — a workspace OWNER/ADMIN can restore the list (and exactly the records that went down with it) with restoreRecordList until it is purged.

mutation DeleteRecordList {
  deleteRecordList(input: { projectId: "project_123", todoListId: "list_123" }) {
    success
    operationId
  }
}

Restore a deleted list with restoreTodoList, which returns the restored TodoList:

mutation RestoreTodoList {
  restoreTodoList(id: "list_123") {
    id
    title
  }
}

Mark every record in a list done or undone

markRecordListAsDone and markRecordListAsUndone set the done state on every record in a list. Pass an optional filter (a TodosFilter) to scope the change to a subset of records — for example, only records carrying a given tag. Both return a nullable Boolean.

mutation MarkListDone {
  markRecordListAsDone(todoListId: "list_123")
}

mutation MarkListUndone {
  markRecordListAsUndone(
    todoListId: "list_123"
    filter: { companyIds: ["company_123"], tagIds: ["tag_123"] }
  )
}

Parameters

CreateRecordListInput

FieldTypeRequiredDescription
projectIdString!YesWorkspace the list is created in (ID or slug).
titleString!YesDisplay name of the list.
positionFloat!YesSort position. Lists render in ascending position order.

EditRecordListInput

FieldTypeRequiredDescription
todoListIdString!YesThe list to update.
titleStringNoNew display name.
positionFloatNoNew sort position.
isLockedBooleanNoLock the list to prevent further changes to it.

DeleteRecordListInput

FieldTypeRequiredDescription
projectIdString!YesWorkspace the list belongs to (used to verify scope).
todoListIdString!YesThe list to delete.

markRecordListAsDone / markRecordListAsUndone arguments

ArgumentTypeRequiredDescription
todoListIdString!YesThe list whose records are updated.
filterTodosFilterNoRestrict the operation to records matching this filter. When omitted, every record in the list is updated.

TodosFilter is the same record-filter input used to query records — it supports companyIds (required when supplied), projectIds, tagIds, assigneeIds, done, search, duedAt, and more. See List records for the full field set.

TodoListsFilterInput

Used by recordListQueries.recordLists.

FieldTypeRequiredDescription
companyIds[String!]!YesOrganizations to search within.
projectIds[String!]NoRestrict to specific workspaces.
ids[String!]NoRestrict to specific list IDs.
titles[String!]NoMatch exact list titles.
searchStringNoCase-insensitive title search.

The recordListQueries.recordLists field also accepts sort: [TodoListsSort!] (default [position_ASC]), skip: Int (default 0), take: Int (default 20), and distinct: [TodoListsFilterDistinct!] to collapse duplicate rows. TodoListsFilterDistinct has a single value, title.

TodoListsSort

ValueDescription
title_ASCTitle, A→Z.
title_DESCTitle, Z→A.
createdAt_ASCOldest created first.
createdAt_DESCNewest created first.
updatedAt_ASCLeast recently updated first.
updatedAt_DESCMost recently updated first.
position_ASCBy position, ascending (default).
position_DESCBy position, descending.

Response

createRecordList and editRecordList return the RecordList:

{
  "data": {
    "createRecordList": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "title": "Sprint 1",
      "position": 1
    }
  }
}

deleteRecordList returns a MutationResult:

{
  "data": {
    "deleteRecordList": {
      "success": true,
      "operationId": "clm4n8qwx000108l0h2pyerz9"
    }
  }
}

markRecordListAsDone and markRecordListAsUndone return a nullable Boolean:

{
  "data": {
    "markRecordListAsDone": true
  }
}

RecordList

FieldTypeDescription
idID!Unique identifier.
uidString!Short human-readable identifier.
titleString!Display name.
positionFloat!Sort position within the workspace.
isDisabledBoolean!Whether the list is disabled.
isLockedBooleanWhether the list is locked against changes.
completedBooleanWhether every record in the list is done.
editableBooleanWhether the current user may edit this list.
deletableBooleanWhether the current user may delete this list.
createdAtDateTime!Creation timestamp.
updatedAtDateTime!Last-update timestamp.
createdByUserUser who created the list.
projectWorkspaceParent workspace.
activityActivityAssociated activity-feed entry.
assignees[User!]!Distinct users assigned to records in this list.
tags[Tag!]!Distinct tags applied to records in this list.
todos[Record!]!Records in this list (see arguments below).
todosCountInt!Count of records in this list (accepts the same filter arguments).
todosMaxPositionFloat!Highest record position in the list — useful when appending a new record.

The todos and todosCount fields accept arguments to filter the records they resolve: search: String, tagIds: [String!], assigneeIds: [String!], unassigned: Boolean, done: Boolean, duedAt: DateTime, duedAtStart: DateTime, duedAtEnd: DateTime, startedAt: DateTime, fields: JSON, and op: FilterLogicalOperator. todos additionally accepts first: Int, skip: Int, and orderBy: TodoOrderByInput.

query OpenRecordsInList {
  recordList(id: "list_123") {
    title
    todosCount(done: false)
    todos(done: false, first: 25) {
      id
      title
      duedAt
    }
  }
}

RecordListsPagination

FieldTypeDescription
items[RecordList!]!The page of lists.
pageInfoPageInfo!Pagination metadata.

PageInfo exposes totalItems, totalPages, page, perPage, hasNextPage, and hasPreviousPage. RecordListsPagination has no totalCount sibling field — read the total from pageInfo.totalItems.

MutationResult

FieldTypeDescription
successBoolean!Whether the operation completed.
operationIdStringIdentifier for the operation, echoed by the matching subscription event.

Errors

CodeWhen
TODO_LIST_NOT_FOUNDThe todoListId does not exist. Message: Todo list was not found.
PROJECT_NOT_FOUNDThe projectId does not exist or you are not a member of it. Message: Project was not found.
FORBIDDENThe workspace already has 50 lists (createRecordList), or your role lacks permission for the operation (message: You are not authorized.).
{
  "errors": [
    {
      "message": "You have reached the maximum number of todo lists for this project.",
      "extensions": { "code": "FORBIDDEN" }
    }
  ]
}
{
  "errors": [
    {
      "message": "You are not authorized.",
      "extensions": { "code": "FORBIDDEN" }
    }
  ]
}

Permissions

All list mutations require an active (non-archived) workspace and deny VIEW_ONLY and COMMENT_ONLY roles. Creating and deleting lists additionally deny CLIENT.

OperationAllowed roles
createRecordListOWNER, ADMIN, MEMBER
editRecordListOWNER, ADMIN, MEMBER, CLIENT
deleteRecordListOWNER, ADMIN, MEMBER
markRecordListAsDoneOWNER, ADMIN, MEMBER, CLIENT
markRecordListAsUndoneOWNER, ADMIN, MEMBER, CLIENT

Custom roles refine this further: canCreateLists gates createRecordList, canEditLists gates editRecordList, and per-list deletable flags can override canEditLists for deleteRecordList.

Positioning

position is a Float, so you can insert a list between two existing ones without renumbering — give it a value between their positions (e.g. 1.5 to sit between 1 and 2). Spacing existing lists out as 1, 2, 3 leaves room to insert later.