QQL (Quevell Query Language) is the language for asking questions about issues: in search, in saved filters, on boards, and in the conditions of automation rules. It is one language with one parser everywhere: a query that works in search works verbatim in a rule's condition.
If you write JQL, the syntax will feel familiar: =, in, ~, and, order by mean what you expect. History predicates are deliberately left out (was, changed). How long a query takes is predictable and does not depend on how it is phrased: every operator has a known cost.
Syntax
A query is a condition and, optionally, an ordering:
status = "In progress" and assignee = currentUser() order by updated desc
- Conditions combine with
andandor, group with parentheses, negate withnot. - Case does not matter:
Status,statusandSTATUSare one field;ANDandandare one word. - Emptiness is tested with keywords:
dueDate is empty,assignee is not empty. There is deliberately no way to write equality with "nothing". - Value lists:
status in ("To do", "In progress"),assignee not in (anna@example.com, ivan@example.com). - An ordering alone is a whole query:
order by updated descmeans "everything visible to me, freshest first".
A query returns only what the user running it sees: issues in projects that are not visible to you do not exist for your query. The same holds for automation rules: a rule's conditions run with the rights of the account the rule acts as.
Operators
| Operator | Meaning |
|---|---|
= , != | Equals, does not equal |
in , not in | Is in a list, is not |
~ | Contains text |
is empty , is not empty | Empty, not empty |
> >= < <= | Comparison of numbers, dates and durations |
Not every operator applies to every field: a date has no ~, a text has no >. Such a query does not quietly return nothing: it is rejected at parse time, pointing at the position and listing the operators the field does accept. The full picture is in the tables below.
Operator precedence
not binds tighter than and, and tighter than or. a or b and c means a or (b and c). Parentheses always win: (a or b) and c.
Quoting and escaping
A value with spaces or special characters goes in quotes, double or single: status = "In progress", summary ~ 'new office'. There is no escaping inside quotes: a string containing double quotes goes in single quotes, and the other way round. Values without spaces are written bare: OFFICE-13, -7d, 2026-08-24, anna@example.com.
Values of enumerated fields resolve from any of their spellings: the key, the default name, and every translation. status = "In progress" and its Russian spelling are the same query: the value becomes the stable key, and the filter survives the status being renamed.
System fields
This table is generated from the product's code and cannot fall behind it.
| Field | Operators | Sorts |
|---|---|---|
archived | = != | no |
assignee | = != in not in is empty is not empty | no |
created | > >= < <= | yes |
description | = != ~ is empty is not empty | no |
dueDate | is empty is not empty > >= < <= | yes |
key | = != in not in | yes |
level | = != in not in | no |
originalEstimate | = != is empty is not empty > >= < <= | yes |
parent | = != in not in is empty is not empty | no |
priority | = != in not in is empty is not empty | yes |
project | = != in not in | yes |
remainingEstimate | = != is empty is not empty > >= < <= | yes |
reporter | = != in not in is empty is not empty | no |
sprint | = != in not in is empty is not empty | no |
startDate | is empty is not empty > >= < <= | yes |
status | = != in not in | no |
statusCategory | = != in not in | no |
summary | = != ~ is empty is not empty | no |
team | = != in not in is empty is not empty | no |
text | ~ | no |
timeSpent | = != > >= < <= | yes |
type | = != in not in | no |
updated | > >= < <= | yes |
watcher | = != in not in is empty is not empty | no |
Notes on individual fields:
keyandparenttake issue keys:key in (OFFICE-1, OFFICE-2),parent = OFFICE-13. Key case does not matter.textis what the plain search box searches: summary and description together.~only.watcherasks who watches the issue:watcher = currentUser()finds the issues you watch. One issue has several watchers, so equality here means "among the watchers".watcher is emptyfinds issues nobody watches.teamasks whether a team is marked on the issue:team = "Logistics". One issue can have several teams, so equality here means containment.sprintasks which sprint an issue is in:sprint = "Sprint 5". The Sprint field on an issue lists every sprint it has been in, closed ones included, and the query reads it the same way: an issue that was in a sprint by that name is still found once the sprint is closed.sprint is emptyfinds issues that have never been in a sprint.- Sprints can share a name, across projects or within one.
sprint = "Sprint 5"finds the issues of every sprint by that name you can see. When the name belongs to several projects, the suggestion shows each project key beside it and inserts only the name. Case does not matter, and a space at either end inside the quotes counts as part of the name. statusCategorytakes three values (TODO,IN_PROGRESS,DONE) and the spellingsto doandin progress.prioritytakeshighest,high,medium,low,lowest.- A query leaves archived issues out until it asks about them:
archived = truefinds only the archived ones,archived = falseonly those in use.yesandnowork as values too. - Durations (
timeSpent,originalEstimate,remainingEstimate) compare in the time-tracking form:timeSpent > 1d,originalEstimate <= 2w, where1w= 5 days,1d= 8 hours. startDateanddueDateare calendar days: "due this week" is the same question in every timezone.createdandupdatedare instants.
Custom fields
A custom field is referenced by its name: Budget > 100000, "Launch date" < startOfMonth(). Its operators come from its type:
| Field type | Operators | Sorts |
|---|---|---|
| Date | = != is empty is not empty > >= < <= | yes |
| Issue | = != in not in is empty is not empty | no |
| Multi-select | in not in is empty is not empty | no |
| Number | = != is empty is not empty > >= < <= | yes |
| Paragraph | ~ is empty is not empty | no |
| Select | = != in not in is empty is not empty | yes |
| Text | = != ~ is empty is not empty | no |
| URL | = != ~ is empty is not empty | no |
| User | = != in not in is empty is not empty | no |
- A number has no
~: a substring of a number says nothing about it. A paragraph has no=: exact equality with a page of text is a question nobody has ever meant to ask. A multi-select is queried only withinandnot in. - Fields are defined per project, so one name can refer to several field definitions. A query by name covers all of them, as long as they share a type; a name that means a date in one project and a paragraph in another behaves as text.
- A query stores stable identifiers, not names: renaming a field or an option does not break a saved filter.
Functions
| Function | What it means |
|---|---|
currentUser() | Whoever runs the query. In a saved filter, that is whoever opened it. |
myReports() | My direct reports by the organization chart. Not including me. |
myVertical() | Everybody under me in the tree, any depth. Not including me. |
myTeammates() | Everybody in every team I am in, whatever the role. Not including me. |
now() | The current moment. |
startOfDay() , startOfWeek() , startOfMonth() | The start of today, of this week (Monday), of this month. |
myReports(), myVertical() and myTeammates() return several users, so they are used with in: assignee in (myReports()). The form assignee = myReports() is rejected with a hint. "My reports and me" is written as: assignee in (myReports(), currentUser()).
User and time functions resolve on every run, not at save time: a filter for "updated in the last week" always means the week before the moment it is opened. Which day that week ends on belongs to whoever is asking: a person's own query uses the timezone on their profile, a rule on a schedule uses its own, and with neither it is UTC.
Dates and relative time
Date and moment fields compare against:
- a fixed date:
duedate <= 2026-09-15; - a relative offset:
updated > -7d(seven days ago),duedate <= +7d(seven days ahead); unitsd,w,mfor days, weeks and months; - functions:
created >= startOfMonth(),updated > startOfWeek().
Users
A user is named by display name or email: assignee = "Anna Petrova", assignee = anna@example.com. When two users share a name, a query by that name is rejected and asks for the email address: namesakes happen, and the query has to say which one it means.
Sorting
order by field plus a direction: asc (the default) or desc. The tables above show which fields are sortable; ordering by an unsortable field is rejected at parse time. One sort key per query.
project = OFFICE and status != Done order by duedate asc
Error messages
A parse error points at a position in the query and says exactly what is not right there. The common messages and what they mean:
| Message | What it means |
|---|---|
There is no field called "..." | No such field. Check the name; custom fields are referenced by field name, statuses are values of status. |
"..." does not support ~. It accepts =, !=, ... | The operator does not apply to this field; the list names the ones that do. |
There is no status called "..." | The value is not in the field's vocabulary. If the list is short, the message includes it. |
Nobody here is called "..." | No user with that name or address. |
2 users are called "...". Use their email address instead | The name is ambiguous; use the email address. |
"..." is not a date. Use 2026-08-24, -7d, startOfWeek() or now() | The value does not read as a date or an offset. |
That is not a duration. Use the 1w 2d 4h 30m form (1w = 5d, 1d = 8h) | The duration is written in another form. |
"..." is not an issue key like PROJECT-12 | The field expects an issue key. |
"..." cannot be ordered by | The field does not sort. |
This quote is never closed | A quote is opened and never closed. |
myReports() names several users. Ask with "assignee in (myReports())" | The function returns several users and works only with in. |
Examples
assignee = currentUser() and status != Done order by duedate asc
My open issues, nearest deadlines first.
project in (OFFICE, WAREHOUSE) and priority in (highest, high) and status = "In progress"
Urgent work in progress across two projects.
assignee in (myVertical()) and duedate < startOfWeek() and status != Done
Overdue work anywhere under me in the tree.
summary ~ "office move" or description ~ "office move"
Issues mentioning the office move in the summary or the description. The same, shorter: text ~ "office move".
(assignee = currentUser() or reporter = currentUser() or watcher = currentUser()) and statusCategory != DONE
Everything open that concerns me: as assignee, reporter or watcher.
type = Bug and created >= -30d and assignee is empty
A month of bugs that nobody owns.
"Budget" > 500000 and "Launch date" <= startOfMonth()
Custom fields: a number and a date.
team = "Logistics" and remainingEstimate > 1w
The team's issues with more than a week of work left.