Filter Builder Reference¶
This document is intended to help an AI assistant understand valid filter structures and values for Lucit campaign and item filters.
Generated by bin/docs/generate-filter-builder-reference.php from the filter-builder catalogs and model-class constants. Update the generator rather than editing this build artifact. Group/condition rules describe the current frontend builder; they do not replace the receiving API contract.
Scope¶
- This covers the valid field catalog, data types, logical-group operators, filter operators, and the final JSON object shape used by the Lucit filter builder.
- Export / inventory item filtering used in the Schedule Builder dialog and Item Filter dialog is based on:
date/time, theitem_filterobject (including dynamic key/value fields from the current account key-value stores, typically populated by data source apps), anddigital_board_filter(a combination of the documented digital-board filter fields plus any dynamic key/value fields for digital boards for that account from data source apps). - The Campaign Settings dialog has an Item Filter for restricting which inventory items are loaded into a campaign; that filter uses the
InventoryFieldOptionscatalog. InventoryExportFields,AccountFields,UserFields,LightningDeviceFields, andInventoryFeedFieldsare currently used only by automation flows/apps that create automations; they are not the primary UI item-filter catalogs.- The schedule editor is separate from the item filter/editor. A schedule is managed by
date_range,days_of_the_week, andtime_rangeentries in theschedulesarray, while the item filter usesitem_filterentries withfilter_fieldsandfilter_functions. - The screen selection filter uses the same
filter_fields/filter_functionsenvelope as the item filter, but is stored ondigital_board_filter.
Filter JSON envelope¶
For a nonempty builder-authored filter, filter_fields contains one root logical group. The root operator is selected from the requested logic; it is NOT always and. Function conditions can be children in this same tree; the builder still emits filter_functions: [].
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.location.country",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "US",
"operator": "equals",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
A full item filter is typically stored as a schedule entry like this:
[
{
"type": "date_range",
"startDate": "2026-01-01T00:00:00Z",
"endDate": "2026-01-31T23:59:59Z"
},
{
"type": "item_filter",
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "title",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": "ford",
"operator": "like"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
]
This schedule example uses UI date names (startDate / endDate). Endpoint DTOs may use start_datetime / end_datetime; use the receiving service contract, rather than mixing these forms. Construct the filter as an object first and let the service apply any required JSON-string serialization.
Logical group marker¶
__logical_groupidentifies EVERY logical group, not only the root.- Every group MUST have
isGroup: true. The UI tests this flag to distinguish a group from a condition;field_name: "__logical_group"alone is insufficient. This applies recursively at all nesting levels. - Group operators are exactly
and(all children must match) andor(at least one child must match). Each operator combines only its immediate children; a child group is evaluated as one subexpression. - A group contains
field_name: "__logical_group",operator, a nonemptyvaluearray of conditions and/or groups, andisGroup: true. IncludeisDirty: truewhen authoring builder-shaped groups, matchingcreateLogicalGroup()output.isDirtyis UI bookkeeping, not the group discriminator. - Groups do not use
type: "filter_field"ortype: "filter_function",function_name,objectClass, orgroup. Those describe conditions. Do not serializeisRoot; the UI derives it. - Nested groups and mixtures of conditions/groups are supported. The current UI allows adding groups while
path.length < maxTreeDepth(maxTreeDepth: 2): root depth 0, child depth 1, grandchild depth 2. This is an add-button constraint, NOT a documented backend nesting limit; do not reject or flatten existing deeper trees merely because of that setting. - Never leave empty groups in a nonempty authored filter. To intentionally clear filtering, use the receiving UI/API contract (commonly
{ "filter_fields": [], "filter_functions": [] }), not a group with an emptyvalue.
Condition nodes and field metadata¶
| Node kind | type |
field_name |
function_name |
value |
|---|---|---|---|---|
| Logical group | Omit | __logical_group |
Omit | Nonempty array of child nodes |
| Field condition | filter_field |
Exact field-option id |
null |
Operator-specific comparison value |
| Function condition | filter_function |
null |
Exact function-option id |
Operator-specific comparison value |
- For conditions, copy
objectClassandgroupfrom the selected field option. Keepfunction_name: nullfor field conditions andfield_name: nullfor function conditions. If a field option has nogroup, omit it; do not invent a group. - Catalog
type: "field"/"function"becomes conditiontype: "filter_field"/"filter_function". A name that looks like a method may be a function option: for example,digital_board.getLatitudeLongitudeusesfunction_name, NOTfield_name. objectClassis the model class string, not a JS expression or display name: e.g.App\DigitalBoardas displayed text, encoded as"App\\DigitalBoard"in JSON. Copy the resolved catalog value exactly.groupis field-picker metadata (e.g.WeatherorScreen), NOT a logical grouping instruction. Only a group node and its operator express AND/OR.- A condition must not have
isGroup: true. LeafisDirtyis optional UI bookkeeping; it is not a comparison operator or an evaluation flag.
Values, operators, and dynamic fields¶
- Choose an operator from the Default operators table that supports the discovered field data type. Use the exact operator token, including case and punctuation; do not replace
equalswitheqor guess a negation token. - Unary operators (e.g.
is_false,is_true,datetime_is_future) need no comparison operand. Builder payloads usevalue: ""; do not encodeis_falseas a string comparison against"false". - List operators use the comma-separated string form shown by the builder, e.g.
"401,40101,40102", NOT a JSON array unless the specific operator explicitly requires an array. Negative list operators are not interchangeable: preservenot_any_ofversusnot_like_any_ofaccording to exact membership versus pattern-matching intent. - Numeric comparisons in working campaigns can have numeric strings (
"80","69"); other builder examples use JSON numbers. Preserve existing values and the field/operator contract rather than globally coercing every value. Use booleans, strings, dates, or geometry according to their field and operator. - Enum values must be underlying API values, never translated picker labels. Boolean, enum, list, and geo values must not be guessed from a label.
- The static catalogs are NOT the entire account field set. Query
/accounts/{accountId}/unique-key-value-store-keyswithobject_typefor the target model. The UI requestsApp\DigitalBoardfor screen keys andApp\Accountfor account/data-source keys. - For discovered keys, use the returned
macroexactly as the fieldid/ conditionfield_name;key_nameis the label, not the identifier. The UI uses returneddataTypeor falls back toscalar, and takesgroupfrominventory_feed.name(fallbackScreenorAccount) andobjectClassfrom the queried model. - The Weather1 fields in the examples are real keys from the supplied working campaign, NOT universally available built-in fields. Confirm the account exposes them and confirm their values/data types. Do not invent a Weather1 key, assume all weather providers use it, or substitute a condition code for a condition group code.
Sports data-source filters for a campaign item¶
The sports examples below come from the Data Source Filter for an item in a campaign. They restrict when that creative is eligible using the feed's account-level data; they are NOT screen-weather filters or campaign-wide inventory selection rules. The examples show the filter envelope edited by the builder. For a schedule payload, put that envelope on the appropriate type: "item_filter" entry and preserve the other schedule entries; do not move these predicates to digital_board_filter merely because the creative plays on a screen.
- Sports feed flags here use
account.store.*,objectClass: "App\\Account"in JSON,type: "filter_field", andfunction_name: null. Copy the discovered feed name intogroup; it is metadata, not a logical group. - Each supplied flag uses unary
operator: "is_true"withvalue: "". The field itself carries the boolean result; do not compare its value with the text"true"or use a game-count predicate invented from a label. - Scope is the configured feed: "no games scheduled" means no games for the teams configured in that data source, NOT no games anywhere in MLB. Likewise, "all games" and "has games today" concern the games selected by that feed. A name containing
(All Games)does not override its team configuration. - In the NCAA example, BOTH
all_games_in_pregameANDhas_games_todaymust be true. Keep the explicit games-today predicate: an all-games status alone does not establish that there is any game today. No games today, or a game that is no longer pregame, must not activate that example. - The NFL example uses the Baltimore Ravens' team-result flag for the requested "won their game today" behavior. It does not test whether any NFL team won, and it does not predict a win before the result is available. Confirm the feed's current-day/result semantics and freshness; do not assume the suffix
_wonindependently guarantees a date or fabricate a date-field key.
Discover sports fields before authoring¶
- Configure the actual sports data source, including the desired team or teams, then run it at least once with valid data so the resulting keys can be observed. Team-result keys depend on team names and may not be known before that run.
- Inspect its output and query the account's available key/value-store fields with
object_type: "App\\Account"in JSON. Copy the returnedmacro, data type, and feed metadata exactly. - Select flags from the intended feed instance.
LF1092,LF1174, andLF1387are identifiers in these supplied examples, not portable constants. Do not reuse them for another feed, manufacture a new identifier, derive a team-key slug from its display name, or combine flags from unrelated feeds unless that cross-feed logic is explicitly requested. - Verify that the desired flag exists and has current valid data before saving. A missing key, an unrun feed, or stale output is not evidence of "no games scheduled" or a win. If the needed key has not appeared, obtain valid feed output and rediscover it rather than guessing.
- Check the expression against matching and nonmatching feed states. For the pregame example,
(true, true)activates;(true, false),(false, true), and(false, false)do not. For a no-games or team-win flag, only a true flag activates.
Constructing complex filters safely¶
- Determine the storage target: screen selection (
digital_board_filter), a scheduleitem_filter, campaign settings inventory filtering, or an automation. Load that target's static AND dynamic field options. - Write a fully parenthesized expression in plain language before constructing JSON. Identify which alternatives share an OR and which restrictions must all hold under AND.
- Map each predicate to one condition with exact identifiers, copied metadata, a supported operator, and the correct value shape.
- Translate each parenthesized AND/OR subexpression into a group, including
isGroup: trueandisDirty: trueon EVERY group. Choose the root operator from the expression; do not add an AND wrapper by habit. - Prefer the simplest equivalent tree for NEW filters: rain OR snow is one root OR with two leaves. An AND containing only a correctly marked OR is logically equivalent but unnecessary. Never flatten an OR into its AND parent, or vice versa; that changes meaning. Preserve existing campaign trees unless explicitly asked to simplify them.
- Put the one root in
filter_fieldsand keepfilter_functions: []for builder-shaped payloads, even when function conditions appear inside the tree. Preserve legacy function arrays only when the receiving contract explicitly requires them. - Validate recursively, then check the truth conditions against representative matching and nonmatching cases. A syntactically valid JSON object or the UI's permissive
isValidcheck alone does not establish correct semantics or backend acceptance.
Recursive preflight checklist¶
- Envelope has both arrays; a nonempty authored tree has one root group.
- Each group has
field_name: "__logical_group",isGroup: true,isDirty: true,operator: "and"or"or", and a nonempty array of individually validated children. - Each leaf has the correct condition
type, exactly one non-null identifier, copiedobjectClassand availablegroup, a supported operator, and an appropriate value. No leaf is marked as a group. - All field identifiers and enum values exist for this account and target; dynamic weather and sports keys have been discovered rather than assumed. Sports flags refer to the intended configured feed/team scope and have current valid output.
- No accidental singleton AND wrapper, dropped group flags, mixed identifier types, JSON-array list operand, or incorrect scope has been introduced.
- Describe the final tree back as a fully parenthesized expression and verify it matches the request. Check the service DTO/serialization and test in the receiving application before changing a live campaign.
Common failure: a nested group without isGroup¶
The previously failing rain/snow payload had a root AND containing an inner OR with only field_name, operator, and value. The inner object lacked isGroup: true; the UI renders it as a condition instead of recursively rendering its children. Nesting itself is not the problem. Mark EVERY group correctly and, for this simple expression, use the root OR example below. Adding isDirty alone does not repair group recognition. The frontend evidence explains the UI failure; backend evaluation requirements must be checked separately.
Geo filter value shape for is_within_geo¶
The value for is_within_geo is a JSON array of geo objects. The UI supports multiple geofences in one filter, and each element must be one of the following shapes.
Radius / circle¶
[
{
"id": "geo-1",
"geo_source": "map_drawing",
"geo_type": "radius",
"label": "Circle on map",
"bounds": { "north": 41.0, "east": -72.0, "south": 40.0, "west": -73.0 },
"centerpoint": { "lat": 40.5, "long": -72.5 },
"units": "m",
"radius": 2500
}
]
Polygon¶
[
{
"id": "geo-2",
"geo_source": "map_drawing",
"geo_type": "polygon",
"label": "Polygon on map",
"bounds": { "north": 41.0, "east": -72.0, "south": 40.0, "west": -73.0 },
"coordinates": [[41.0, -72.0], [41.0, -73.0], [40.0, -73.0], [40.0, -72.0]]
}
]
Rectangle¶
[
{
"id": "geo-3",
"geo_source": "map_drawing",
"geo_type": "rectangle",
"label": "Rectangle on map",
"bounds": { "north": 41.0, "east": -72.0, "south": 40.0, "west": -73.0 },
"coordinates": [[41.0, -72.0], [41.0, -73.0], [40.0, -73.0], [40.0, -72.0]]
}
]
Important constraints:
valueis an array, not a single object.geo_typemust be one of:radius,polygon, orrectangle.geo_sourceis typically eithergoogle_placeormap_drawing.- Radius filters use
centerpoint,radius, andunits(typically"m"for meters). - Polygon and rectangle filters use
coordinatesas an array of[latitude, longitude]pairs. boundsis optional metadata used by the map UI, but the essential geometry is thecenterpoint/radiusorcoordinatesfields.
Data types¶
| id | label |
|---|---|
| scalar | Scalar (Could be any of text or number) |
| string | Text |
| float | Decimal Number |
| integer | Whole Number |
| boolean | True/False |
| datetime | Date/Time |
| tags | Tags |
| geo_location | Geo Location |
| geo_tags | Geo Tags |
| array | Array (List) |
Logical group operators¶
| id | operator | label |
|---|---|---|
| and | and | all of the following are true |
| or | or | at least one of the following is true |
Default operators¶
Each operator is valid only for certain data types. The runtime logic in useFilterBuilder.ts filters available operators by dataTypes.
| id | operator | label | dataTypes | isUnary | isList | help |
|---|---|---|---|---|---|---|
| like | like | Contains | scalar, string | false | false | Any text, not case sensitive |
| not_like | not_like | Does not Contain | scalar, string | false | false | Any text |
| like_one_of | like_one_of | Contains One Of | scalar, string, tags | false | true | Multiple items separated by commas |
| like_all_of | like_all_of | Contains All Of | scalar, string, tags | false | true | Multiple items separated by commas |
| not_like_any_of | not_like_any_of | Contains None Of | scalar, string, tags | false | true | Multiple items separated by commas |
| equals | equals | Equals | scalar, string, float, integer, boolean | false | false | Any value, case sensitive, matches exactly |
| does_not_equals | does_not_equals | Does Not Equal | scalar, string, float, integer, boolean | false | false | Any value |
| is_one_of | is_one_of | Is One Of | scalar, string, float, integer | false | true | Multiple items separated by commas |
| not_any_of | not_any_of | Is Not One Of | scalar, string, float, integer | false | true | Multiple items separated by commas |
| greater_than | > | Greater Than | scalar, float, integer, datetime | false | false | |
| less_than | < | Less Than | scalar, float, integer, datetime | false | false | |
| greater_than_or_equal_to | >= | Greater Than Or Equal To | scalar, float, integer, datetime | false | false | |
| less_than_or_equal_to | <= | Less Than Or Equal To | scalar, float, integer, datetime | false | false | |
| is_between | is_between | Is Between | scalar, float, integer, datetime | false | false | Inclusive of two numbers separated by a comma (min,max) |
| is_true | is_true | Is True | scalar, string, boolean | true | false | Must be true or true'ish value |
| is_false | is_false | Is False | scalar, string, boolean | true | false | Must be false or falsy value |
| is_empty | is_empty | Is Empty | scalar, string, float, integer, boolean | true | false | Must be empty, blank, null or false value |
| is_not_empty | is_not_empty | Is Not Empty | scalar, string, float, integer, boolean | true | false | Must be a value |
| datetime_is_future | datetime_is_future | Is In Future | scalar, datetime | true | false | Must be a future date or time |
| datetime_is_past | datetime_is_past | Is In Past | scalar, datetime | true | false | Must be a past date or time |
| datetime_is_today | datetime_is_today | Is Today | scalar, datetime | true | false | Must be today's date or time |
| datetime_is_older_than_seconds | datetime_is_older_than_seconds | Is Older Than Seconds | scalar, datetime | false | false | Must be older than a number of seconds |
| datetime_is_younger_than_seconds | datetime_is_younger_than_seconds | Is Younger Than Seconds | scalar, datetime | false | false | Must be younger than a number of seconds |
| matches_regexp | matches_regexp | Matches Regular Expression | scalar, string | false | false | Must match a regular expression in PCRE format /{pattern}/{modifiers} |
| does_not_match_regexp | does_not_match_regexp | Does Not Match Regular Expression | scalar, string | false | false | Must not match a regular expression in PCRE format /{pattern}/{modifiers} |
| is_within_geo | is_within_geo | Within Geo | scalar, geo_location, geo_tags | false | false | Must be within a geo location |
| array_like | array_like | List Contains | array | false | false | List contains a value |
| array_like_one_of | array_like_one_of | List Contains One Of | array | false | true | Multiple items separated by commas |
| array_not_like | array_not_like | List Does Not Contain | array | false | false | List does not contain a value |
| array_not_like_any_of | array_not_like_any_of | List Contains None Of | array | false | true | Multiple items separated by commas |
| array_contains_one_of | array_contains_one_of | List Contains One Of (Exact) | array | false | true | Multiple items separated by commas |
| array_contains_none_of | array_contains_none_of | List Contains None Of (Exact) | array | false | true | Multiple items separated by commas |
| array_contains_one_true | array_contains_one_true | List Contains At Least One True | array | true | false | At least one item in the list is true |
| array_contains_one_false | array_contains_one_false | List Contains At Least One False | array | true | false | At least one item in the list is false |
| array_is_all_true | array_is_all_true | List Is All True | array | true | false | All items in the list are true |
| array_is_all_false | array_is_all_false | List Is All False | array | true | false | All items in the list are false |
Model field catalogs¶
Inventory item fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| title | Title | field | string | App\InventoryItem | false | ||
| description | Description | field | string | App\InventoryItem | false | ||
| price | Price | field | float | App\InventoryItem | false | ||
| price_original | Original Price | field | float | App\InventoryItem | false | ||
| unqique_id | Unique ID | field | string | App\InventoryItem | false | ||
| dealer_stock_number | Stock Number | field | string | App\InventoryItem | false | ||
| year | Year | field | integer | App\InventoryItem | false | ||
| make | Make | field | string | App\InventoryItem | false | ||
| model | Model | field | string | App\InventoryItem | false | ||
| sub_model | Sub Model | field | string | App\InventoryItem | false | ||
| new_used | New | field | boolean | App\InventoryItem | false | ||
| basePhotoCount | Photo Count | function | integer | App\InventoryItem | false | ||
| primaryPhotoAspectRatio | Photo Aspect Ratio | function | float | App\InventoryItem | false | ||
| mileage | Mileage | function | integer | App\InventoryItem | false | ||
| current_date_time | Current Date/Time | field | datetime | App\InventoryItem | false | ||
| random_number_to_one_hundred | Random Number (1-100) | function | integer | App\InventoryItem | false | ||
| cached_tags | Tags | field | tags | App\InventoryItem | false | ||
| geoTagLatLongs | Location (GeoTags) | function | geo_tags | App\InventoryItem | false | ||
| account.name | Item Account Name | field | string | App\InventoryItem | false | ||
| account.lcuid | Item Account LCUID | field | string | App\InventoryItem | false | ||
| user.name | Item User Name | field | string | App\InventoryItem | false | ||
| user.lcuid | Item User LCUID | field | string | App\InventoryItem | false | ||
| drive_template.name | Item Drive Template Name | field | string | App\InventoryItem | false | ||
| drive_template.lcuid | Item Drive Template LCUID | field | string | App\InventoryItem | false |
Digital board fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| digital_board.name | Screen Name | field | string | Screen | App\DigitalBoard | false | |
| digital_board.board_identifier | Screen ID | field | string | Screen | App\DigitalBoard | false | |
| digital_board.agency.name | Media Owner Name | field | string | Screen | App\DigitalBoard | false | |
| digital_board.venue_taxonomy_id | Venue Taxonomy ID | field | integer | Screen | App\DigitalBoard | true | See https://github.com/openooh/venue-taxonomy for list off valid types |
| digital_board.digital_board_format | Screen Format | field | string | Screen | App\DigitalBoard | true | See https://lucit.app/docs/guides/screen-format-reference/ for a valid list of screen formats |
| digital_board.location.city | Screen City | field | string | Screen | App\DigitalBoard | false | |
| digital_board.location.region | Screen State/Province | field | string | Screen | App\DigitalBoard | false | Typically a 2 CharacterUS state or Canadian Provice, but can be any worldwide region |
| digital_board.location.postal_code | Postal Code | field | string | Screen | App\DigitalBoard | false | |
| digital_board.location.usa_dma_code | DMA Code (US) | field | string | Screen | App\DigitalBoard | true | Any valid North American DMA Code |
| digital_board.location.country | Screen Country | field | string | Screen | App\DigitalBoard | false | |
| digital_board.location.datetime_current | Screen Current Date/Time | field | datetime | Screen | App\DigitalBoard | false | The current date and time at the physical location of the screen |
| digital_board.location.timezone | Screen Timezone | field | string | Screen | App\DigitalBoard | true | Any valid timezone name, e.g., 'America/New_York' |
| digital_board.location.timezone_offset_seconds | Screen Timezone Offset Seconds | field | integer | Screen | App\DigitalBoard | false | |
| digital_board.getLatitudeLongitude | Screen Latitude/Longitude Location | function | geo_location | Screen | App\DigitalBoard | false | |
| digital_board.cached_tags | Screen Tags | field | tags | Screen | App\DigitalBoard | false |
Account fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| account.name | Account Name | field | string | App\Account | false | ||
| account.description | Account Description | field | string | App\Account | false | ||
| account.website | Account Website | field | string | App\Account | false | ||
| account.is_parent_account | Account Is Parent Account | field | boolean | App\Account | false | ||
| account.agency.lcuid | Account Agency LCUID | field | string | App\Account | false | ||
| account.agency.name | Account Agency Name | field | string | App\Account | false | ||
| account.created_by_user.name | Account Created By User Name | field | string | App\Account | false | ||
| account.created_by_user.lcuid | Account Created By User LCUID | field | string | App\Account | false |
Inventory export fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| inventory_export.name | Campaign Name | field | string | App\InventoryExport | false | ||
| inventory_export.created_by_user.name | Campaign Created By User Name | field | string | App\InventoryExport | false | ||
| inventory_export.created_by_user.lcuid | Campaign Created By User LCUID | field | string | App\InventoryExport | false | ||
| inventory_export.account.name | Campaign Account Name | field | string | App\InventoryExport | false | ||
| inventory_export.account.lcuid | Campaign Account LCUID | field | string | App\InventoryExport | false | ||
| inventory_export.active | Campaign Active | field | boolean | App\InventoryExport | false | ||
| inventory_export.status | Campaign Creative Build Status | field | integer | App\InventoryExport | true | Any valid ExportStatus | |
| inventory_export.last_run_at | Campaign Last Run At | field | datetime | App\InventoryExport | false | ||
| inventory_export.campaign_state | Campaign State | field | integer | App\InventoryExport | true | Any valid CampaignState | |
| inventory_export.campaign_state_changed_at | Campaign State Changed At | field | datetime | App\InventoryExport | false | ||
| inventory_export.campaign_class | Campaign Class | field | string | App\InventoryExport | true | Any valid InventoryExportCampaignClasses |
Inventory feed fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| inventory_feed.name | Feed Name | field | string | App\InventoryFeed | false | ||
| inventory_feed.active | Feed Active | field | boolean | App\InventoryFeed | false | ||
| inventory_feed.status | Feed Status | field | integer | App\InventoryFeed | true | Any valid FeedStatus | |
| inventory_feed.last_run_at | Feed Last Run At | field | datetime | App\InventoryFeed | false | ||
| inventory_feed.last_changed_at | Feed Last Changed At | field | datetime | App\InventoryFeed | false | ||
| inventory_feed.account.name | Feed Account Name | field | string | App\InventoryFeed | false | ||
| inventory_feed.account.lcuid | Feed Account LCUID | field | string | App\InventoryFeed | false | ||
| inventory_feed.created_by_user.name | Feed Created By User Name | field | string | App\InventoryFeed | false | ||
| inventory_feed.created_by_user.lcuid | Feed Created By User LCUID | field | string | App\InventoryFeed | false | ||
| inventory_feed.inventory_feed_provider.name | Feed Provider Name | field | string | App\InventoryFeed | false | ||
| inventory_feed.inventory_feed_provider.lcuid | Feed Provider LCUID | field | string | App\InventoryFeed | false |
Lightning device fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| lightning_device.name | Lightning Device Name | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.description | Lightning Device Description | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.status | Lightning Device Status | field | integer | Lightning Device | App\LightningDevice | true | Any valid LightningDeviceStatus |
| lightning_device.venue_taxonomy_id | Venue Taxonomy ID | field | integer | Lightning Device | App\LightningDevice | true | See https://github.com/openooh/venue-taxonomy for list off valid types |
| lightning_device.digital_board_format | Screen Format | field | string | Lightning Device | App\LightningDevice | true | See https://lucit.app/docs/guides/screen-format-reference/ for a valid list of screen formats |
| lightning_device.location.city | Device City | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.location.region | Device State/Province | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.location.postal_code | Postal Code | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.location.usa_dma_code | DMA Code (US) | field | string | Lightning Device | App\LightningDevice | true | Any valid North American DMA Code |
| lightning_device.location.country | Device Country | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.location.datetime_current | Device Current Date/Time | field | datetime | Lightning Device | App\LightningDevice | false | |
| lightning_device.location.timezone | Device Timezone | field | string | Lightning Device | App\LightningDevice | false | |
| lightning_device.location.timezone_offset_seconds | Device Timezone Offset Seconds | field | integer | Lightning Device | App\LightningDevice | false | |
| lightning_device.getLatitudeLongitude | Device Latitude/Longitude Location | function | geo_location | Lightning Device | App\LightningDevice | false | |
| lightning_device.cached_tags | Device Tags | field | tags | Lightning Device | App\LightningDevice | false |
User fields¶
| id | label | type | dataType | group | objectClass | enum | docs_reference |
|---|---|---|---|---|---|---|---|
| user.name | Name | field | string | App\User | false | ||
| user.email | field | string | App\User | false | |||
| user.title | Title | field | string | App\User | false | ||
| user.roles | Roles | field | string | App\User | false | ||
| user.can_log_in | Can Log In | field | boolean | App\User | false | ||
| user.last_visit_seconds_ago | Last Visit (seconds ago) | field | integer | App\User | false | ||
| has_visited_layout | Has Visited | field | boolean | App\User | false | ||
| accounts.name | Has Account with Name | field | array | App\User | false | ||
| accounts.account_class | Has Account with Class | field | array | App\User | true | ||
| accounts.inventory_item_class | Has Account with Inventory Item Class | field | array | App\User | true | ||
| agencies.name | Has Agency with Name | field | array | App\User | false | ||
| accounts.has_active_campaigns | Has Account with Active Campaigns | field | array | App\User | false | ||
| accounts.has_completed_campaigns | Has Account with Completed Campaigns | field | array | App\User | false | ||
| accounts.has_never_run_a_campaign | Has Account that have Never Run a Campaign | field | array | App\User | false | ||
| agencies.agency_class | Has Agency with Class | field | array | App\User | true | ||
| user.has_accounts | Has Accounts | field | boolean | App\User | false | ||
| user.has_agencies | Has Agencies | field | boolean | App\User | false | ||
| user.has_created_drive_templates | Has Created Templates | field | boolean | App\User | false | ||
| user.has_inventory_items | Owns Creatives | field | boolean | App\User | false | ||
| user.has_created_inventory_items | Has Created Creatives | field | boolean | App\User | false |
Notes for AI-assisted authoring¶
- Author the builder tree in
filter_fields; function conditions can appear in that tree and do NOT require a nonemptyfilter_functionsarray. The current builder always emitsfilter_functions: []. - For
screenfilters, usedigital_board_filter.filter_fieldsanddigital_board_filter.filter_functions. - For
itemfilters, the schedule entry is stored as part of a schedule array and should be keyed bytype: "item_filter". - For export / inventory-item filtering in the Schedule Builder and Item Filter dialogs, include date/time logic plus the current account’s dynamic key/value fields in the item filter and digital-board filter payloads.
- The Campaign Settings dialog item filter uses the
InventoryFieldOptionscatalog, while automation-only catalogs such asInventoryExportFields,AccountFields,UserFields,LightningDeviceFields, andInventoryFeedFieldsshould not be assumed to be part of the standard item filter UI. - In order to determine the full slate of available fields for the
item_filterand thedigital_board_filter, you must query the available key/value fields for the account object and the digitalboard object. This is typically handled via theunique-key-value-store-keysendpoint on the accounts object and by passing in the correctobject_type. - Use the
objectClassandfield_name/function_namevalues in this document as the canonical field identifiers that the UI accepts. - For enum-backed values, use the underlying API value rather than the display label; back-end enum conversion is handled by
getEnumOptions.
Examples¶
All filter examples below include complete builder-shaped group flags and condition metadata, including correct field/function identifiers. They are logical objects before endpoint-layer serialization. Dynamic Weather1 and sports examples require the matching account/provider/feed keys; discover the actual identifiers before adapting them to another account or feed.
1. Show a creative when it is raining OR snowing¶
Expression: (condition_group = rain OR condition_group = snow). Rain matches, snow matches, a different group (e.g. clear) does not. Both predicates belong directly under the root OR; do not use AND between rain and snow on the same single-valued field.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_condition_group_code",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "rain",
"operator": "equals",
"group": "Weather"
},
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_condition_group_code",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "snow",
"operator": "equals",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
2. Country restriction AND (rain OR snow)¶
Expression: (country = US AND (condition_group = rain OR condition_group = snow)). A US screen in rain or snow matches; a US screen in clear weather does not; a non-US screen never matches even when raining. This needs a root AND and a nested OR, both marked as groups.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.location.country",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "US",
"operator": "equals",
"group": "Screen"
},
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_condition_group_code",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "rain",
"operator": "equals",
"group": "Weather"
},
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_condition_group_code",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "snow",
"operator": "equals",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
3. Complex working live campaign: weather AND venue restrictions¶
This reproduces the supplied working campaign structure and comparison values, including its intentional singleton groups. It is a reference for nesting and metadata, not a universal weather policy. Do not assume its dynamic fields are available on another account. The root AND has seven children: five weather groups, one direct venue condition, and one OR containing two AND branches. The final OR means either the venue is outside 401,40101,40102, or it is inside that set AND its agency name matches Zoom. It is NOT a global Zoom requirement.
Fully parenthesized expression (short aliases for the exact fields in the JSON below; uppercase operator names here are explanatory, not JSON tokens):
(
(sun_is_up IS FALSE OR forecast_max_temp_f > 69)
AND (sun_is_up IS FALSE OR current_temp_f < 80)
AND (condition_code NOT_LIKE_ANY_OF "1183,1189,1195,1198,1201,1204,1207,1240,1243,1246,1249,1252,1276")
AND (current_temp_f < 80 OR current_humidity < 60)
AND (sun_is_up IS FALSE OR current_cloud < 80)
AND venue_taxonomy_id NOT_ANY_OF "301,30101,30102,302,303"
AND (
(venue_taxonomy_id NOT_ANY_OF "401,40101,40102")
OR (venue_taxonomy_id IS_ONE_OF "401,40101,40102" AND agency_name LIKE "Zoom")
)
)
For example, when sun-up is false and temperature is below 80, the first, second, fourth, and fifth clauses match, but the excluded condition-code and venue clauses must still match independently. An allowed venue outside the 401 set does not need the Zoom name; a venue inside that set must also satisfy the agency-name condition (operator: "like", value: "Zoom"). Preserve < versus <= and > versus >=: exactly 80 does not satisfy < 80, and exactly 69 does not satisfy > 69.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_today_astro_is_sun_up",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "",
"operator": "is_false",
"group": "Weather"
},
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_forecast_maxtemp_f",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "69",
"operator": ">",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
},
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_today_astro_is_sun_up",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "",
"operator": "is_false",
"group": "Weather"
},
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_temp_f",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "80",
"operator": "<",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
},
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_condition_code",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "1183,1189,1195,1198,1201,1204,1207,1240,1243,1246,1249,1252,1276",
"operator": "not_like_any_of",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
},
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_temp_f",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "80",
"operator": "<",
"group": "Weather"
},
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_humidity",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "60",
"operator": "<",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
},
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_today_astro_is_sun_up",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "",
"operator": "is_false",
"group": "Weather"
},
{
"type": "filter_field",
"field_name": "digital_board.store.Weather1_current_cloud",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "80",
"operator": "<",
"group": "Weather"
}
],
"isDirty": true,
"isGroup": true
},
{
"type": "filter_field",
"field_name": "digital_board.venue_taxonomy_id",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "301,30101,30102,302,303",
"operator": "not_any_of",
"group": "Screen"
},
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.venue_taxonomy_id",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "401,40101,40102",
"operator": "not_any_of",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
},
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.venue_taxonomy_id",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "401,40101,40102",
"operator": "is_one_of",
"group": "Screen"
},
{
"type": "filter_field",
"field_name": "digital_board.agency.name",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "Zoom",
"operator": "like",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
}
],
"isDirty": true,
"isGroup": true
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
4. Function condition within the builder tree¶
Expression: (basePhotoCount >= 1). The catalog marks this identifier as a function, so the leaf uses type: "filter_function", field_name: null, and function_name: "basePhotoCount". The envelope still has an empty filter_functions array.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_function",
"field_name": null,
"function_name": "basePhotoCount",
"objectClass": "App\\InventoryItem",
"value": 1,
"operator": ">="
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
5. Sports data-source filter: no MLB games scheduled for the configured teams¶
Expression: (feed_no_games_scheduled IS TRUE). Activate this creative only when the configured MLB data-source feed reports no games scheduled for its selected team or teams. Games involving other teams outside that feed do not change the scope of this predicate. A false flag does not activate it; a missing flag must not be interpreted as true. This reproduces the supplied LF1092 account-store key; discover the actual key for another feed.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "account.store.MlbLiveGameScoresAllGames_LF1092_retriever_no_games_scheduled",
"function_name": null,
"objectClass": "App\\Account",
"value": "",
"operator": "is_true",
"group": "MLB Live Game Scores (All Games)"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
6. Sports data-source filter: NCAA games today AND all games still in pregame¶
Expression: (feed_all_games_in_pregame IS TRUE AND feed_has_games_today IS TRUE). Activate this creative only when there are games today within the configured NCAA feed AND all those games have not yet started. Both conditions use the same LF1174 feed. Keep the root AND and both predicates; do not replace it with OR or drop has_games_today. Either a false pregame flag or a false games-today flag prevents activation.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "account.store.NcaaCollegeFootballLiveGameScoresAllGames_LF1174_retriever_all_games_in_pregame",
"function_name": null,
"objectClass": "App\\Account",
"value": "",
"operator": "is_true",
"group": "NCAA College Football Live Game Scores (All Games)"
},
{
"type": "filter_field",
"field_name": "account.store.NcaaCollegeFootballLiveGameScoresAllGames_LF1174_retriever_has_games_today",
"function_name": null,
"objectClass": "App\\Account",
"value": "",
"operator": "is_true",
"group": "NCAA College Football Live Game Scores (All Games)"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
7. Sports data-source filter: Baltimore Ravens won their game today¶
Expression: (feed_team_result_baltimore_ravens_won IS TRUE). Activate this creative when the configured NFL feed reports that the Baltimore Ravens won their game today, subject to the feed's current-day result semantics. A false result flag does not activate it; another team's win is not a substitute. This reproduces the supplied LF1387 key. Team-result keys are dynamic: configure the sports data source, run it once with valid data, and inspect/discover the resulting key rather than constructing a key from a team name.
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "account.store.NflLiveGameScoresAllGames_LF1387_retriever_team_result_baltimore_ravens_won",
"function_name": null,
"objectClass": "App\\Account",
"value": "",
"operator": "is_true",
"group": "NFL Live Game Scores (All Games)"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
8. Simple text search for inventory item title¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "title",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": "ford",
"operator": "like"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
9. Numeric comparison on price¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "price",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": 25000,
"operator": ">="
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
10. Boolean condition for new vs used inventory¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "new_used",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": true,
"operator": "equals"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
11. Multiple tag match with OR logic¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "or",
"value": [
{
"type": "filter_field",
"field_name": "cached_tags",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": "truck,commercial",
"operator": "like_one_of"
},
{
"type": "filter_field",
"field_name": "cached_tags",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": "fleet,local",
"operator": "like_all_of"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
12. Date and time condition for a future campaign window¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "inventory_export.last_run_at",
"function_name": null,
"objectClass": "App\\InventoryExport",
"value": "",
"operator": "datetime_is_future"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
13. Screen filter for a specific country and format¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.location.country",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "US",
"operator": "equals",
"group": "Screen"
},
{
"type": "filter_field",
"field_name": "digital_board.digital_board_format",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "bulletin",
"operator": "equals",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
14. Geo radius filter for a local ad area¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_function",
"field_name": null,
"function_name": "digital_board.getLatitudeLongitude",
"objectClass": "App\\DigitalBoard",
"value": [
{
"id": "radius-1",
"geo_source": "map_drawing",
"geo_type": "radius",
"label": "Downtown radius",
"centerpoint": {
"lat": 40.7128,
"long": -74.006
},
"units": "m",
"radius": 5000
}
],
"operator": "is_within_geo",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
15. Geo polygon filter for a service area¶
{
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_function",
"field_name": null,
"function_name": "digital_board.getLatitudeLongitude",
"objectClass": "App\\DigitalBoard",
"value": [
{
"id": "poly-1",
"geo_source": "map_drawing",
"geo_type": "polygon",
"label": "City polygon",
"coordinates": [
[
40.75,
-74.01
],
[
40.78,
-73.95
],
[
40.72,
-73.9
],
[
40.7,
-74.02
]
]
}
],
"operator": "is_within_geo",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
16. Schedule/export filter example: date range, day-of-week, time-range, and item filter¶
{
"filter": [
{
"type": "days_of_the_week",
"start_datetime": null,
"end_datetime": null,
"start_time": null,
"end_time": null,
"days": [
1,
2,
3,
4,
5
],
"filter_fields": null,
"filter_functions": null
},
{
"type": "date_range",
"start_datetime": "2026-01-01T00:00:00Z",
"end_datetime": "2026-12-31T23:59:59Z",
"start_time": null,
"end_time": null,
"days": null,
"filter_fields": null,
"filter_functions": null
},
{
"type": "time_range",
"start_datetime": null,
"end_datetime": null,
"start_time": "2026-01-01T09:00:00Z",
"end_time": "2026-01-01T17:00:00Z",
"days": null,
"filter_fields": null,
"filter_functions": null
},
{
"type": "item_filter",
"start_datetime": null,
"end_datetime": null,
"start_time": null,
"end_time": null,
"days": null,
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "account.lcuid",
"function_name": null,
"objectClass": "App\\InventoryItem",
"value": "LA_EXAMPLE_ACCOUNT",
"operator": "equals"
},
{
"type": "filter_field",
"field_name": "account.name",
"function_name": null,
"objectClass": "App\\Account",
"value": "retail",
"operator": "like"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
]
}
17. Board filter example: country, board format, and geo radius¶
{
"digital_board_filter": {
"filter_fields": [
{
"field_name": "__logical_group",
"operator": "and",
"value": [
{
"type": "filter_field",
"field_name": "digital_board.location.country",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "US",
"operator": "equals",
"group": "Screen"
},
{
"type": "filter_field",
"field_name": "digital_board.digital_board_format",
"function_name": null,
"objectClass": "App\\DigitalBoard",
"value": "bulletin",
"operator": "equals",
"group": "Screen"
},
{
"type": "filter_function",
"field_name": null,
"function_name": "digital_board.getLatitudeLongitude",
"objectClass": "App\\DigitalBoard",
"value": [
{
"id": "radius-geo",
"geo_source": "map_drawing",
"geo_type": "radius",
"label": "Downtown catchment",
"centerpoint": {
"lat": 40.7128,
"long": -74.006
},
"units": "m",
"radius": 7500
}
],
"operator": "is_within_geo",
"group": "Screen"
}
],
"isDirty": true,
"isGroup": true
}
],
"filter_functions": []
}
}