Skip to main content

Instance Filtering Guide

Overview

The vNext workflow system provides powerful filtering capabilities for querying instances. You can filter on both Instance table columns and JSON data fields using either legacy format or GraphQL-style JSON format.

Supported Routes

Use this route for listing and filtering workflow instances (including GetInstancesTask and integrations that replaced the old workflow-level patterns):

GET /{domain}/workflows/{workflow}/instances?filter={...}

2. Function/Data route (instance-scoped data)

GET /{domain}/workflows/{workflow}/instances/{instance}/functions/data?filter={...}

Note: For bulk / workflow-level queries, prefer .../instances?filter=.... The unscoped GET .../workflows/{workflow}/functions/data pattern is not the supported path for GetInstancesTask as of (see release notes (Phase 3 — Release Notes)).

Both supported list/filter entry points use the same filter query parameter semantics where applicable.


Filter Formats

Legacy Format

Simple key-value format: field=operator:value

JSON-based format with logical operator support: {"field":{"operator":"value"}}

** breaking change:** The filter parameter must be a single expression (one JSON object or string). The previous array format "filter": ["expr1", "expr2"] is no longer supported. Use a single expression; combine conditions with and/or inside that expression (e.g. {"and":[{"status":{"eq":"Active"}},{"attributes.amount":{"gt":"500"}}]}).


Filterable Fields

Instance Table Columns

Direct database columns:

ColumnTypeDescriptionSupported Operators
keystringInstance keyeq, ne, like, startswith, endswith, in, nin
flowstringWorkflow nameeq, ne, like, startswith, endswith, in, nin
statusstringInstance statuseq, ne, in, nin
currentState (or state)stringCurrent stateeq, ne, like, startswith, endswith, in, nin
effectiveStatestringEffective state nameeq, ne, like, startswith, endswith, in, nin
effectiveStateTypeintEffective state type codeeq, ne, gt, ge, lt, le, in, nin
effectiveStateSubTypeintEffective state subtype code (+;: 7 = Cancelled, 8 = Timeout)eq, ne, gt, ge, lt, le, in, nin
createdAtDateTimeCreation timeeq, ne, gt, ge, lt, le, between
modifiedAtDateTimeModification timeeq, ne, gt, ge, lt, le, between
completedAtDateTimeCompletion timeeq, ne, gt, ge, lt, le, between
isTransientbooleanTransient flageq, ne

JSON Data Fields (attributes)

Fields stored in the instance's JSON data can be filtered using the attributes prefix. However, whether a JSON field is filterable and sortable depends on the vocabulary (x-filterOperators / x-sortable) declared for that field in the master schema — see Schema-Driven Filterability & Sorting.


Schema-Driven Filterability & Sorting

Instance table columns (key, status, createdAt …) are directly filterable/sortable. For JSON (attributes.*) fields, this capability is determined by the keywords the field carries in the master schema. The Data Function and instance-listing endpoints honor this definition:

KeywordEffect
x-filterOperators (string[])Allowed filter operators. Empty or absent means the field is not filterable
x-sortable (boolean)When true, the field is sortable; absent means not sortable
x-displayFormat (string)UI-facing format hint (e.g. yyyy-MM-dd'T'HH:mm:ssXXX) — does not affect filtering/sorting

For keyword definitions, see Schema → Filter & Sort Vocabulary.

Type-Operator Relationship

The behavior of an allowed operator depends on the field's JSON Schema type:

Schema typeOperator categorySQL behavior
number / integergt, lt, ge, le, betweenaccessor::numeric {op} @param
string + gt/lt/ge/le/betweendate compareaccessor::timestamptz {op} @param
string + eq/like/startswith/endswith/matchtext compareaccessor ILIKE @param
booleaneq, neequality
array (JSON array in instance data)includesData @> @param; single-element array + partial object pattern at the leaf path

Rules

  1. If x-filterOperators is present and non-empty, the field is filterable. Empty or absent means not filterable.
  2. If x-sortable: true, the field is sortable. Otherwise it is not.
  3. Querying a non-filterable field, or using a disallowed operator, raises SchemaFilterValidationException.
  4. For the GraphQL-only includes operator on JSON array fields, includes must also be listed in the field's x-filterOperators. Payload size and nesting depth are bounded by InputValidator limits.
"startDateTime": {
"type": "string",
"format": "date-time",
"x-filterOperators": ["eq", "gt", "ge", "lt", "le", "between"],
"x-sortable": true,
"x-displayFormat": "yyyy-MM-dd'T'HH:mm:ssXXX"
}

Supported Operators

OperatorDescriptionExample Value
eqEquals"1111"
neNot equals"test"
gtGreater than"100"
geGreater than or equal"100"
ltLess than"100"
leLess than or equal"100"
betweenBetween (inclusive)["2024-01-01", "2024-12-31"]
likeContains (case insensitive)"workflow"
startswithStarts with"payment"
endswithEnds with"flow"
inIn list["Active", "Busy"]
ninNot in list["Completed", "Faulted"]
isnullNull or not nulltrue or false

Status Values

The status field accepts both code and name:

Status NameCodeDescription
ActiveAInstance is active
BusyBInstance is processing
CompletedCInstance completed successfully
FaultedFInstance encountered an error
PassivePInstance is passive

: Filtering on status and state (currentState) now works correctly in instance queries.


OrderBy / Sort

Instance list and data endpoints support sorting via the sort or orderBy query parameter.

Single field

?sort={"field":"createdAt","direction":"desc"}
?orderBy={"field":"status","direction":"asc"}

Multiple fields

?sort={"fields":[{"field":"status","direction":"asc"},{"field":"createdAt","direction":"desc"}]}
  • direction: "asc" or "desc" (case-insensitive). Defaults to "asc" if omitted.

Sortable fields

FieldNotes
createdAtCreation timestamp
modifiedAtModification timestamp
completedAtCompletion timestamp
statusInstance status
keyInstance key
currentState / stateCurrent state (state is alias)
attributes.fieldNameJSON path into instance data; nested paths supported (e.g. attributes.nested.path). Only fields carrying x-sortable: true in the master schema are sortable

Instance columns are applied in the database; ordering by attributes.* uses the latest instance data JSON and is subject to the same schema/security as filtering (see Schema-Driven Filterability & Sorting).


GraphQL Format Examples

1. Simple Instance Column Filter

GET /banking/workflows/payment-workflow/instances?filter={"key":{"eq":"payment-12345"}}

2. Multiple Instance Column Filters (AND Logic)

Multiple fields at the same level are combined with AND logic:

GET /banking/workflows/payment-workflow/instances?filter={"status":{"eq":"Active"},"createdAt":{"gt":"2024-01-01"}}

3. JSON Data Field Filter (attributes)

Filter on JSON data fields using the attributes prefix:

GET /banking/workflows/payment-workflow/instances?filter={"attributes":{"customerId":{"eq":"CUST-123"}}}

4. Mixed Filter (Instance + JSON Fields)

GET /banking/workflows/payment-workflow/instances?filter={"key":{"like":"payment"},"status":{"eq":"Active"},"attributes":{"amount":{"gt":"500"}}}

5. Date Range Filter

GET /banking/workflows/payment-workflow/instances?filter={"createdAt":{"between":["2024-01-01","2024-01-31"]}}

6. Status IN Filter

GET /banking/workflows/payment-workflow/instances?filter={"status":{"in":["Active","Busy"]}}

7. EffectiveState Filters

Filter by Effective State Name:

GET /banking/workflows/payment-workflow/instances?filter={"effectiveState":{"eq":"awaiting-approval"}}

Filter by Effective State SubType (Human Tasks):

GET /approvals/workflows/approval-flow/instances?filter={"effectiveStateSubType":{"eq":"6"}}

Filter by Effective State SubType (Busy Tasks):

GET /processing/workflows/order-flow/instances?filter={"effectiveStateSubType":{"eq":"5"}}

Combined Status and EffectiveState Filter:

GET /core/workflows/payment/instances?filter={"status":{"eq":"Active"},"effectiveStateSubType":{"eq":"6"}}

EffectiveState SubType Values:

  • 0 - None
  • 1 - Success
  • 2 - Error
  • 3 - Terminated
  • 4 - Suspended
  • 5 - Busy (processing in progress)
  • 6 - Human (human interaction required)

Logical Operators

AND Operator

Combines multiple conditions where all must be true:

{
"and": [
{"status": {"eq": "Active"}},
{"attributes": {"amount": {"gt": "500"}}}
]
}

OR Operator

Combines multiple conditions where any can be true:

{
"or": [
{"key": {"eq": "payment-12345"}},
{"key": {"eq": "payment-12346"}}
]
}

NOT Operator

Negates a condition:

{
"not": {"status": {"in": ["Completed", "Faulted"]}}
}

Complex Nested Example

{
"and": [
{"status": {"eq": "Active"}},
{
"or": [
{"attributes": {"priority": {"eq": "high"}}},
{"attributes": {"amount": {"gt": "10000"}}}
]
}
]
}

Group By and Aggregations

Group By with Count

GET /banking/workflows/payment-workflow/instances?filter={"groupBy":{"field":"attributes.status","aggregations":{"count":true}}}

Response:

{
"groups": [
{"name": "pending", "count": 45},
{"name": "approved", "count": 123},
{"name": "rejected", "count": 12}
]
}

Group By with Multiple Aggregations

GET /banking/workflows/payment-workflow/instances?filter={"groupBy":{"field":"attributes.currency","aggregations":{"count":true,"sum":"attributes.amount","avg":"attributes.amount","min":"attributes.amount","max":"attributes.amount"}}}

Response:

{
"groups": [
{"name": "USD", "count": 150, "sum": 450000, "avg": 3000, "min": 10, "max": 50000},
{"name": "EUR", "count": 75, "sum": 180000, "avg": 2400, "min": 50, "max": 25000}
]
}

Supported Aggregations

AggregationDescription
countCount of items in group
sumSum of numeric field
avgAverage of numeric field
minMinimum value
maxMaximum value

Fluent InstanceQuery Builder

In script mappings (.csx), prefer the fluent InstanceQuery builder over hand-concatenated filter/sort JSON. One builder describes every instance query in the platform. It lives in BBT.Workflow.Filtering, which is part of the script engine's default imports — no using directive needed.

How you end the chain decides what you get:

TerminalProducesUsed for
.First() / .Last()A filter resolving exactly one instanceEvent correlation (EventMappingResult.Selector) — see Event-Driven Workflows
.Build()An InstanceQuerySpec list/report queryGetInstancesTask.SetFilterSpec(...), or wire strings for DaprServiceTask
var query = InstanceQuery.Create()
.Where("currentState", f => f.Eq("active"))
.Where("attributes.amount", f => f.Ge(1000).Lt(5000))
.OrGroup(
q => q.Where("attributes.city", f => f.Eq("London")),
q => q.Where("attributes.city", f => f.Eq("Paris")))
.OrderByDescending("createdAt");

getInstancesTask.SetFilterSpec(query.Build());

Key points:

  • Fields: bare names address whitelisted instance columns (id, key, flow, status, currentState, effectiveState, stage, createdAt, ...); attributes.-prefixed dotted paths address instance-data JSON at any depth. Unknown column names throw instead of silently matching nothing.
  • Operators (Eq, Ne, Gt/Ge/Lt/Le, Like, StartsWith/EndsWith, In/NotIn, Between, IsNull, Includes) emit the same GraphQL wire JSON documented above. Includes (array containment) is a list-query-only feature.
  • Composition: top-level Where calls AND together; OrGroup(...) adds OR branches (conditions inside a branch AND together); Not(...) negates; operators chained on one field AND together (f.Ge(18).Lt(65)).
  • Ordering: OrderBy / OrderByDescending; default is createdAt ascending. Numeric attributes order numerically.
  • GroupBy + aggregations (Count, Sum, Avg, Min, Max) are list-query features; they throw at build time with First()/Last().
  • Guardrails: First()/Last() with zero conditions throws; values are always serialized by the spec — escaping/injection handled for you.
  • Serializers for raw DaprServiceTask wiring: ToFilterJson(), ToSortJson(), ToGroupByJson(), ToAggregationsJson(), ToQueryString(page, pageSize).

Prefer GetInstancesTask + SetFilterSpec(query.Build()) for new code — same-domain queries run in-process with no HTTP/Dapr hop.

🚧 Full English translation of this section is pending. See the Turkish page for the complete operator table, type semantics, and consumption-point examples.

Best Practices

1. Use GraphQL Format for Complex Queries

GraphQL format is more readable and supports logical operators.

Good:

{
"and": [
{"status": {"eq": "Active"}},
{"attributes": {"amount": {"gt": "500"}}}
]
}

2. Use Specific Fields for Better Performance

Filter on indexed Instance columns when possible.

Better Performance:

{"key": {"eq": "payment-12345"}}

Slower:

{"attributes": {"unindexedField": {"eq": "value"}}}

3. Use Status Names for Readability

{"status": {"eq": "Active"}}

is equivalent to:

{"status": {"eq": "A"}}

4. Use Group By for Analytics

When you need statistics, use group by instead of fetching all records.

{
"groupBy": {
"field": "attributes.status",
"aggregations": {"count": true, "sum": "attributes.amount"}
}
}

5. Always Use Pagination

Always use page and pageSize parameters:

GET /banking/workflows/payment-workflow/instances?filter={...}&page=1&pageSize=20

Error Handling

Invalid Filter Syntax

{
"error": {
"code": "invalid_filter",
"message": "Invalid filter syntax. Valid JSON expected."
}
}

Unsupported Operator

{
"error": {
"code": "unsupported_operator",
"message": "'regex' operator is not supported",
"supportedOperators": ["eq", "ne", "gt", "ge", "lt", "le", "between", "like", "startswith", "endswith", "in", "nin", "isnull"]
}
}

Invalid Column Name

{
"error": {
"code": "invalid_column",
"message": "'invalidColumn' is not a valid Instance column. Use 'attributes.fieldName' for JSON fields.",
"validColumns": ["key", "flow", "status", "currentState", "createdAt", "modifiedAt", "completedAt", "isTransient"]
}
}

Schema Filter Validation Error

Querying a field that is not filterable in the master schema (x-filterOperators empty/absent), or using a disallowed operator, raises SchemaFilterValidationException. The same applies to sorting via x-sortable. See Schema-Driven Filterability & Sorting.


Performance Tips

  1. Use Pagination: Always use page and pageSize parameters
  2. Filter on Indexed Columns: Prefer key, status, createdAt for better performance
  3. Limit Group By Fields: Use maximum 2-3 fields for optimal performance
  4. Use Date Ranges Wisely: Narrow date ranges improve query performance
  5. Avoid Wildcard Searches on Large Datasets: Use startswith or endswith instead of like when possible