| name | b24jssdk-filtering |
| description | Build filter, order, and select parameters for Bitrix24 REST methods called via b24jssdk's actions.v{2,3}.* API. Covers the v2 prefix dialect (>=, <=, !, %, =%), the v3 array-triple dialect ([['field', 'op', value]]) and the typed FilterV3 builder (eq/in/between/and/or/not/build), NOT, multi-value (IN), dates via Text.toB24Format, and the order-stripping rule of callList. Load when building filtered queries. |
b24jssdk filtering
Bitrix24 has two filter dialects. They are not interchangeable — each API version accepts only its own. Pick the dialect that matches the action surface you're using.
v2 — prefix-keyed object
Used by $b24.actions.v2.{call,callList,fetchList}.make({ params: { filter: ... }}). The operator is a prefix on the field name.
filter: {
'>=opportunity': 50000,
'<=opportunity': 200000,
'!stageId': 'LOST',
'=%title': 'A%'
}
v2 operators
| Prefix | Meaning | Example |
|---|
| (none) | exact match | { stageId: 'NEW' } |
>= | greater or equal | { '>=opportunity': 50000 } |
> | greater | { '>id': 100 } |
<= | less or equal | { '<=opportunity': 200000 } |
< | less | { '<closeDate': '2026-01-01' } |
! | not equal | { '!stageId': 'LOST' } |
!= | not equal (alternate) | { '!=stageId': 'LOST' } |
% | LIKE (substring, case-insensitive) | { '%title': 'поставка' } |
=% | LIKE with explicit pattern (%/_) | { '=%title': 'A%' } |
!% | NOT LIKE | { '!%title': 'тест' } |
Multiple keys are combined with AND. Two operators on the same field need two separate keys: { '>=opportunity': 50000, '<=opportunity': 200000 }.
v2 IN / multi-value
Plain array means IN:
filter: { stageId: ['NEW', 'PREPARATION', 'EXECUTING'] }
For "not in":
filter: { '!stageId': ['LOST', 'WON'] }
v3 — array of triples
Used by $b24.actions.v3.{call,callList,fetchList}.make({ params: { filter: ... }}). The filter is a JSON array, each element is a condition.
filter: [
['stageId', '=', 'NEW'],
['createdTime', '>=', '2026-01-01T00:00:00+03:00'],
['responsible', 'in', [1, 2, 3]]
]
v3 operators — exhaustive list (only these 8)
= != > >= < <= in between
No %, like, ~, or substring operator at the v3 protocol level — Filtering/Operator.php does not define one. Substring search is currently a v2-only feature.
between value must be a 2-element array: [min, max].
in value must be an array.
v3 condition forms
The two-arg form is sugar:
['id', 42]
['stageId', ['A', 'B']]
The long struct form supports nested groups with or logic and negation (rarely needed in user code):
filter: [
['status', '=', 'OPEN'],
{
type: 'filter',
logic: 'or',
negative: false,
conditions: [
['priority', '=', 'HIGH'],
['responsible', '=', 42]
]
}
]
The top-level array is implicitly logic: 'and'. The type: 'filter' key above is optional — the server infers it — so the FilterV3 builder below emits the shorter { logic, conditions } form (verified live), and both are accepted.
v3 — typed builder (FilterV3)
For anything beyond a flat list of triples, prefer the FilterV3 builder over hand-writing the structs — it validates operators and in/between shapes on the client (a typo fails fast instead of as a server UNKNOWNFILTEROPERATOREXCEPTION):
import { FilterV3 as F } from '@bitrix24/b24jssdk'
const filter = F.build(
F.eq('status', 'NEW'),
F.or(
F.in('id', [1, 2]),
F.gt('id', 100)
)
)
await $b24.actions.v3.call.make({ method: 'tasks.task.list', params: { filter } })
- Leaves:
F.eq / F.ne / F.gt / F.ge / F.lt / F.le / F.in(field, array) / F.between(field, from, to).
- Groups:
F.and(...), F.or(...), F.not(node) (negates a condition or group).
F.build(...nodes) returns the top-level (AND-joined) array for params.filter; falsy nodes are skipped, so F.build(F.eq('a', 1), flag && F.gt('b', 2)) inlines conditionals.
Inside a v3 batch, a filter value can reference an earlier command's output via a $ref/$refArray marker — build them with BatchRefV3 (import { BatchRefV3 } from '@bitrix24/b24jssdk'), e.g. ['id', 'in', BatchRefV3.refArray('tasks.id')]. The server does the substitution; see the b24jssdk-rest skill / the v3 batch docs.
order rule for callList / fetchList
Both actions.v{2,3}.callList.make and fetchList.make strip user-supplied order and force { [cursorIdKey]: 'ASC' } (where cursorIdKey defaults to idKey) because the action uses keyset cursor pagination. If you pass an order, the SDK logs a warning (callList.make: user-provided 'order' parameter is ignored…) and discards it (see the order warning in packages/jssdk/src/core/actions/v2/call-list.ts and the v3 equivalent).
If you need a specific sort order, drop down to call.make and page manually — but you almost certainly want to filter more narrowly instead.
order for single call.make
- v2: object with values
'asc' | 'desc' | 'ASC' | 'DESC' — order: { id: 'asc', amount: 'desc' }.
- v3: object form only (
{ field: 'asc' | 'desc' }). Arrays throw InvalidOrderException. The DTO field must carry the server-side #[Sortable] attribute or you'll get DtoFieldRequiredAttributeException.
Dates
Use the SDK helper Text.toB24Format(date) — it produces the Bitrix24 format yyyy-MM-dd'T'HH:mm:ssZZ and handles Date | DateTime | string inputs (see Text.toB24Format in packages/jssdk/src/tools/text.ts).
import { Text } from '@bitrix24/b24jssdk'
const sixMonthsAgo = new Date()
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6)
filter: { '>=createdTime': Text.toB24Format(sixMonthsAgo) }
filter: [['createdTime', '>=', Text.toB24Format(sixMonthsAgo)]]
Field naming — v2 (classic vs v3-style)
The same logical field has different names depending on the method:
| Logical | Classic methods (crm.deal.list, …) | v3-style methods (crm.item.list) |
|---|
| ID | ID | id |
| Title | TITLE | title |
| Stage | STAGE_ID | stageId |
| Amount | OPPORTUNITY | opportunity |
| Currency | CURRENCY_ID | currencyId |
| Created | DATE_CREATE | createdTime |
| Modified | DATE_MODIFY | updatedTime |
| Assigned to | ASSIGNED_BY_ID | assignedById |
| Custom field | UF_CRM_INN | ufCrmInn |
Both shapes are v2-API. The casing is per-method, not per-API-version. Use the same casing across filter, select, and (where applicable) order. Mixing styles silently breaks paging.
Select
Always pass select to limit response size:
select: ['id', 'title', 'stageId', 'opportunity', 'currencyId']
For custom fields, list them explicitly. For v3, dot syntax expands relations:
select: ['id', 'title', 'responsible.name', 'responsible.email']
Examples
v2 — open deals with amount range
const response = await $b24.actions.v2.callList.make<CrmItem>({
method: 'crm.item.list',
params: {
entityTypeId: EnumCrmEntityTypeId.deal,
filter: {
'!stageId': ['WON', 'LOSE'],
'>=opportunity': 50000,
'<=opportunity': 200000
},
select: ['id', 'title', 'stageId', 'opportunity']
},
idKey: 'id',
customKeyForResult: 'items'
})
v2 — contacts by phone substring
const response = await $b24.actions.v2.callList.make<CrmItem>({
method: 'crm.item.list',
params: {
entityTypeId: EnumCrmEntityTypeId.contact,
filter: { '%phone': '+7916' },
select: ['id', 'name', 'lastName', 'phone']
},
idKey: 'id',
customKeyForResult: 'items'
})
v2 — tasks not closed
const response = await $b24.actions.v2.callList.make<TaskItem>({
method: 'tasks.task.list',
params: {
filter: { '!REAL_STATUS': 5 },
select: ['ID', 'TITLE', 'STATUS', 'RESPONSIBLE_ID']
},
idKey: 'id',
cursorIdKey: 'ID',
customKeyForResult: 'tasks'
})
v3 — eventlog last 6 months
const sixMonthsAgo = new Date()
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6)
sixMonthsAgo.setHours(0, 0, 0, 0)
const response = await $b24.actions.v3.callList.make<EventItem>({
method: 'main.eventlog.list',
params: {
filter: [['timestampX', '>=', Text.toB24Format(sixMonthsAgo)]],
select: ['id', 'userId']
},
idKey: 'id',
customKeyForResult: 'items',
limit: 200
})
v3 — IN / between
filter: [
['responsibleId', 'in', [1, 2, 3]],
['createdTime', 'between', ['2026-01-01T00:00:00+03:00', '2026-03-31T23:59:59+03:00']]
]
Multi-funnel pipelines (CRM)
On portals with multiple funnels, stage IDs come prefixed: C2:WON, C4:LOSE. Strategies:
- Filter by category ID and base stage:
filter: { categoryId: 2, stageId: 'NEW' }.
- Enumerate full list explicitly:
filter: { stageId: ['C2:WON', 'C4:WON'] }.
- Don't trust a bare
'NEW' to match across funnels — read the live stage list from crm.dealcategory.stage.list.
Anti-patterns
- ❌
filter: { stageId: { $gte: 100 } } — MongoDB-style. Not understood by Bitrix24, will 400.
- ❌
filter: [['title', 'like', 'A%']] — like is not in the v3 operator set. Use v2 + =% for substring search.
- ❌ Hand-assembling nested v3 groups (
{ logic, conditions }) by hand — use the FilterV3 builder, which validates operators and in/between shapes client-side.
- ❌ Passing a single condition as
params.filter (filter: F.eq('a', 1)) — filter must be an array; wrap it: filter: F.build(F.eq('a', 1)) (or [F.eq('a', 1)]).
- ❌ Passing
order to callList.make / fetchList.make — silently discarded with a warning.
- ❌ Mixing
STAGE_ID and stageId across filter and select — they're different fields per method.
- ❌ Forgetting timezone in date strings — Bitrix24 portals are configured in a portal timezone. Use
Text.toB24Format() to stay consistent.
- ❌ Using v3 array-of-triples filter for
actions.v2.* — silently misparsed and returns wrong results.
- ❌ Using v2 prefix-keyed filter for
actions.v3.* — returns UnknownFilterOperatorException.