Skip to main content

TQL query language

TQL, the TOW Query Language, filters tickets with readable expressions like:

status = open AND label = backend ORDER BY priority ASC

TQL is designed for saved ticket views, board definitions, and advanced ticket filtering. It is intentionally similar to Jira JQL, but it uses TOW ticket fields, project workflows, and project custom fields.

Basic shape

A TQL query has an optional filter expression and optional ordering:

expression ORDER BY field ASC

Examples:

status = open
type IN (bug, story) AND priority <= 2 ORDER BY due DESC
(summary ~ "billing" OR description ~ "billing") AND NOT status = done
project = APP AND cf.customer_tier = enterprise

Boolean logic

Use AND, OR, NOT, and parentheses to combine filters.

TQL applies precedence in this order:

  1. NOT
  2. AND
  3. OR

When in doubt, use parentheses:

(status = blocked OR priority = 1) AND assignee = currentUser()

Operators

OperatorMeaningExample
=Equalsstatus = blocked
!=Does not equaltype != epic
<Less thanpriority < 3
<=Less than or equaldue <= 2026-06-30
>Greater thanestimate > 3
>=Greater than or equalprogress >= 50
IN (...)Equals one of several valuestype IN (bug, story)
NOT IN (...)Does not equal any listed valuestatus NOT IN (done, archived)
IS EMPTYHas no valueassignee IS EMPTY
IS NOT EMPTYHas a valuedue IS NOT EMPTY
~Text containssummary ~ "checkout"
!~Text does not containdescription !~ "deprecated"

Range operators only work on numeric, date, and datetime fields. Text fields support equality, IN, ~, !~, and empty checks where the field can be empty.

Values

TQL accepts:

  • Bare values: open, APP, backend
  • Quoted strings: "launch plan", 'customer pilot'
  • Numbers: 1, 3.5
  • ISO dates: 2026-06-30
  • ISO datetimes: 2026-06-30T12:30:00Z
  • Functions: currentUser(), today(), now()
  • Relative dates: -7d, +2w, -1m, +1y

Use quoted strings when a value contains spaces, punctuation, or words that could be confused with TQL keywords:

summary ~ "launch plan"

Relative date units are:

UnitMeaning
hHours
dDays
wWeeks
m30-day months
y365-day years

Built-in fields

FieldAliasesCommon operatorsNotes
issue_keykey, issue=, !=, IN, ~Jira-style ticket key, such as TOW-12 or APP-4.
issue_number=, IN, rangesNumeric part of the issue key.
project=, !=, IN, IS EMPTYProject key or project ID.
issue_typetype=, !=, INtask, bug, story, epic, subtask.
status=, !=, INProject workflow status. status = open means active work.
status_category=, !=, INtodo, in_progress, or done.
titlesummary=, !=, IN, ~, !~Ticket title.
description=, !=, IN, ~, !~, IS EMPTYTicket description.
priority=, IN, rangesPriority is numeric, where lower numbers are more urgent.
start_date=, IN, ranges, IS EMPTYISO date.
due_datedue=, IN, ranges, IS EMPTYISO date.
created_atcreated=, IN, rangesISO datetime.
updated_atupdated=, IN, rangesISO datetime.
labelslabel=, IN, ~, !~, IS EMPTYMatches ticket labels.
assignee=, IN, IS EMPTYUser ID, me, currentUser(), or unassigned.
parent=, IN, IS EMPTYParent ticket issue key or ID.
progress_percentprogress=, IN, rangesNumeric progress from 0 to 100.
estimate_pointsestimate=, IN, ranges, IS EMPTYNumeric estimate.
rank=, IN, rangesTicket rank used for ordering.

Status and open work

status filters match workflow status keys. For projects with custom workflows, use the status key configured in project settings:

status = working

status = open is a shortcut for active work. It matches tickets in active status categories, such as todo or in progress.

Use status_category when you want a workflow-independent filter:

status_category = in_progress

Assignees

Use currentUser() or me for tickets assigned to you:

assignee = currentUser()

Use unassigned or IS EMPTY for tickets without an assignee:

assignee = unassigned
assignee IS EMPTY

Labels

Labels are normalized before matching. These examples find tickets with a backend label:

label = backend
labels IN (backend, infrastructure)

Use ~ for partial label text:

labels ~ infra

Custom fields

Project custom fields use the cf. prefix:

cf.customer_tier = enterprise

Because custom fields are defined per project, TQL can only use cf.<key> when there is a single project scope. Provide that scope with a project filter:

project = APP AND cf.customer_tier = enterprise

Or run the query inside a project-scoped ticket view.

Custom field operators depend on the field type:

Custom field typeSupported filters
Short text, paragraph=, !=, IN, NOT IN, ~, !~, IS EMPTY
Number=, !=, IN, NOT IN, ranges, IS EMPTY
Date, datetime=, !=, IN, NOT IN, ranges, IS EMPTY
Single select, multi select, checkboxes=, !=, IN, NOT IN, IS EMPTY
User single, user multi=, !=, IN, NOT IN, IS EMPTY
Labels=, !=, IN, NOT IN, ~, !~, IS EMPTY

Examples:

project = APP AND cf.budget >= 10000
project = APP AND cf.launch_date < today()
project = APP AND cf.regions IN (us, emea)
project = APP AND cf.reviewer = currentUser()

Ordering

Add ORDER BY to control result order:

status = open ORDER BY priority ASC, updated DESC

If no direction is provided, TQL sorts ascending:

status = open ORDER BY due

Ordering supports built-in sortable fields, such as priority, due, updated, created, rank, summary, status, and issue_number.

Custom-field ordering is not supported in TQL v1.

Limits and safety

TOW validates TQL before running it. Invalid or overly complex queries return a validation error instead of running.

Current safety limits include:

  • Maximum query length: 10,000 characters
  • Maximum tokens: 500
  • Maximum nesting depth: 64
  • Maximum IN list size: 100 values
  • Maximum total values: 200
  • Maximum predicates: 80
  • Maximum text-search predicates: 25
  • Maximum custom-field predicates: 25
  • Maximum ORDER BY fields: 10

TQL values are handled as query parameters. User-entered strings are not executed as SQL.

Common examples

Open work assigned to you:

status = open AND assignee = currentUser()

High-priority blocked work:

status = blocked OR (priority = 1 AND status = open)

Tickets due in the next week:

due >= today() AND due <= +7d

Recent work that mentions a customer:

summary ~ "Acme" OR description ~ "Acme"

Open bugs in a project:

project = APP AND type = bug AND status = open ORDER BY priority ASC

Enterprise customer work with no reviewer:

project = APP AND cf.customer_tier = enterprise AND cf.reviewer IS EMPTY

Troubleshooting

If a query returns a validation error:

  • Check that field names are spelled correctly.
  • Quote values that contain spaces or punctuation.
  • Add project = KEY before using cf.<key> custom fields.
  • Use range operators only with number, date, or datetime fields.
  • Use ORDER BY only with built-in sortable fields.

If a query returns fewer tickets than expected, check your project scope and permissions. TQL only returns tickets you are allowed to see.